如何在Python中创建常量?

时间:2010-04-21 12:20:19

标签: python constants

有没有办法在Python中声明一个常量?在Java中,我们可以用这种方式创建常量值:

public static final String CONST_NAME = "Name";

Python中上述Java常量声明的等效内容是什么?

43 个答案:

答案 0 :(得分:823)

不,没有。您不能在Python中将变量或值声明为常量。只是不要改变它。

如果你在课堂上,那么等同于:

class Foo(object):
    CONST_NAME = "Name"

如果没有,那只是

CONST_NAME = "Name"

但您可能需要查看Alex Martelli的代码段Constants in Python

答案 1 :(得分:318)

与其他语言一样,没有const关键字,但是可以创建具有“getter function”来读取数据的属性,但是 no“setter函数“重写数据。 这实质上保护标识符不被更改。

以下是使用class属性的替代实现:

请注意,对于想知道常量的读者而言,代码并不容易。请参阅下面的说明

def constant(f):
    def fset(self, value):
        raise TypeError
    def fget(self):
        return f()
    return property(fget, fset)

class _Const(object):
    @constant
    def FOO():
        return 0xBAADFACE
    @constant
    def BAR():
        return 0xDEADBEEF

CONST = _Const()

print CONST.FOO
##3131964110

CONST.FOO = 0
##Traceback (most recent call last):
##    ...
##    CONST.FOO = 0
##TypeError: None

代码说明:

  1. 定义一个函数constant,它接受​​一个表达式,并用它来构造一个“getter” - 一个只返回表达式值的函数。
  2. setter函数引发TypeError,因此它是只读的
  3. 使用我们刚创建的constant函数作为装饰,快速定义只读属性。

  4. 以其他一些更老套的方式:

    (代码非常棘手,下面有更多解释)

    class _Const(object):
        @apply
        def FOO():
            def fset(self, value):
                raise TypeError
            def fget(self):
                return 0xBAADFACE
            return property(**locals())
    
    CONST = _Const()
    
    print CONST.FOO
    ##3131964110
    
    CONST.FOO = 0
    ##Traceback (most recent call last):
    ##    ...
    ##    CONST.FOO = 0
    ##TypeError: None
    

    请注意,@ apply装饰器似乎已被弃用。

    1. 要定义标识符FOO,firs定义两个函数(fset,fget - 名称由我选择)。
    2. 然后使用内置的property函数构造一个可以“设置”或“获取”的对象。
    3. 请注意,property函数的前两个参数名为fsetfget
    4. 使用我们选择这些名称作为我们自己的getter& setter并使用应用于该范围的所有本地定义的**(双星号)创建关键字字典,以将参数传递给property函数

答案 2 :(得分:99)

在Python中,而不是语言强制执行某些操作,人们使用命名约定,例如__method用于私有方法,并使用_method用于受保护的方法。

因此,您可以简单地将常量声明为所有上限,例如

MY_CONSTANT = "one"

如果你希望这个常量永远不会改变,你可以挂钩属性访问并做一些技巧,但更简单的方法是声明一个函数

def MY_CONSTANT():
    return "one"

只有问题无处不在,你必须做MY_CONSTANT(),但是再次MY_CONSTANT = "one"是python中的正确方法(通常)。

您还可以使用namedtuple创建常量:

>>> from collections import namedtuple
>>> Constants = namedtuple('Constants', ['pi', 'e'])
>>> constants = Constants(3.14, 2.718)
>>> constants.pi
3.14
>>> constants.pi = 3
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: can't set attribute

答案 3 :(得分:47)

我最近发现了一个非常简洁的更新,它会自动引发有意义的错误消息并阻止通过__dict__进行访问:

class CONST(object):
    __slots__ = ()
    FOO = 1234

CONST = CONST()

# ----------

print(CONST.FOO)    # 1234

CONST.FOO = 4321              # AttributeError: 'CONST' object attribute 'FOO' is read-only
CONST.__dict__['FOO'] = 4321  # AttributeError: 'CONST' object has no attribute '__dict__'
CONST.BAR = 5678              # AttributeError: 'CONST' object has no attribute 'BAR'

我们自己定义为使自己成为实例然后使用插槽来确保不添加其他属性。这也会删除__dict__访问路由。当然,整个对象仍然可以重新定义。

修改 - 原始解决方案

我可能在这里错过了一个技巧,但这似乎对我有用:

class CONST(object):
    FOO = 1234

    def __setattr__(self, *_):
        pass

CONST = CONST()

#----------

print CONST.FOO    # 1234

CONST.FOO = 4321
CONST.BAR = 5678

print CONST.FOO    # Still 1234!
print CONST.BAR    # Oops AttributeError

创建实例允许魔术__setattr__方法启动并拦截设置FOO变量的尝试。如果你愿意,可以在这里抛出异常。通过类名实例化实例可以防止直接通过类进行访问。

一个值总是很痛苦,但你可以在你的CONST对象上附加许多。有一个上流社会,班级名称似乎有点粗糙,但我认为整体而言非常简洁。

答案 4 :(得分:25)

Python没有常数。

也许最简单的选择是为它定义一个函数:

def MY_CONSTANT():
    return 42

MY_CONSTANT()现在具有常量的所有功能(加上一些恼人的大括号)。

答案 5 :(得分:18)

除了两个最佳答案(只使用带有大写名称的变量,或使用属性使值只读),我想提一下,可以使用元类来实现命名常数。我在GitHub使用元类提供了一个非常简单的解决方案,如果您希望这些值对其类型/名称提供更多信息,这可能会有所帮助:

>>> from named_constants import Constants
>>> class Colors(Constants):
...     black = 0
...     red = 1
...     white = 15
...
>>> c = Colors.black
>>> c == 0
True
>>> c
Colors.black
>>> c.name()
'black'
>>> Colors(0) is c
True

这是稍微高级的Python,但仍然非常易于使用和方便。 (该模块具有更多功能,包括常量只读,请参阅其自述文件。)

在各种存储库中存在类似的解决方案,但据我所知,它们要么缺少我期望的常量(如常量,或任意类型)的基本特征之一,要么它们具有深奥的意义。添加的功能使它们不太普遍适用。但YMMV,我很感激反馈。 : - )

答案 6 :(得分:16)

编辑:添加了Python 3的示例代码

注意:this other answer看起来像提供了更完整的实现,类似于以下内容(具有更多功能)。

首先,制作metaclass

class MetaConst(type):
    def __getattr__(cls, key):
        return cls[key]

    def __setattr__(cls, key, value):
        raise TypeError

这可以防止更改静态属性。然后创建另一个使用该元类的类:

class Const(object):
    __metaclass__ = MetaConst

    def __getattr__(self, name):
        return self[name]

    def __setattr__(self, name, value):
        raise TypeError

