在Python中如何声明常量?
在Java中,我们这样做:
public static final String CONST_NAME = "Name";
注意:这是一个糟糕的想法和实现。而且它只适用于最后的小例子,完整的实现意味着需要大量的工作,而我太懒了。此外,审计钩子可能在 Python 3.8 之前不可用。
我主要回答了另一个问题,结果发现它与这个问题有关。这个想法是利用审计钩子捕获每行代码的执行,解析代码对象,如果满足某些条件(例如特定前缀已定义一次),则可以抛出错误。
您可能需要支持其他赋值类型(例如导入的内容、函数内部的本地变量、解包等),不使用globals
,因为该字典可以轻松修改,实际上调查是否安全,接受此实现将对整个应用程序产生的性能损失,确保此功能在 REPL 外部、ipython 内部等处正常工作。无论如何,我们开始吧:
>>> import sys
>>> import ast
>>> import dis
>>> import types
>>>
>>>
>>> def hook(name, tup):
... if name == "exec" and tup:
... if tup and isinstance(tup[0], types.CodeType):
... code = tup[0]
... store_instruction_arg = None
... instructions = [dis.opname[op] for op in code.co_code]
...
... for i, instruction in enumerate(instructions):
... if instruction == "STORE_NAME":
... store_instruction_arg = code.co_code[i + 1]
... break
...
... if store_instruction_arg is not None:
... var_name = code.co_names[store_instruction_arg]
... if var_name in globals():
... raise Exception("Cannot re-assign variable")
...
>>>
>>> sys.addaudithook(hook)
>>>
>>> a = '123'
>>> a = 456
Traceback (most recent call last):
File "<stdin>", line 16, in hook
Exception: Cannot re-assign variable
>>>
>>> a
'123'
不禁提供了我自己的非常轻量级极简元类
实现(可能与先前的元类答案有所不同)。
常量存储在容器类中(无需实例化)。 值可以设置一次,但一旦设置就无法更改(或删除)。
个人目前没有用例,但这是一个有趣的练习。
class MetaConstant(type):
''' Metaclass that allows underlying class to store constants at class-level (subclass instance not needed).
Non-existent attributes of underlying class (constants) can be set initially, but cannot be changed or deleted.
'''
def __setattr__(klass, attr, value):
'If attribute (constant) doesn\'t exist, set value. If attribute exists, raise AttributeError.'
if hasattr(klass, attr):
raise AttributeError(f'Can\'t change the value of the constant {klass.__name__}.{attr} to {value}'
f' (the value of {klass.__name__}.{attr} is already set to'
f' {getattr(klass, attr)}).')
super().__setattr__(attr, value)
def __delattr__(klass, attr):
if hasattr(klass, attr):
raise AttributeError(f'Can\'t delete constant {klass.__name__}.{attr}'
f' (set to {getattr(klass, attr)}).')
class Constants(metaclass=MetaConstant):
'Container class for constants. No instantiation required.'
#pass # uncomment if no constants set upon class creation
B = 'Six' # sets Constants.B to 'Six'
Constants.B = 6 # AttributeError
del Constants.B # AttributeError
Constants.A = 'Five' # sets Constants.A to 'Five'
Constants.A = 5 # AttributeError
del Constants.A # AttributeError
欢迎提出改进意见。
# Const
const = Const().add(two=2, three=3)
print 'const.two: ', const.two
print 'const.three: ', const.three
const.add(four=4)
print 'const.four: ', const.four
#const.four = 5 # a error here: four is a constant
const.add(six=6)
print 'const.six: ', const.six
const2 = Const().add(five=5) # creating a new namespace with Const()
print 'const2.five: ', const2.five
#print 'const2.four: ', const2.four # a error here: four does not exist in const2 namespace
const2.add(five=26)
当您想要启动新的常量命名空间时,请调用构造函数。请注意,当Martelli的const类不存在时,该类受到意外修改序列类型常量的保护。
以下是源代码。
from copy import copy
class Const(object):
"A class to create objects with constant fields."
def __init__(self):
object.__setattr__(self, '_names', [])
def add(self, **nameVals):
for name, val in nameVals.iteritems():
if hasattr(self, name):
raise ConstError('A field with a name \'%s\' is already exist in Const class.' % name)
setattr(self, name, copy(val)) # set up getter
self._names.append(name)
return self
def __setattr__(self, name, val):
if name in self._names:
raise ConstError('You cannot change a value of a stored constant.')
object.__setattr__(self, name, val)
__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'。
dt = 5
可以被接受而没有任何投诉。在Raufio的答案中,虽然也可以覆盖它,但结果会导致下一次使用 dt.value
时出现投诉。因此,这是一种较少危险的失败。你已经抵消了他解决方案的好处。 - ToolmakerSteve
property
函数/装饰器,可以实现将变量设置为只读。inv的答案是它的一个自定义用例示例。虽然property
函数更具有通用性,但关于它的工作原理的良好分析可在Shalabh Chaturvedi的Python Attributes and Methods网站上找到。 - n611x007True=False
,然后(2+2==4)==True
将返回False
。 - Sergey OrshanskiySyntaxError: can't assign to keyword
。这看起来是一件好事。 - naught101