在Python中如何声明常量?
在Java中,我们这样做:
public static final String CONST_NAME = "Name";
在Python中,您无法将变量或值声明为常量。
为了向程序员表明一个变量是常量,通常会将其用大写字母表示:
CONST_NAME = "Name"
typing.Final
变量注释,可以告诉静态类型检查器(如mypy),你的变量不应重新赋值。这是与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
mypy
未对:Final
的重新赋值给出任何注释。我需要进行任何配置设置吗? - alper在其他语言中没有像常量一样的const
关键字,但是可以创建一个具有“getter函数”用于读取数据,但是没有“setter函数”用于重新写入数据的属性。这实际上保护了标识符不被更改。
以下是使用类属性的替代实现:
请注意,对于想要了解常量的读者来说,代码远非易懂。请查看下面的说明。
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(hex(CONST.FOO)) # -> '0xbaadfaceL'
CONST.FOO = 0
##Traceback (most recent call last):
## File "example1.py", line 22, in <module>
## CONST.FOO = 0
## File "example1.py", line 5, in fset
## raise TypeError
##TypeError
代码解释:
constant
,接受一个表达式,并使用它构建一个“getter”——一个仅返回该表达式值的函数。TypeError
错误,因此只读constant
函数作为装饰器,快速定义只读属性。还有一种更老式的方式:
(这段代码相当棘手,请参见下文的更多解释)
class _Const(object):
def FOO():
def fset(self, value):
raise TypeError
def fget(self):
return 0xBAADFACE
return property(**locals())
FOO = FOO() # Define property.
CONST = _Const()
print(hex(CONST.FOO)) # -> '0xbaadfaceL'
CONST.FOO = 0
##Traceback (most recent call last):
## File "example2.py", line 16, in <module>
## CONST.FOO = 0
## File "example2.py", line 6, in fset
## raise TypeError
##TypeError
property
函数构造一个对象,该对象可以进行“设置”或“获取”操作。property
函数的前两个参数分别命名为fset
和fget
。property
函数。uuuid.uuid4().hex
,该怎么办?这个属性将防止uuid函数改变,但是值会改变。 - Jurakinnamedtuple
不是一种更简单的实现吗? - Gerhardnamedtuple
不是一个更简单的实现吗? - undefinedERRORS = _Errors()
等等。 - undefined__method
来表示私有方法,使用_method
来表示受保护的方法。MY_CONSTANT = "one"
def MY_CONSTANT():
return "one"
唯一的问题是您需要在每个地方都执行MY_CONSTANT()
,但是在Python中MY_CONSTANT =“one”
通常是正确的方式。
您还可以使用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
def MY_CONSTANT(): return "one"
并不能防止方法引用被重新赋值,对吗?这难道不正是鸭子类型的工作原理吗? - Josh Gustconstants.pi = 3
失败了,但是 constants = Constants(3, 2)
没有。 - Maxim Egorushkin最近我发现了一种非常简洁的更新方式,它会自动触发有意义的错误消息,并防止通过__dict__
访问:
我最近发现了一种非常简洁的更新方法,可以自动生成有意义的错误信息并防止通过__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
对象上。拥有一个上层类名似乎也有点不好看,但总体上我认为它相当简洁。
Python 没有常量。
或许最简单的替代方法是为其定义一个函数:
def MY_CONSTANT():
return 42
MY_CONSTANT()
现在具有常量的所有功能(加上一些恼人的括号)。
constexpr
)也是真正的硬常量,因此您仍然无法受到保护。 - Ruslan属性是创建常量的一种方式。您可以通过声明getter属性,但忽略setter来实现这一点。例如:
class MyFinalProperty(object):
@property
def name(self):
return "John"
你可以查看我写的一篇文章,以找到更多使用Python属性的方法。
除了两个最佳答案(只需使用大写名称的变量或使用属性使值为只读),我想提到还可以使用元类来实现命名常量。我在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,但仍然非常易于使用和方便。 (该模块具有一些其他功能,包括常量为只读,请参阅其README。)
在各种存储库中都有类似的解决方案,但据我所知,它们要么缺少我从常量中期望的基本功能之一(例如是常量或任意类型),要么添加了奇特的功能,使它们的适用性不如此通用。但你的情况可能不同,我会感激您的反馈。 :-)
编辑:已添加Python 3的示例代码
注意:这个答案看起来提供了一个更完整的实现,类似于以下内容(具有更多特性)。
首先,制作一个元类:
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
Final
类型是通用的,如果您已经进行类型检查以强制执行此操作,则可能会对示例中的未输入使用感到不满。我认为这种用法的正确示例应该是:
MY_CONSTANT: Final[int] = 12407
。 - Dave Birch我使用冻结数据类声明常量值,像这样:
from dataclasses import dataclass
@dataclass(frozen=True)
class _Const:
SOME_STRING = 'some_string'
SOME_INT = 5
Const = _Const()
# In another file import Const and try
print(Const.SOME_STRING) # ITS OK!
Const.SOME_INT = 6 # dataclasses.FrozenInstanceError: cannot assign to field 'SOME_INT'
property
函数/装饰器,可以实现将变量设置为只读。inv的答案是它的一个自定义用例示例。虽然property
函数更具有通用性,但关于它的工作原理的良好分析可在Shalabh Chaturvedi的Python Attributes and Methods网站上找到。 - n611x007True=False
,然后(2+2==4)==True
将返回False
。 - Sergey OrshanskiySyntaxError: can't assign to keyword
。这看起来是一件好事。 - naught101