或者,如果您使用的是Python 3:

class Const(object, metaclass=MetaConst):
    def __getattr__(self, name):
        return self[name]

    def __setattr__(self, name, value):
        raise TypeError

这可以防止实例道具被更改。要使用它,继承:

class MyConst(Const):
    A = 1
    B = 2

现在直接或通过实例访问的道具应该是不变的:

MyConst.A
# 1
my_const = MyConst()
my_const.A
# 1

MyConst.A = 'changed'
# TypeError
my_const.A = 'changed'
# TypeError

Here's上面的一个例子。 {3}另一个Python 3的例子。

答案 7 :(得分:12)

属性是创建常量的一种方法。您可以通过声明getter属性来执行此操作,但忽略setter。例如:

class MyFinalProperty(object):

    @property
    def name(self):
        return "John"

您可以查看an article I've written以查找更多使用Python属性的方法。

答案 8 :(得分:8)

这是“常量”类的实现,它使用只读(常量)属性创建实例。例如。可以使用Nums.PI获取已初始化为3.14159的值,Nums.PI = 22会引发异常。

# ---------- Constants.py ----------
class Constants(object):
    """
    Create objects with read-only (constant) attributes.
    Example:
        Nums = Constants(ONE=1, PI=3.14159, DefaultWidth=100.0)
        print 10 + Nums.PI
        print '----- Following line is deliberate ValueError -----'
        Nums.PI = 22
    """

    def __init__(self, *args, **kwargs):
        self._d = dict(*args, **kwargs)

    def __iter__(self):
        return iter(self._d)

    def __len__(self):
        return len(self._d)

    # NOTE: This is only called if self lacks the attribute.
    # So it does not interfere with get of 'self._d', etc.
    def __getattr__(self, name):
        return self._d[name]

    # ASSUMES '_..' attribute is OK to set. Need this to initialize 'self._d', etc.
    #If use as keys, they won't be constant.
    def __setattr__(self, name, value):
        if (name[0] == '_'):
            super(Constants, self).__setattr__(name, value)
        else:
            raise ValueError("setattr while locked", self)

if (__name__ == "__main__"):
    # Usage example.
    Nums = Constants(ONE=1, PI=3.14159, DefaultWidth=100.0)
    print 10 + Nums.PI
    print '----- Following line is deliberate ValueError -----'
    Nums.PI = 22

感谢@MikeGraham 's FrozenDict,我将其作为起点。已更改,因此使用语法Nums['ONE']而不是Nums.ONE

感谢@ Raufio的回答,想要覆盖__ setattr __。

或者对于具有更多功能的实现,请参阅@Hans_meine named_constants at GitHub

答案 9 :(得分:6)

不幸的是,Python还没有常量,这很遗憾。 ES6已经为JavaScript(https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Statements/const)添加了支持常量,因为它在任何编程语言中都是非常有用的东西。 正如在Python社区的其他答案中所回答的那样,使用约定 - 用户大写变量作为常量,但它不能防止代码中的任意错误。 如果您愿意,可能会发现下一个单文件解决方案很有用 (参见docstrings如何使用它)。

文件constants.py

import collections


__all__ = ('const', )


class Constant(object):
    """
    Implementation strict constants in Python 3.

    A constant can be set up, but can not be changed or deleted.
    Value of constant may any immutable type, as well as list or set.
    Besides if value of a constant is list or set, it will be converted in an immutable type as next:
        list -> tuple
        set -> frozenset
    Dict as value of a constant has no support.

    >>> const = Constant()
    >>> del const.temp
    Traceback (most recent call last):
    NameError: name 'temp' is not defined
    >>> const.temp = 1
    >>> const.temp = 88
    Traceback (most recent call last):
        ...
    TypeError: Constanst can not be changed
    >>> del const.temp
    Traceback (most recent call last):
        ...
    TypeError: Constanst can not be deleted
    >>> const.I = ['a', 1, 1.2]
    >>> print(const.I)
    ('a', 1, 1.2)
    >>> const.F = {1.2}
    >>> print(const.F)
    frozenset([1.2])
    >>> const.D = dict()
    Traceback (most recent call last):
        ...
    TypeError: dict can not be used as constant
    >>> del const.UNDEFINED
    Traceback (most recent call last):
        ...
    NameError: name 'UNDEFINED' is not defined
    >>> const()
    {'I': ('a', 1, 1.2), 'temp': 1, 'F': frozenset([1.2])}
    """

    def __setattr__(self, name, value):
        """Declaration a constant with value. If mutable - it will be converted to immutable, if possible.
        If the constant already exists, then made prevent againt change it."""

        if name in self.__dict__:
            raise TypeError('Constanst can not be changed')

        if not isinstance(value, collections.Hashable):
            if isinstance(value, list):
                value = tuple(value)
            elif isinstance(value, set):
                value = frozenset(value)
            elif isinstance(value, dict):
                raise TypeError('dict can not be used as constant')
            else:
                raise ValueError('Muttable or custom type is not supported')
        self.__dict__[name] = value

    def __delattr__(self, name):
        """Deny against deleting a declared constant."""

        if name in self.__dict__:
            raise TypeError('Constanst can not be deleted')
        raise NameError("name '%s' is not defined" % name)

    def __call__(self):
        """Return all constans."""

        return self.__dict__


const = Constant()


if __name__ == '__main__':
    import doctest
    doctest.testmod()

如果这还不够,请参阅完整的测试用例。

import decimal
import uuid
import datetime
import unittest

from ..constants import Constant


