如何在Python中创建常量?

如何在Python中创建常量?,python,constants,Python,Constants,有没有办法在Python中声明常量?在Java中,我们可以通过以下方式创建常量值: 公共静态最终字符串CONST_NAME=“NAME”; Python中上述Java常量声明的等价物是什么?不,没有。在Python中不能将变量或值声明为常量。只是不要改变它 如果您在一个班级中,则等效值为: class Foo(object): CONST_NAME = "Name" 如果不是,那只是 CONST_NAME = "Name" 但是您可能想看看A

有没有办法在Python中声明常量?在Java中,我们可以通过以下方式创建常量值:

公共静态最终字符串CONST_NAME=“NAME”;

Python中上述Java常量声明的等价物是什么?

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

如果您在一个班级中,则等效值为:

class Foo(object):
    CONST_NAME = "Name"
如果不是,那只是

CONST_NAME = "Name"
但是您可能想看看Alex Martelli的代码片段


从Python3.8开始,有一个变量注释会告诉静态类型检查器(比如mypy)不应该重新分配变量。这与Java的
final
最接近。但是,它实际上并不阻止重新分配:

from typing import Final

a: Final = 1

# Executes fine, but mypy will report an error if you run mypy on this:
a = 2

在Python中,人们使用命名约定,而不是语言强制执行某些内容,例如私有方法使用
\uu方法
,受保护方法使用
\u方法

因此,以同样的方式,您可以简单地将常量声明为所有大写

MY_CONSTANT = "one"
如果您希望这个常量永远不变,您可以挂接到属性访问并执行一些技巧,但更简单的方法是声明一个函数

def MY_CONSTANT():
    return "one"
唯一的问题是在任何地方都必须执行MY_CONSTANT(),但在python中,
MY_CONSTANT=“one”
是正确的方法(通常)

您还可以使用创建常量:

>>> 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
>>> constants = {"foo":1, "bar":2}
>>> print constants
{'foo': 1, 'bar': 2}
>>> constants["bar"] = 3
>>> print constants
{'foo': 1, 'bar': 3}
>>从集合导入名为tuple的
>>>常数=命名整数('Constants',['pi','e']))
>>>常数=常数(3.14,2.718)
>>>常数.pi
3.14
>>>常数pi=3
回溯(最近一次呼叫最后一次):
文件“”,第1行,在
AttributeError:无法设置属性

声明“常量”的Python方式基本上是一个模块级变量:

RED = 1
GREEN = 2
BLUE = 3
然后编写类或函数。由于常量几乎都是整数,而且在Python中也是不可变的,所以修改它的可能性很小


当然,除非在其他语言中明确设置
RED=2
没有
const
关键字,否则可以创建一个属性具有“getter函数”来读取数据,但没有“setter函数”来重新写入数据这基本上可以保护标识符不被更改。

下面是一个使用类属性的替代实现:

请注意,对于想知道常量的读者来说,代码远不容易。见下面的解释

代码说明:

  • 定义一个接受表达式的函数
    constant
    ,并使用它构造一个“getter”——一个只返回表达式值的函数
  • setter函数引发TypeError,因此它是只读的
  • 使用刚刚创建的
    常量
    函数作为装饰,快速定义只读属性

  • 还有一些更老式的方式:

    (代码相当复杂,下面有更多解释)

    请注意,@apply decorator似乎已被弃用

  • 要定义标识符FOO,首先定义两个函数(fset、fget-名称由我选择)
  • 然后使用内置的
    属性
    函数构造一个可以“设置”或“获取”的对象
  • 请注意,
    属性
    函数的前两个参数分别命名为
    fset
    fget
  • 我们为自己的getter&setter选择了这些名称,并使用应用于该范围的所有本地定义的**(双星号)创建了一个关键字词典,以将参数传递给
    属性
    函数

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

    >>> 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
    
    >>> constants = {"foo":1, "bar":2}
    >>> print constants
    {'foo': 1, 'bar': 2}
    >>> constants["bar"] = 3
    >>> print constants
    {'foo': 1, 'bar': 3}
    

    我将创建一个覆盖基本对象类的
    \uuuuu setattr\uuuu
    方法的类,并用它包装我的常量,注意我使用的是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”的方式)。

    除了两个最重要的答案(只需使用大写的变量,或使用属性使值只读),我想提到的是,可以使用元类来实现命名常量。我提供了一个使用元类的非常简单的解决方案,如果您想让值更详细地了解其类型/名称,那么这个解决方案可能会有所帮助:

    >>> 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,如果您能提供反馈,我将不胜感激。:-)

    这里是一个“Constants”类的实现,它创建具有只读(常量)属性的实例。例如,可以使用
    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
    
    多亏了,这是我的出发点。已更改,因此用法语法不是
    Nums['ONE']
    而是
    Nums.ONE

    多亏了@Raufio的回答,我们才有了超越setattr的想法

    或者要了解更多功能的实现,请参阅@Hans_meine的

    我最近发现了一个非常简洁的更新,它会自动产生有意义的错误消息和错误
    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'
    
    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
    
    my_tuple = (0 """Or any other value""",)
    
    if my_tuple[0] == 0:
        #Code goes here
    
    public class Foo {
      public static final String CONST_NAME = "Name";
    }
    
    from collections import namedtuple
    Foo = namedtuple('_Foo', 'CONST_NAME')('Name')
    
    >>> Foo.CONST_NAME
    'Name'
    
    >>> Foo.CONST_NAME = 'bar'
    …
    AttributeError: can't set attribute
    
    >>> Foo = 'bar'
    
    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"})
    
    $ 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
    
    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")
    
    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
    
    # 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
    
    class Bar(Foo): pass
    
    >>> Bar.NUM  #=> 255
    >>> Bar.NUM = 0  #=> ValueError: You can't change a constant
    
    class MyFinalProperty(object):
    
        @property
        def name(self):
            return "John"
    
    class RED: 
        pass
    class BLUE: 
        pass
    
    def MY_CONSTANT():
        return 42
    
    val = 'Stackoverflow'
    const_val = StringVar(val)
    const.trace('w', reverse)
    
    def reverse(*args):
        const_val.set(val)
    
    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
    
    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"))
    
    globals()["PI"+str(id("PI"))] = 3.1415
    
    class Status:
        open, pending, closed = range(3)
    
    from collections import namedtuple
    
    
    def make_consts(name, **kwargs):
        return namedtuple(name, kwargs.keys())(**kwargs)
    
    CONSTS = make_consts("baz1",
                         foo=1,
                         bar=2)
    
    from pconst import const
    const.APPLE_PRICE = 100
    const.APPLE_PRICE = 200
    
    MY_CONSTANT: Final = 12407
    
    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