如何在Python中声明常量?
在Java中,我们做:
public static final String CONST_NAME = "Name";
如何在Python中声明常量?
在Java中,我们做:
public static final String CONST_NAME = "Name";
当前回答
扩展Raufio的答案,添加__repr__来返回值。
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)
def __repr__(self):
return ('{0}'.format(self.value))
dt = const(float(0.01))
print dt
那么对象的行为就更像你所期望的那样,你可以直接访问它而不是使用"。value "
其他回答
我正在尝试用不同的方法在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")
这里有一个技巧,如果你想要常量,而不关心它们的值:
只定义空类。
e.g:
class RED:
pass
class BLUE:
pass
PEP 591有“final”限定符。执行取决于类型检查器。
所以你可以这样做:
MY_CONSTANT: Final = 12407
注意:Final关键字仅适用于Python 3.8版本
你可以使用Tuple常量变量:
tuple是一个有序且不可更改的集合
my_tuple = (1, "Hello", 3.4)
print(my_tuple[0])
我知道这是一个老问题,但由于新的解决方案仍在添加,我想使可能的解决方案列表更加完整。你可以通过从类中继承属性来实现实例中的常量,如下所示:
class ConstantError(Exception):
pass # maybe give nice error message
class AllowConstants:
_constants = None
_class_constants = None
def __init__(self):
self._constants = {}
if self._class_constants is not None:
self._constants.update(self._class_constants)
def constant(self, name, value):
assert isinstance(name, str)
assert self._constants is not None, "AllowConstants was not initialized"
if name in self._constants or name in self.__dict__:
raise ConstantError(name)
self._constants[name] = value
def __getattr__(self, attr):
if attr in self._constants:
return self._constants[attr]
raise AttributeError(attr)
def __setattr__(self, attr, val):
if self._constants is None:
# not finished initialization
self.__dict__[attr] = val
else:
if attr in self._constants:
raise ConstantError(attr)
else:
self.__dict__[attr] = val
def __dir__(self):
return super().__dir__() + list(self._constants.keys())
子类化this时,你创建的常量将受到保护:
class Example(AllowConstants):
def __init__(self, a, b):
super().__init__()
self.constant("b", b)
self.a = a
def try_a(self, value):
self.a = value
def try_b(self, value):
self.b = value
def __str__(self):
return str({"a": self.a, "b": self.b})
def __repr__(self):
return self.__str__()
example = Example(1, 2)
print(example) # {'a': 1, 'b': 2}
example.try_a(5)
print(example) # {'a': 5, 'b': 2}
example.try_b(6) # ConstantError: b
example.a = 7
print(example) # {'a': 7, 'b': 2}
example.b = 8 # ConstantError: b
print(hasattr(example, "b")) # True
# To show that constants really do immediately become constant:
class AnotherExample(AllowConstants):
def __init__(self):
super().__init__()
self.constant("a", 2)
print(self.a)
self.a=3
AnotherExample() # 2 ConstantError: a
# finally, for class constants:
class YetAnotherExample(Example):
_class_constants = {
'BLA': 3
}
def __init__(self, a, b):
super().__init__(a,b)
def try_BLA(self, value):
self.BLA = value
ex3 = YetAnotherExample(10, 20)
ex3.BLA # 3
ex3.try_BLA(10) # ConstantError: BLA
ex3.BLA = 4 # ConstantError: BLA
常量是局部的(从AllowConstants继承的类的每个实例都有自己的常量),只要它们没有被重新赋值,就像普通的属性一样,并且编写从这个继承的类允许或多或少与支持常量的语言相同的风格。
此外,如果您想通过直接访问实例来防止任何人更改值。_constants,您可以使用其他答案中建议的许多不允许这样做的容器之一。最后,如果你真的觉得有必要,你可以阻止人们设置所有的实例。通过AllowConstants的更多属性访问,将_constants赋给一个新字典。(当然,这些都不是非常python化的,但这不是重点)。
编辑(因为使python非python化是一个有趣的游戏):为了使继承更容易一点,你可以修改AllowConstants如下:
class AllowConstants:
_constants = None
_class_constants = None
def __init__(self):
self._constants = {}
self._update_class_constants()
def __init_subclass__(cls):
"""
Without this, it is necessary to set _class_constants in any subclass of any class that has class constants
"""
if cls._class_constants is not None:
#prevent trouble where _class_constants is not overwritten
possible_cases = cls.__mro__[1:-1] #0 will have cls and -1 will have object
for case in possible_cases:
if cls._class_constants is case._class_constants:
cls._class_constants = None
break
def _update_class_constants(self):
"""
Help with the inheritance of class constants
"""
for superclass in self.__class__.__mro__:
if hasattr(superclass, "_class_constants"):
sccc = superclass._class_constants
if sccc is not None:
for key in sccc:
if key in self._constants:
raise ConstantError(key)
self._constants.update(sccc)
def constant(self, name, value):
assert isinstance(name, str)
assert self._constants is not None, "AllowConstants was not initialized"
if name in self._constants or name in self.__dict__:
raise ConstantError(name)
self._constants[name] = value
def __getattr__(self, attr):
if attr in self._constants:
return self._constants[attr]
raise AttributeError(attr)
def __setattr__(self, attr, val):
if self._constants is None:
# not finished initialization
self.__dict__[attr] = val
else:
if attr in self._constants:
raise ConstantError(attr)
else:
self.__dict__[attr] = val
def __dir__(self):
return super().__dir__() + list(self._constants.keys())
这样你就可以:
class Example(AllowConstants):
_class_constants = {
"BLA": 2
}
def __init__(self, a, b):
super().__init__()
self.constant("b", b)
self.a = a
def try_a(self, value):
self.a = value
def try_b(self, value):
self.b = value
def __str__(self):
return str({"a": self.a, "b": self.b})
def __repr__(self):
return self.__str__()
class ChildExample1(Example):
_class_constants = {
"BLI": 88
}
class ChildExample2(Example):
_class_constants = {
"BLA": 44
}
example = ChildExample1(2,3)
print(example.BLA) # 2
example.BLA = 8 # ConstantError BLA
print(example.BLI) # 88
example.BLI = 8 # ConstantError BLI
example = ChildExample2(2,3) # ConstantError BLA