class TestConstant(unittest.TestCase):
    """
    Test for implementation constants in the Python
    """

    def setUp(self):

        self.const = Constant()

    def tearDown(self):

        del self.const

    def test_create_constant_with_different_variants_of_name(self):

        self.const.CONSTANT = 1
        self.assertEqual(self.const.CONSTANT, 1)
        self.const.Constant = 2
        self.assertEqual(self.const.Constant, 2)
        self.const.ConStAnT = 3
        self.assertEqual(self.const.ConStAnT, 3)
        self.const.constant = 4
        self.assertEqual(self.const.constant, 4)
        self.const.co_ns_ta_nt = 5
        self.assertEqual(self.const.co_ns_ta_nt, 5)
        self.const.constant1111 = 6
        self.assertEqual(self.const.constant1111, 6)

    def test_create_and_change_integer_constant(self):

        self.const.INT = 1234
        self.assertEqual(self.const.INT, 1234)
        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.INT = .211

    def test_create_and_change_float_constant(self):

        self.const.FLOAT = .1234
        self.assertEqual(self.const.FLOAT, .1234)
        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.FLOAT = .211

    def test_create_and_change_list_constant_but_saved_as_tuple(self):

        self.const.LIST = [1, .2, None, True, datetime.date.today(), [], {}]
        self.assertEqual(self.const.LIST, (1, .2, None, True, datetime.date.today(), [], {}))

        self.assertTrue(isinstance(self.const.LIST, tuple))

        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.LIST = .211

    def test_create_and_change_none_constant(self):

        self.const.NONE = None
        self.assertEqual(self.const.NONE, None)
        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.NONE = .211

    def test_create_and_change_boolean_constant(self):

        self.const.BOOLEAN = True
        self.assertEqual(self.const.BOOLEAN, True)
        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.BOOLEAN = False

    def test_create_and_change_string_constant(self):

        self.const.STRING = "Text"
        self.assertEqual(self.const.STRING, "Text")

        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.STRING += '...'

        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.STRING = 'TEst1'

    def test_create_dict_constant(self):

        with self.assertRaisesRegexp(TypeError, 'dict can not be used as constant'):
            self.const.DICT = {}

    def test_create_and_change_tuple_constant(self):

        self.const.TUPLE = (1, .2, None, True, datetime.date.today(), [], {})
        self.assertEqual(self.const.TUPLE, (1, .2, None, True, datetime.date.today(), [], {}))

        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.TUPLE = 'TEst1'

    def test_create_and_change_set_constant(self):

        self.const.SET = {1, .2, None, True, datetime.date.today()}
        self.assertEqual(self.const.SET, {1, .2, None, True, datetime.date.today()})

        self.assertTrue(isinstance(self.const.SET, frozenset))

        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.SET = 3212

    def test_create_and_change_frozenset_constant(self):

        self.const.FROZENSET = frozenset({1, .2, None, True, datetime.date.today()})
        self.assertEqual(self.const.FROZENSET, frozenset({1, .2, None, True, datetime.date.today()}))

        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.FROZENSET = True

    def test_create_and_change_date_constant(self):

        self.const.DATE = datetime.date(1111, 11, 11)
        self.assertEqual(self.const.DATE, datetime.date(1111, 11, 11))

        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.DATE = True

    def test_create_and_change_datetime_constant(self):

        self.const.DATETIME = datetime.datetime(2000, 10, 10, 10, 10)
        self.assertEqual(self.const.DATETIME, datetime.datetime(2000, 10, 10, 10, 10))

        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.DATETIME = None

    def test_create_and_change_decimal_constant(self):

        self.const.DECIMAL = decimal.Decimal(13123.12312312321)
        self.assertEqual(self.const.DECIMAL, decimal.Decimal(13123.12312312321))

        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.DECIMAL = None

    def test_create_and_change_timedelta_constant(self):

        self.const.TIMEDELTA = datetime.timedelta(days=45)
        self.assertEqual(self.const.TIMEDELTA, datetime.timedelta(days=45))

        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.TIMEDELTA = 1

    def test_create_and_change_uuid_constant(self):

        value = uuid.uuid4()
        self.const.UUID = value
        self.assertEqual(self.const.UUID, value)

        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.UUID = []

    def test_try_delete_defined_const(self):

        self.const.VERSION = '0.0.1'
        with self.assertRaisesRegexp(TypeError, 'Constanst can not be deleted'):
            del self.const.VERSION

    def test_try_delete_undefined_const(self):

        with self.assertRaisesRegexp(NameError, "name 'UNDEFINED' is not defined"):
            del self.const.UNDEFINED

    def test_get_all_defined_constants(self):

        self.assertDictEqual(self.const(), {})

        self.const.A = 1
        self.assertDictEqual(self.const(), {'A': 1})

        self.const.B = "Text"
        self.assertDictEqual(self.const(), {'A': 1, 'B': "Text"})

优点:  1.访问整个项目的所有常量  2.严格控制常数值

还缺:  1.不支持自定义类型和类型'dict'

注意:

  1. 使用Python3.4和Python3.5进行测试(我使用'tox')

  2. 测试环境:

  3. $ uname -a
    Linux wlysenko-Aspire 3.13.0-37-generic #64-Ubuntu SMP Mon Sep 22 21:28:38 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux
    

答案 10 :(得分:6)

