如何在Python中声明常量?

在Java中,我们做:

public static final String CONST_NAME = "Name";

当前回答

您可以使用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中那样发生,因此它是不需要的,我猜。

你可以通过collections.namedtuple和itertools来实现:

import collections
import itertools
def Constants(Name, *Args, **Kwargs):
  t = collections.namedtuple(Name, itertools.chain(Args, Kwargs.keys()))
  return t(*itertools.chain(Args, Kwargs.values()))

>>> myConstants = Constants('MyConstants', 'One', 'Two', Three = 'Four')
>>> print myConstants.One
One
>>> print myConstants.Two
Two
>>> print myConstants.Three
Four
>>> myConstants.One = 'Two'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: can't set attribute

Python字典是可变的,所以它们似乎不是声明常量的好方法:

>>> constants = {"foo":1, "bar":2}
>>> print constants
{'foo': 1, 'bar': 2}
>>> constants["bar"] = 3
>>> print constants
{'foo': 1, 'bar': 3}

在Python中,常量是不存在的,但是你可以通过在变量名的开头添加CONST_并在注释中声明它是一个常量来表明变量是一个常量并且不能被改变:

myVariable = 0
CONST_daysInWeek = 7    # This is a constant - do not change its value.   
CONSTANT_daysInMonth = 30 # This is also a constant - do not change this value.

或者,你可以创建一个像常量一样的函数:

def CONST_daysInWeek():
    return 7;

我正在尝试用不同的方法在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")