如何在Python中声明常量?
在Java中,我们做:
public static final String CONST_NAME = "Name";
如何在Python中声明常量?
在Java中,我们做:
public static final String CONST_NAME = "Name";
当前回答
Python字典是可变的,所以它们似乎不是声明常量的好方法:
>>> constants = {"foo":1, "bar":2}
>>> print constants
{'foo': 1, 'bar': 2}
>>> constants["bar"] = 3
>>> print constants
{'foo': 1, 'bar': 3}
其他回答
您可以使用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值,这比什么都没有好。祝你好运。
嗯. .尽管这是过时的,让我在这里补充我的2分:-)
class ConstDict(dict):
def __init__(self, *args, **kwargs):
super(ConstDict, self).__init__(*args, **kwargs)
def __setitem__(self, key, value):
if key in self:
raise ValueError("Value %s already exists" % (key))
super(ConstDict, self).__setitem__(key, value)
您可以防止在那里发生任何更新,而不是破坏ValueError。这样做的一个好处是,您可以在程序中动态地添加常数,但一旦设置了常数就不能更改。你也可以在设置常量之前添加任何规则(比如key必须是字符串或小写字符串或大写字符串等)
然而,我不认为在Python中设置常量有任何重要性。没有优化可以像在C中那样发生,因此它是不需要的,我猜。
我忍不住要提供我自己的极简元类实现(这可能是前面元类答案的变体)。
常量存储在容器类中(不需要实例化)。值只能设置一次,但设置后不能更改(或删除)。
就我个人而言,我目前还没有这个用例,但这是一个有趣的练习。
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
请随意提出改进建议。
我们可以创建一个描述符对象。
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
Python字典是可变的,所以它们似乎不是声明常量的好方法:
>>> constants = {"foo":1, "bar":2}
>>> print constants
{'foo': 1, 'bar': 2}
>>> constants["bar"] = 3
>>> print constants
{'foo': 1, 'bar': 3}