您可以使用namedtuple作为变通方法来有效地创建一个常量,该常量与Java中的静态最终变量(Java&#34;常量&#34;)的工作方式相同。随着变通办法,它有点优雅。 (更优雅的方法是简单地改进Python语言 - 什么样的语言可以让你重新定义math.pi? - 但我离题了。)

(在我写这篇文章的时候,我意识到这个问题的另一个答案提到了名字,但我会继续在这里,因为我会展示一种语法,与你在Java中所期望的更为平行,因为没有必要像namedtuple那样强制你创建一个命名的类型。)

按照您的示例,您将记住在Java中我们必须在某个类中定义常量;因为你没有提到课程名称,所以我们称之为Foo。这是Java类:

public class Foo {
  public static final String CONST_NAME = "Name";
}

这是等效的Python。

from collections import namedtuple
Foo = namedtuple('_Foo', 'CONST_NAME')('Name')

我想在此处添加的关键点是,您不需要单独的Foo类型(&#34;匿名命名的元组&#34;会很好,即使这听起来像是一个oxymoron),所以我们将我们命名为_Foo的名字命名为希望它不会逃脱导入模块。

这里的第二点是我们立即创建nametuple的实例,称之为Foo;没有必要在单独的步骤中执行此操作(除非您愿意)。现在,您可以在Java中执行以下操作:

>>> Foo.CONST_NAME
'Name'

但你不能指定它:

>>> Foo.CONST_NAME = 'bar'
…
AttributeError: can't set attribute

致谢:我以为我发明了一种命名方法,但后来我发现别人给出了类似的(虽然不那么紧凑)答案。然后我也注意到What are "named tuples" in Python?,它指出sys.version_info现在是一个命名元组,所以Python标准库可能早就提出了这个想法。

请注意,遗憾的是(这仍然是Python),您可以完全删除整个Foo作业:

>>> Foo = 'bar'

(捂脸)

但至少我们会阻止Foo.CONST_NAME价值被改变,而且这比没有好。祝你好运。

答案 11 :(得分:6)

PEP 591具有“最终”限定词。强制执行取决于类型检查器。

因此您可以这样做:

MY_CONSTANT: Final = 12407

注意: Final关键字仅适用于Python 3.8版本

答案 12 :(得分:6)

我会创建一个类来覆盖基础对象类的__setattr__方法并用它包装我的常量,注意我正在使用python 2.7:

class const(object):
    def __init__(self, val):
        super(const, self).__setattr__("value", val)
    def __setattr__(self, name, val):
        raise ValueError("Trying to change a constant value", self)

包装字符串:

>>> constObj = const("Try to change me")
>>> constObj.value
'Try to change me'
>>> constObj.value = "Changed"
Traceback (most recent call last):
   ...
ValueError: Trying to change a constant value
>>> constObj2 = const(" or not")
>>> mutableObj = constObj.value + constObj2.value
>>> mutableObj #just a string
'Try to change me or not'

这很简单,但是如果你想使用与非常量对象相同的常量(不使用constObj.value),它将会更加密集。这可能会导致问题,因此最好保持.value显示并知道您正在使用常量进行操作(尽管可能不是最“pythonic”方式)。

答案 13 :(得分:5)

元组在技术上有资格作为常量,因为如果您尝试更改其中一个值,元组将引发错误。如果你想用一个值声明一个元组,那么在它唯一的值之后放一个逗号,如下所示:

my_tuple = (0 """Or any other value""",)

要检查此变量的值,请使用与此类似的内容:

if my_tuple[0] == 0:
    #Code goes here

如果您尝试更改此值,则会引发错误。

答案 14 :(得分:4)

声明“常量”的Pythonic方法基本上是模块级变量:

RED = 1
GREEN = 2
BLUE = 3

然后编写您的类或函数。因为常量几乎总是整数,并且它们在Python中也是不可变的,所以你很少有机会改变它。

当然,除非您明确设置RED = 2

答案 15 :(得分:3)

我正在尝试以不同的方式在Python中创建一个实常数,也许我找到了漂亮的解决方案。

示例:

创建常量容器

>>> DAYS = Constants(
...     MON=0,
...     TUE=1,
...     WED=2,
...     THU=3,
...     FRI=4,
...     SAT=5,
...     SUN=6
... )   

从容器中获取价值

>>> DAYS.MON
0
>>> DAYS['MON']
0  

以纯python数据结构表示

>>> list(DAYS)
['WED', 'SUN', 'FRI', 'THU', 'MON', 'TUE', 'SAT']
>>> dict(DAYS)
{'WED': 2, 'SUN': 6, 'FRI': 4, 'THU': 3, 'MON': 0, 'TUE': 1, 'SAT': 5}

所有常量都是不可变的

>>> DAYS.MON = 7
...
AttributeError: Immutable attribute

>>> del DAYS.MON 
...
AttributeError: Immutable attribute

仅针对常量自动完成

>>> dir(DAYS)
['FRI', 'MON', 'SAT', 'SUN', 'THU', 'TUE', 'WED']

list.sort

排序
>>> DAYS.sort(key=lambda (k, v): v, reverse=True)
>>> list(DAYS)
['SUN', 'SAT', 'FRI', 'THU', 'WED', 'TUE', 'MON']

python2python3的功能

Simple container for constants

from collections import OrderedDict
from copy import deepcopy

class Constants(object):
    """Container of constant"""

    __slots__ = ('__dict__')

    def __init__(self, **kwargs):

        if list(filter(lambda x: not x.isupper(), kwargs)):
            raise AttributeError('Constant name should be uppercase.')

        super(Constants, self).__setattr__(
            '__dict__',
            OrderedDict(map(lambda x: (x[0], deepcopy(x[1])), kwargs.items()))
        )

    def sort(self, key=None, reverse=False):
        super(Constants, self).__setattr__(
            '__dict__',
            OrderedDict(sorted(self.__dict__.items(), key=key, reverse=reverse))
        )

    def __getitem__(self, name):
        return self.__dict__[name]

    def __len__(self):
        return  len(self.__dict__)

    def __iter__(self):
        for name in self.__dict__:
            yield name

    def keys(self):
        return list(self)

    def __str__(self):
        return str(list(self))

    def __repr__(self):
        return '<%s: %s>' % (self.__class__.__name__, str(self.__dict__))

    def __dir__(self):
        return list(self)

    def __setattr__(self, name, value):
        raise AttributeError("Immutable attribute")

    def __delattr__(*_):
        raise AttributeError("Immutable attribute")

答案 16 :(得分:3)

Python字典是可变的,因此它们似乎不是声明常量的好方法:

>>> constants = {"foo":1, "bar":2}
>>> print constants
{'foo': 1, 'bar': 2}
>>> constants["bar"] = 3
>>> print constants
{'foo': 1, 'bar': 3}

答案 17 :(得分:3)

也许pconst库会为您提供帮助(github)。

$ pip install pconst

from pconst import const
const.APPLE_PRICE = 100
const.APPLE_PRICE = 200

[Out] Constant value of "APPLE_PRICE" is not editable.

答案 18 :(得分:3)

有一个更干净的方法可以使用namedtuple做到这一点:

from collections import namedtuple


def make_consts(name, **kwargs):
    return namedtuple(name, kwargs.keys())(**kwargs)

用法示例

CONSTS = make_consts("baz1",
                     foo=1,
                     bar=2)

通过这种精确的方法,您可以为常量命名空间。

答案 19 :(得分:3)

我们可以创建一个描述符对象。

class Constant:
  def __init__(self,value=None):
    self.value = value
  def __get__(self,instance,owner):
    return self.value
  def __set__(self,instance,value):
    raise ValueError("You can't change a constant")

1)如果我们想在实例级别使用常量,那么:

class A:
  NULL = Constant()
  NUM = Constant(0xFF)

class B:
  NAME = Constant('bar')
  LISTA = Constant([0,1,'INFINITY'])

>>> obj=A()
>>> print(obj.NUM)  #=> 255
>>> obj.NUM =100

Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: You can't change a constant

2)如果我们只想在类级别创建常量,我们可以使用一个元类作为我们常量的容器(我们的描述符对象);所有下降的类都将继承我们的常量(我们的描述符对象)而没有任何可以修改的风险。

# metaclass of my class Foo
class FooMeta(type): pass

# class Foo
class Foo(metaclass=FooMeta): pass

# I create constants in my metaclass
FooMeta.NUM = Constant(0xff)
FooMeta.NAME = Constant('FOO')

>>> Foo.NUM   #=> 255
>>> Foo.NAME  #=> 'FOO'
>>> Foo.NUM = 0 #=> ValueError: You can't change a constant

如果我创建了Foo的子类,这个类将继承该常量而不可能修改它们

class Bar(Foo): pass

>>> Bar.NUM  #=> 255
>>> Bar.NUM = 0  #=> ValueError: You can't change a constant

答案 20 :(得分:3)

在python中,常量只是一个变量,在所有大写中都有一个名称,单词用下划线字符分隔,

e.g

DAYS_IN_WEEK = 7

该值是可变的,因为您可以更改它。但鉴于名称的规则告诉你是一个常数,为什么你呢?我的意思是,毕竟这是你的计划!

这是整个python采用的方法。出于同样的原因,没有private个关键字。使用下划线对名称进行前缀,您知道它是私有的。代码可以破坏规则....就像程序员无论如何都可以删除私有关键字一样。

