如何在Python中声明常量?
在Java中,我们做:
public static final String CONST_NAME = "Name";
如何在Python中声明常量?
在Java中,我们做:
public static final String CONST_NAME = "Name";
当前回答
(这一段本来是对那些提到namedtuple的答案的注释,但它太长了,不适合注释,所以,就这样吧。)
上面提到的命名元组方法绝对是创新的。不过,为了完整起见,在其官方文档的NamedTuple部分的末尾,它如下所示:
枚举常量可以用命名元组实现,但使用简单的类声明更简单高效: 类的状态: 打开,待处理,关闭= range(3)
换句话说,官方文档更倾向于使用一种实用的方式,而不是实际实现只读行为。我想这是Python的另一个禅宗的例子:
简单比复杂好。 实用胜过纯粹。
其他回答
在我的例子中,我需要不可变字节数组来实现包含许多文字数字的加密库,我想确保这些数字是常量。
这个答案是有效的,但是尝试重赋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
常量是不可变的,但是常量bytearray赋值默默失败:
>>> 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')
一种更强大、更简单,甚至可能更“python化”的方法涉及使用memoryview对象(<= 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
(这一段本来是对那些提到namedtuple的答案的注释,但它太长了,不适合注释,所以,就这样吧。)
上面提到的命名元组方法绝对是创新的。不过,为了完整起见,在其官方文档的NamedTuple部分的末尾,它如下所示:
枚举常量可以用命名元组实现,但使用简单的类声明更简单高效: 类的状态: 打开,待处理,关闭= range(3)
换句话说,官方文档更倾向于使用一种实用的方式,而不是实际实现只读行为。我想这是Python的另一个禅宗的例子:
简单比复杂好。 实用胜过纯粹。
元组在技术上属于常量,因为如果您试图更改其中一个值,元组将引发错误。如果你想声明一个只有一个值的元组,那么在它唯一的值后面加一个逗号,就像这样:
my_tuple = (0 """Or any other value""",)
要检查这个变量的值,使用类似这样的方法:
if my_tuple[0] == 0:
#Code goes here
如果您试图更改此值,将引发一个错误。
在Python中,不能将变量或值声明为常量。
为了让程序员知道变量是常量,通常用大写:
CONST_NAME = "Name"
要在常量发生变化时引发异常,请参阅Alex Martelli的《Python中的常量》。注意,这在实践中并不常用。
从Python 3.8开始,有一个类型。最后一个变量注释,它将告诉静态类型检查器(如myypy)您的变量不应该被重新分配。这是最接近于Java的final。然而,它实际上并不能阻止重新分配:
from typing import Final
a: Final[int] = 1
# Executes fine, but mypy will report an error if you run mypy on this:
a = 2
这并不完全是常数,但从python 3.7开始,你可以使用如下所示的数据类模块:
from dataclasses import dataclass
from typing import Final
@dataclass(frozen=True)
class A():
a1:Final = 3
a = A()
a.a1 = 4
---------------------------------------------------------------------------
FrozenInstanceError Traceback (most recent call last)
<ipython-input-14-5f7f4efc5bf0> in <module>
----> 1 a.a1 = 4
<string> in __setattr__(self, name, value)
FrozenInstanceError: cannot assign to field 'a1'