如何在Python中声明常量?
在Java中,我们做:
public static final String CONST_NAME = "Name";
如何在Python中声明常量?
在Java中,我们做:
public static final String CONST_NAME = "Name";
当前回答
下面是一个“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
感谢@MikeGraham的FrozenDict,我将其作为一个起点。更改后,使用语法不再是Nums['ONE'],而是Nums.ONE。
感谢@Raufio的回答,对于覆盖__ setattr __的想法。
或者要了解更多功能的实现,请参阅@Hans_meine的实现 named_constants在GitHub
其他回答
没有完美的方法可以做到这一点。据我所知,大多数程序员只是将标识符大写,所以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就是这样的。
我的建议是避免所有的麻烦,将标识符大写。它不是一个合适的常数,但也没有什么合适的常数。
我们可以创建一个描述符对象。
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
(这一段本来是对那些提到namedtuple的答案的注释,但它太长了,不适合注释,所以,就这样吧。)
上面提到的命名元组方法绝对是创新的。不过,为了完整起见,在其官方文档的NamedTuple部分的末尾,它如下所示:
枚举常量可以用命名元组实现,但使用简单的类声明更简单高效: 类的状态: 打开,待处理,关闭= range(3)
换句话说,官方文档更倾向于使用一种实用的方式,而不是实际实现只读行为。我想这是Python的另一个禅宗的例子:
简单比复杂好。 实用胜过纯粹。
我最近发现了一个非常简洁的更新,它会自动引发有意义的错误消息,并阻止通过__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 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
或者,如果你使用的是python3:
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
现在,直接或通过实例访问的props应该是常量:
MyConst.A
# 1
my_const = MyConst()
my_const.A
# 1
MyConst.A = 'changed'
# TypeError
my_const.A = 'changed'
# TypeError
下面是上面的一个例子。下面是Python 3的另一个例子。