Python可以添加一个const关键字......但程序员可以删除关键字,然后根据需要更改常量,但为什么这样做呢?如果您想破坏规则,您仍然可以更改规则。但是,如果名称明确表达,为什么还要打破规则?

也许有一些单元测试可以将值更改应用到哪里?要查看每周8天的情况,即使在现实世界中,也不能更改本周的天数。如果语言停止你做出异常如果只有这一个案例你需要打破规则...你必须停止将它声明为常量,即使它仍然是应用程序中的常量,并且有只是这一个测试案例,看看如果它发生了变化会发生什么。

全部大写名称告诉您它应该是一个常量。这才是最重要的。不是语言强制限制代码,你有权改变。

这就是python的哲学。

答案 21 :(得分:2)

这是我创建的一组成语,目的是改进一些已经可用的答案。

我知道常量的使用不是pythonic,因此您不应在家中使用它!

但是,Python是一种动态语言!该论坛展示了如何创建外观和感觉像常量的构造。该答案的主要目的是探讨语言可以表达的内容。

请不要对我太苛刻:-)。

有关更多详细信息,我写了accompaniment blog about these idioms

在这篇文章中,我将常量变量称为对值(不可变或其他)的常量引用。此外,我说的是,当变量引用客户端代码无法更新其可变值的可变对象时,该变量具有冻结值。

一个常量空间(SpaceConstants)

这个惯用法创建了看起来像常量变量的名称空间(又称SpaceConstants)。它是Alex Martelli对代码段的修改,以避免使用模块对象。特别是,此修改使用了我所谓的类工厂,因为在 SpaceConstants 函数中,定义了一个名为 SpaceConstants 的类,并返回了它的一个实例。

我探索了如何使用类工厂在stackoverflowblogpost中的Python中实现基于策略的设计。

def SpaceConstants():
    def setattr(self, name, value):
        if hasattr(self, name):
            raise AttributeError(
                "Cannot reassign members"
            )
        self.__dict__[name] = value
    cls = type('SpaceConstants', (), {
        '__setattr__': setattr
    })
    return cls()

sc = SpaceConstants()

print(sc.x) # raise "AttributeError: 'SpaceConstants' object has no attribute 'x'"
sc.x = 2 # bind attribute x
print(sc.x) # print "2"
sc.x = 3 # raise "AttributeError: Cannot reassign members"
sc.y = {'name': 'y', 'value': 2} # bind attribute y
print(sc.y) # print "{'name': 'y', 'value': 2}"
sc.y['name'] = 'yprime' # mutable object can be changed
print(sc.y) # print "{'name': 'yprime', 'value': 2}"
sc.y = {} # raise "AttributeError: Cannot reassign members"

冻结值的空间(SpaceFrozenValues)

下一个习惯用法是对 SpaceConstants 的修改,其中冻结了引用的可变对象。此实现利用了我在 setattr getattr 函数之间使用的shared closure。可变对象的值由共享共享函数内部的变量 cache define复制和引用。它形成了我所说的closure protected copy of a mutable object

在使用此惯用语时必须小心,因为 getattr 通过执行深层复制来返回缓存的值。此操作可能会对大型对象产生重大的性能影响!

from copy import deepcopy

def SpaceFrozenValues():
    cache = {}
    def setattr(self, name, value):
        nonlocal cache
        if name in cache:
            raise AttributeError(
                "Cannot reassign members"
            )
        cache[name] = deepcopy(value)
    def getattr(self, name):
        nonlocal cache
        if name not in cache:
            raise AttributeError(
                "Object has no attribute '{}'".format(name)
            )
        return deepcopy(cache[name])
    cls = type('SpaceFrozenValues', (),{
        '__getattr__': getattr,
        '__setattr__': setattr
    })
    return cls()

fv = SpaceFrozenValues()
print(fv.x) # AttributeError: Object has no attribute 'x'
fv.x = 2 # bind attribute x
print(fv.x) # print "2"
fv.x = 3 # raise "AttributeError: Cannot reassign members"
fv.y = {'name': 'y', 'value': 2} # bind attribute y
print(fv.y) # print "{'name': 'y', 'value': 2}"
fv.y['name'] = 'yprime' # you can try to change mutable objects
print(fv.y) # print "{'name': 'y', 'value': 2}"
fv.y = {} # raise "AttributeError: Cannot reassign members"

恒定空间(ConstantSpace)

此习语是常量变量或 ConstantSpace 的不可变命名空间。它是stackoverflow中Jon Betts非常简单的答案与class factory的组合。

def ConstantSpace(**args):
    args['__slots__'] = ()
    cls = type('ConstantSpace', (), args)
    return cls()

cs = ConstantSpace(
    x = 2,
    y = {'name': 'y', 'value': 2}
)

print(cs.x) # print "2"
cs.x = 3 # raise "AttributeError: 'ConstantSpace' object attribute 'x' is read-only"
print(cs.y) # print "{'name': 'y', 'value': 2}"
cs.y['name'] = 'yprime' # mutable object can be changed
print(cs.y) # print "{'name': 'yprime', 'value': 2}"
cs.y = {} # raise "AttributeError: 'ConstantSpace' object attribute 'x' is read-only"
cs.z = 3 # raise "AttributeError: 'ConstantSpace' object has no attribute 'z'"

冻结空间(FrozenSpace)

这个习惯用法是冻结变量或 FrozenSpace 的不可变命名空间。它是通过将每个变量设为生成的 FrozenSpace 类的protected property by closure来从先前的模式派生的。

from copy import deepcopy

def FreezeProperty(value):
    cache = deepcopy(value)
    return property(
        lambda self: deepcopy(cache)
    )

def FrozenSpace(**args):
    args = {k: FreezeProperty(v) for k, v in args.items()}
    args['__slots__'] = ()
    cls = type('FrozenSpace', (), args)
    return cls()

fs = FrozenSpace(
    x = 2,
    y = {'name': 'y', 'value': 2}
)

print(fs.x) # print "2"
fs.x = 3 # raise "AttributeError: 'FrozenSpace' object attribute 'x' is read-only"
print(fs.y) # print "{'name': 'y', 'value': 2}"
fs.y['name'] = 'yprime' # try to change mutable object
print(fs.y) # print "{'name': 'y', 'value': 2}"
fs.y = {} # raise "AttributeError: 'FrozenSpace' object attribute 'x' is read-only"
fs.z = 3 # raise "AttributeError: 'FrozenSpace' object has no attribute 'z'"

答案 22 :(得分:2)

您可以使用collections.namedtupleitertools

执行此操作
import collections
import itertools
def Constants(Name, *Args, **Kwargs):
  t = collections.namedtuple(Name, itertools.chain(Args, Kwargs.keys()))
  return t(*itertools.chain(Args, Kwargs.values()))

