如何在Python中声明常量?
在Java中,我们做:
public static final String CONST_NAME = "Name";
如何在Python中声明常量?
在Java中,我们做:
public static final String CONST_NAME = "Name";
当前回答
你可以简单地:
STRING_CONSTANT = "hi"
NUMBER_CONSTANT = 89
希望这能让一切变得简单
其他回答
属性是创建常量的一种方法。你可以通过声明一个getter属性来做到这一点,但忽略setter。例如:
class MyFinalProperty(object):
@property
def name(self):
return "John"
您可以看看我写的一篇文章,以找到更多使用Python属性的方法。
扩展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 "
您可以使用namedtuple作为一种变通方法来有效地创建一个常量,其工作方式与Java中的静态final变量(Java“常量”)相同。作为一种变通方法,它是优雅的。(更优雅的方法是简单地改进Python语言——什么样的语言可以让您重新定义math.pi?——但我跑题了。)
(当我写这篇文章时,我意识到这个问题的另一个答案提到了namedtuple,但我将在这里继续,因为我将展示一种更接近于您在Java中所期望的语法,因为不需要像namedtuple那样创建命名类型。)
跟着你的例子,你会记得在Java中,我们必须在某个类中定义常量;因为你没有提到类名,我们称它为Foo。下面是Java类:
public class Foo {
public static final String CONST_NAME = "Name";
}
这里是等效的Python。
from collections import namedtuple
Foo = namedtuple('_Foo', 'CONST_NAME')('Name')
我想在这里补充的关键点是,您不需要单独的Foo类型(“匿名命名元组”会很好,尽管这听起来有点矛盾),所以我们将命名元组命名为_Foo,这样它就不会转义到导入模块中。
这里的第二点是,我们立即创建了nametuple的一个实例,称其为Foo;没有必要在单独的步骤中执行此操作(除非您想这样做)。现在你可以做你在Java中可以做的事情:
>>> Foo.CONST_NAME
'Name'
但你不能给它赋值:
>>> Foo.CONST_NAME = 'bar'
…
AttributeError: can't set attribute
确认:我认为我发明了命名元组方法,但后来我看到其他人给出了类似的答案(尽管不那么紧凑)。然后我还注意到Python中的“命名元组”是什么?,这就指出了sys。version_info现在是一个命名元组,所以可能Python标准库早就提出了这个想法。
注意,不幸的是(这仍然是Python),你可以完全删除整个Foo赋值:
>>> Foo = 'bar'
(facepalm)
但至少我们阻止了福星。CONST_NAME值,这比什么都没有好。祝你好运。
注意:这是一个糟糕的想法和糟糕的实现。此外,它只适用于最后的小例子,一个完整的实现将意味着大量的工作,这是我太懒了。而且,在Python 3.8之前,审计钩子可能是不可用的。
我基本上回答了另一个问题,结果和这个问题有关。它的思想是,你可以利用审计钩子来捕捉每一行的执行,解析代码对象,如果它满足某些条件(例如某个前缀并且已经定义过一次),你可以抛出一个错误。
你可能不得不支持其他赋值类型(例如,对于导入的东西,可能对于函数内部的局部变量,解包等),不使用全局变量,因为字典可以很容易地修改,实际上调查这是否安全,接受这个实现将对你的整个应用程序造成的性能损失,确保它在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'
如果你以这种方式结束,你不应该,除了修复和泛化代码,你可能会想要找到一种方法,只让一些东西不变,例如,只有那些有特殊前缀的对象或只有对象有一些注释。
您可以在下一个类的帮助下模拟常量变量。用法示例:
# 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)