在Python中如何声明常量?
在Java中,我们这样做:
public static final String CONST_NAME = "Name";
如果您需要常量但不关心其值,这里有一个技巧:
只需定义空类。
例如:
class RED:
pass
class BLUE:
pass
没有完美的方法来做到这一点。据我了解,大多数程序员只需将标识符大写,因此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属性方法的聪明解决方案 - 总有办法规避它们。这就是Python的特点。
我的建议是尽量避免麻烦,直接将标识符大写。虽然它不会真正成为一个合适的常量,但它也算得上是一个。
我正在尝试不同的方法在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']
兼容 python2
和 python3
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")
Python字典是可变的,所以它们似乎不是声明常量的好方法:
>>> constants = {"foo":1, "bar":2}
>>> print constants
{'foo': 1, 'bar': 2}
>>> constants["bar"] = 3
>>> print constants
{'foo': 1, 'bar': 3}
$ pip install pconst
from pconst import const
const.APPLE_PRICE = 100
const.APPLE_PRICE = 200
namedtuple
, but it is getting too long to be fit into a comment, so, here it goes.)换句话说,官方文档更倾向于使用实用的方式,而不是实际实现只读行为。我想这成为 Python之禅的又一个例子:enumerated constants can be implemented with named tuples, but it is simpler and more efficient to use a simple class declaration:
class Status: open, pending, closed = range(3)
简洁胜于复杂。
实用性胜过纯粹。
collections.namedtuple
和 itertools
来实现这个功能: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
val = 'Stackoverflow'
const_val = StringVar(val)
const.trace('w', reverse)
def reverse(*args):
const_val.set(val)
final
限定符来表示您打算将一个或多个名称设置为常量)。dict
,允许添加符合您选择的模式的项目(例如所有大写字母的名称)仅一次,并防止这些名称的值被更改。为此,您需要定义自己的方法,例如__setitem__
、__delitem__
等。这样的字典的代码(例如在此文件中找到的代码,超过250行)大约有100行。dict
无法修改。因此,在创建模块时,您需要首先执行特殊字典中的代码,然后使用其内容来更新模块的字典。__class__
替换为自定义的__setattr__
和__delattr__
方法重新定义的类。
property
函数/装饰器,可以实现将变量设置为只读。inv的答案是它的一个自定义用例示例。虽然property
函数更具有通用性,但关于它的工作原理的良好分析可在Shalabh Chaturvedi的Python Attributes and Methods网站上找到。 - n611x007True=False
,然后(2+2==4)==True
将返回False
。 - Sergey OrshanskiySyntaxError: can't assign to keyword
。这看起来是一件好事。 - naught101