>>> myConstants = Constants('MyConstants', 'One', 'Two', Three = 'Four')
>>> print myConstants.One
One
>>> print myConstants.Two
Two
>>> print myConstants.Three
Four
>>> myConstants.One = 'Two'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: can't set attribute

答案 23 :(得分:2)

(本段旨在对提及namedtuple的那些答案herethere进行评论,但是由于太长而无法放入评论中,因此,在这里。)

上面提到的namedtuple方法绝对是创新的。但是,为了完整起见,在its official documentation的NamedTuple部分的末尾,其内容为:

  

枚举常量可以用命名元组实现,但是使用简单的类声明更简单,更高效:

class Status:
    open, pending, closed = range(3)

换句话说,官方文档更喜欢使用一种实用的方式,而不是实际实现只读行为。我想这将成为Zen of Python的另一个示例:

  

简单胜于复杂。

     

实用胜过纯洁。

答案 24 :(得分:2)

没有完美的方法来做到这一点。据我了解,大多数程序员只会将标识符大写,因此PI = 3.142很容易理解为常量。

反之,如果您想要某种实际上像常量的东西,我不确定您会找到它。无论您做什么,总会有某种方式来编辑“常量”,因此它并不是真正的常量。这是一个非常简单,肮脏的示例:

def define(name, value):
  if (name + str(id(name))) not in globals():
    globals()[name + str(id(name))] = value

def constant(name):
  return globals()[name + str(id(name))]

define("PI",3.142)

print(constant("PI"))

这看起来像一个PHP样式的常量。

实际上,某人更改值所需要的是:

globals()["PI"+str(id("PI"))] = 3.1415

对于您在这里可以找到的所有其他解决方案,这都是相同的-即使是聪明的创建类并重新定义set属性方法的解决方案-总是会有解决的办法。就是这样。

我的建议是避免所有麻烦,仅将标识符大写。确实不是一个适当的常数,但是再也没有。

答案 25 :(得分:2)

只需你可以:

STRING_CONSTANT = "hi"
NUMBER_CONSTANT = 89

希望让一切变得更简单

答案 26 :(得分:2)

在Python中,常量不存在。但是,您可以指示变量是常量,并且不能通过将CONST_CONSTANT_添加到变量名称的开头或在BLOCK CAPITALS中命名变量来声明变量,并且声明它是常量在评论中:

    myVariable = 0
    CONST_daysInWeek = 7    # This is a constant - do not change its value.   
    CONSTANT_daysInMonth = 30 # This is also a constant - do not change this value.

答案 27 :(得分:2)

你可以使用StringVar或IntVar等,你的常量是 const_val

layer

答案 28 :(得分:2)

如果你想要常数并且不关心他们的价值观,那么这就是一个技巧:

只需定义空类。

e.g:

class RED: 
    pass
class BLUE: 
    pass

答案 29 :(得分:1)

你可以将一个常量包装在一个numpy数组中,只标记它是只写的,并且总是用索引零来调用它。

import numpy as np

# declare a constant
CONSTANT = 'hello'

# put constant in numpy and make read only
CONSTANT = np.array([CONSTANT])
CONSTANT.flags.writeable = False
# alternatively: CONSTANT.setflags(write=0)

# call our constant using 0 index    
print 'CONSTANT %s' % CONSTANT[0]

# attempt to modify our constant with try/except
new_value = 'goodbye'
try:
    CONSTANT[0] = new_value
except:
    print "cannot change CONSTANT to '%s' it's value '%s' is immutable" % (
        new_value, CONSTANT[0])

# attempt to modify our constant producing ValueError
CONSTANT[0] = new_value



>>>
CONSTANT hello
cannot change CONSTANT to 'goodbye' it's value 'hello' is immutable
Traceback (most recent call last):
  File "shuffle_test.py", line 15, in <module>
    CONSTANT[0] = new_value
ValueError: assignment destination is read-only

当然这只保护numpy的内容,而不是变量“CONSTANT”本身;你仍然可以这样做:

CONSTANT = 'foo'

CONSTANT会发生变化,但是这会在以后第一次在脚本中调用CONSTANT[0]时快速抛出TypeError。

虽然......我想如果你在某个时候将其改为

CONSTANT = [1,2,3]

现在你不会再得到TypeError了。 hmmmm ....

https://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.setflags.html

答案 30 :(得分:1)

我知道这是一个老问题,但是由于仍在添加新的解决方案,因此我希望使可能的解决方案列表更加完整。您可以通过继承如下所示的类来通过属性访问在实例内实现常量:

class ConstantError(Exception):
    pass  # maybe give nice error message

class AllowConstants:
    _constants = None
    _class_constants = None

    def __init__(self):
        self._constants = {}
        if self._class_constants is not None:
            self._constants.update(self._class_constants)

    def constant(self, name, value):
        assert isinstance(name, str)
        assert self._constants is not None, "AllowConstants was not initialized"
        if name in self._constants or name in self.__dict__:
            raise ConstantError(name)
        self._constants[name] = value

    def __getattr__(self, attr):
        if attr in self._constants:
            return self._constants[attr]
        raise AttributeError(attr)

    def __setattr__(self, attr, val):
        if self._constants is None:
            # not finished initialization
            self.__dict__[attr] = val
        else:
            if attr in self._constants:
                raise ConstantError(attr)
            else:
                self.__dict__[attr] = val

    def __dir__(self):
        return super().__dir__() + list(self._constants.keys())

对此进行子类化时,您创建的常量将受到保护:

class Example(AllowConstants):
    def __init__(self, a, b):
        super().__init__()
        self.constant("b", b)
        self.a = a

    def try_a(self, value):
        self.a = value

    def try_b(self, value):
        self.b = value

    def __str__(self):
        return str({"a": self.a, "b": self.b})

    def __repr__(self):
        return self.__str__()


example = Example(1, 2)
print(example)  # {'a': 1, 'b': 2}

example.try_a(5)
print(example)  # {'a': 5, 'b': 2}

example.try_b(6)  # ConstantError: b

example.a = 7
print(example)  # {'a': 7, 'b': 2}

example.b = 8  # ConstantError: b

print(hasattr(example, "b"))  # True

#  To show that constants really do immediately become constant: 

class AnotherExample(AllowConstants):
    def __init__(self):
        super().__init__()
        self.constant("a", 2)
        print(self.a)
        self.a=3


AnotherExample()  # 2  ConstantError: a


# finally, for class constants:
class YetAnotherExample(Example):
    _class_constants = {
        'BLA': 3
    }

    def __init__(self, a, b):
        super().__init__(a,b)

    def try_BLA(self, value):
        self.BLA = value

ex3 = YetAnotherExample(10, 20)
ex3.BLA  # 3
ex3.try_BLA(10)  # ConstantError: BLA
ex3.BLA = 4  # ConstantError: BLA

常量是局部的(继承自AllowConstants的类的每个实例都将具有其自己的常量),只要不重新分配它们就可以充当普通属性,并且编写从其继承的类或多或少都可以使它们相同样式与支持常量的语言一样。

此外,如果要防止任何人通过直接访问instance._constants来更改值,则可以使用许多容器之一,不允许使用其他答案中建议的容器。最后,如果确实需要,可以通过对AllowConstants的更多属性访问来阻止人们将所有instance._constants设置为新字典。 (当然,这些都不是非常pythonic的,但这不重要)。

编辑(由于使python unpythonic是一个有趣的游戏):为了使继承更容易,您可以按以下方式修改AllowConstants:

class AllowConstants:
    _constants = None
    _class_constants = None

    def __init__(self):
        self._constants = {}
        self._update_class_constants()

    def __init_subclass__(cls):
        """
        Without this, it is necessary to set _class_constants in any subclass of any class that has class constants
        """
        if cls._class_constants is not None:
            #prevent trouble where _class_constants is not overwritten
            possible_cases = cls.__mro__[1:-1] #0 will have cls and -1 will have object
            for case in possible_cases:
                if cls._class_constants is case._class_constants:
                    cls._class_constants = None
                    break

    def _update_class_constants(self):
        """
        Help with the inheritance of class constants
        """
        for superclass in self.__class__.__mro__:
            if hasattr(superclass, "_class_constants"):
                sccc = superclass._class_constants
                if sccc is not None:
                    for key in sccc:
                        if key in self._constants:
                            raise ConstantError(key)
                    self._constants.update(sccc)

    def constant(self, name, value):
        assert isinstance(name, str)
        assert self._constants is not None, "AllowConstants was not initialized"
        if name in self._constants or name in self.__dict__:
            raise ConstantError(name)
        self._constants[name] = value

    def __getattr__(self, attr):
        if attr in self._constants:
            return self._constants[attr]
        raise AttributeError(attr)

    def __setattr__(self, attr, val):
        if self._constants is None:
            # not finished initialization
            self.__dict__[attr] = val
        else:
            if attr in self._constants:
                raise ConstantError(attr)
            else:
                self.__dict__[attr] = val

    def __dir__(self):
        return super().__dir__() + list(self._constants.keys())

那样您就可以做到:

class Example(AllowConstants):
    _class_constants = {
        "BLA": 2
    }
    def __init__(self, a, b):
        super().__init__()
        self.constant("b", b)
        self.a = a

    def try_a(self, value):
        self.a = value

    def try_b(self, value):
        self.b = value

    def __str__(self):
        return str({"a": self.a, "b": self.b})

    def __repr__(self):
        return self.__str__()


class ChildExample1(Example):
    _class_constants = {
        "BLI": 88
    }


class ChildExample2(Example):
    _class_constants = {
        "BLA": 44
    }


example = ChildExample1(2,3)
print(example.BLA)  # 2
example.BLA = 8  # ConstantError BLA
print(example.BLI)  # 88
example.BLI = 8  # ConstantError BLI

example = ChildExample2(2,3)  # ConstantError BLA

答案 31 :(得分:1)

我为python const编写了一个util lib: kkconst - pypi 支持str,int,float,datetime

const字段实例将保持其基类型行为。

例如:

public class RestartAppReceiver extends BroadcastReceiver {

    private static final String LOG_TAG = "RestartAppReceiver";

    public RestartAppReceiver() {
    }

    @Override
    public void onReceive(Context context, Intent intent) {

        if (intent != null) {
            String action = intent.getAction();

            switch (action) {
                case Intent.ACTION_BOOT_COMPLETED:
                    Log.i(LOG_TAG, "Start resetting alarms after reboot");


                    //restart what you need 


                    Log.i(LOG_TAG, "Finish resetting alarms after reboot");
                    break;
                default:
                    break;
            }
        }
    }
}

更多细节用法你可以阅读pypi网址: pypi github

答案 32 :(得分:1)

在我的情况下,我需要不可变的bytearray来实现一个加密库,其中包含许多我想要确保的常量数字。

This answer有效但尝试重新分配bytearray元素不会引发错误。

def const(func):
    '''implement const decorator'''
    def fset(self, val):
        '''attempting to set a const raises `ConstError`'''
        class ConstError(TypeError):
            '''special exception for const reassignment'''
            pass

        raise ConstError

    def fget(self):
        '''get a const'''
        return func()

    return property(fget, fset)


class Consts(object):
    '''contain all constants'''

    @const
    def C1():
        '''reassignment to C1 fails silently'''
        return bytearray.fromhex('deadbeef')

    @const
    def pi():
        '''is immutable'''
        return 3.141592653589793

常量是不可变的,但是常量字节数分配无提示失败:

>>> c = Consts()
>>> c.pi = 6.283185307179586  # (https://en.wikipedia.org/wiki/Tau_(2%CF%80))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "consts.py", line 9, in fset
    raise ConstError
__main__.ConstError
>>> c.C1[0] = 0
>>> c.C1[0]
222
>>> c.C1
bytearray(b'\xde\xad\xbe\xef')

更强大,更简单,甚至可能更“pythonic”的方法涉及使用memoryview对象(&lt; = python-2.6中的缓冲区对象)。

import sys

PY_VER = sys.version.split()[0].split('.')

if int(PY_VER[0]) == 2:
    if int(PY_VER[1]) < 6:
        raise NotImplementedError
    elif int(PY_VER[1]) == 6:
        memoryview = buffer

class ConstArray(object):
    '''represent a constant bytearray'''
    def __init__(self, init):
        '''
        create a hidden bytearray and expose a memoryview of that bytearray for
        read-only use
        '''
        if int(PY_VER[1]) == 6:
            self.__array = bytearray(init.decode('hex'))
        else:
            self.__array = bytearray.fromhex(init)

        self.array = memoryview(self.__array)

    def __str__(self):
        return str(self.__array)

    def __getitem__(self, *args, **kwargs):
       return self.array.__getitem__(*args, **kwargs)

ConstArray项目分配是TypeError

>>> C1 = ConstArray('deadbeef')
>>> C1[0] = 0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'ConstArray' object does not support item assignment
>>> C1[0]
222

答案 33 :(得分:1)

from enum import Enum
class StringConsts(str,Enum):
    ONE='one'
    TWO='two'

print(f'Truth is  {StringConsts.ONE=="one"}') #Truth is True
StringConsts.ONE="one" #Error: Cannot reassign

Enum和str的这种混合使您不必重新实现setattr(通过Enum)并与其他str对象进行比较(通过str)。

这可能会完全弃用http://code.activestate.com/recipes/65207-constants-in-python/?in=user-97991

答案 34 :(得分:0)

在Python中创建常量的更好方法是从 优秀的attrs库,可以帮助您 Python程序员创建没有样板的类。的 short-con软件包对 通过提供便利包装器来常量 attr.make_class。 [免责声明:我是短剧的作者。]

可以通过dictkwargs显式声明值:这些 例子做同样的事情。 constants()功能支持以下功能: 库,cons()是简单的基于kwarg的用法的助手。

from short_con import constants, cons

Pieces = constants('Pieces', dict(king = 0, queen = 9, rook = 5, bishop = 3, knight = 3, pawn = 1))
Pieces = cons('Pieces', king = 0, queen = 9, rook = 5, bishop = 3, knight = 3, pawn = 1)

对于与值相同(或可以从中得出)的情况 属性名称,用法更加紧凑。只需提供名称作为 用空格分隔的字符串,列表或元组。

NAMES = 'KING QUEEN ROOK BISHOP KNIGHT PAWN'
xs = NAMES.split()

Pieces = constants('Pieces', NAMES)      # All of these do the same thing.
Pieces = constants('Pieces', xs)
Pieces = constants('Pieces', tuple(xs))

基于名称的用法支持一些样式约定:大写或 小写的属性名称以及枚举样式值。

可以直接访问基础值,这与由 内置enum库:

Pieces.QUEEN        # short-con usage
Pieces.QUEEN.value  # enum library usage

该对象可以直接迭代并转换为其他集合:

for name, value in Pieces:
    print(name, value)

d = dict(Pieces)
tups = list(Pieces)

答案 35 :(得分:0)

您可以将Tuple用于常量变量:

•元组是有序且不可更改

的集合
my_tuple = (1, "Hello", 3.4)
print(my_tuple[0])

答案 36 :(得分:0)

好吧..即使这已经过时了,让我在这里加上我的2美分: - )

class ConstDict(dict):
    def __init__(self, *args, **kwargs):
        super(ConstDict, self).__init__(*args, **kwargs)

    def __setitem__(self, key, value):
        if key in self:
            raise ValueError("Value %s already exists" % (key))
        super(ConstDict, self).__setitem__(key, value)

而不是要破坏ValueError,您可以阻止任何更新发生。这样做的一个优点是您可以在程序中动态添加常量,但一旦设置了常量就无法更改。您还可以在设置常量之前添加任何规则(类似于键必须是字符串或小写字符串或大写字符串等等,然后才能设置键)

但是,我认为在Python中设置常量并不重要。在C中不会发生任何优化,因此我认为它不是必需的。

答案 37 :(得分:0)

给出的所有答案基本上都是两种类型:

  1. 创建某种对象,您可以为此 创建一旦定义就无法更改的属性。
  2. 使用约定(例如在所有大写字母中编写常量,或者对于Python 3.8,使用final限定符表示,您打算将一个或多个名称用作常量。
  3. li>

它们可以概括为:“您无法使用Python来完成所要求的工作”。

但是,实际上有一种方法可以创建带有真实常量的模块。这样做的代码相当复杂,我将仅概述所需要做的事情,因为它已经在开源许可证下提供了。

  1. 使用import hook启用自定义模块的创建。我为此使用的通用代码是found here
  2. 创建一个特殊的dict,它仅允许添加符合您所选模式的项目(例如,所有大写字母中的名称),并防止更改这些名称的值。为此,您将需要定义自己的方法,例如__setitem____delitem__等。该字典的代码(例如找到的in this file,超过250行)是大约100行。
  3. 无法修改普通Python模块的dict。因此,在创建模块时,您需要首先在特殊字典中执行代码,然后使用其内容更新模块的字典。
  4. 为防止从模块外部修改contant的值(例如,猴子修补),可以用__class__和{{1}的自定义变量替换模块的__setattr__ }方法已重新定义。

documentation about this example can be found here。可能应该对其进行更新以反映对此问题给出的答案。

答案 38 :(得分:0)

不禁提供了我自己的非常轻巧的极简reference实现(可能是对以前的元类答案的一种变体)。

常量存储在容器类中(无需实例化)。值只能设置一次,但是一旦设置就无法更改(或删除)。

我个人目前没有用例,但这是一个有趣的练习。

metaclass

随时提出改进建议。

答案 39 :(得分:0)

在所有答案中,这是在Python中创建常量的最简单方法。只需制作一个一维元组。

myconstant_var = (10,)

就是这样。现在变量myconstant_var无法更改

答案 40 :(得分:0)

您可以借助下一个类来模拟常量变量。用法示例:

# Const
const = Const().add(two=2, three=3)

print 'const.two: ', const.two
print 'const.three: ', const.three

const.add(four=4)

print 'const.four: ', const.four

#const.four = 5 # a error here: four is a constant

const.add(six=6)

print 'const.six: ', const.six

const2 = Const().add(five=5) # creating a new namespace with Const()
print 'const2.five: ', const2.five
#print 'const2.four: ', const2.four # a error here: four does not exist in const2 namespace

const2.add(five=26)

如果要启动新的常量命名空间,请调用构造函数。请注意,当Martelli的 const 类不是时,该类是受保护的意外修改序列类型常量。

来源如下。

from copy import copy

class Const(object):
"A class to create objects with constant fields."

def __init__(self):
    object.__setattr__(self, '_names', [])


def add(self, **nameVals):
    for name, val in nameVals.iteritems():          
        if hasattr(self, name):
            raise ConstError('A field with a name \'%s\' is already exist in Const class.' % name)

        setattr(self, name, copy(val)) # set up getter

        self._names.append(name)

    return self


def __setattr__(self, name, val):
    if name in self._names:
        raise ConstError('You cannot change a value of a stored constant.')

    object.__setattr__(self, name, val)

答案 41 :(得分:0)

扩展Raufio的答案,添加 repr 以返回值。

class const(object):
    def __init__(self, val):
        super(const, self).__setattr__("value", val)
    def __setattr__(self, name, val):
        raise ValueError("Trying to change a constant value", self)
    def __repr__(self):
        return ('{0}'.format(self.value))

dt = const(float(0.01))
print dt

然后对象的行为更像你期望的那样,你可以直接访问它而不是'.value'

答案 42 :(得分:-12)

python字典一旦声明就不可更改,可以作为常量。

my_consts={"TIMEOUT":300, "RETRIES":10, "STATE":"happy"}

i=301
if i > my_consts["TIMEOUT"]:
  print "I've just timed out. Sorry folks."
  print "I tried, many times, " + str(my_consts["RETRIES"]) + " in fact."
  print "But I am still feeling quite " + my_consts["STATE"]