如何在Python中声明常量?

在Java中,我们做:

public static final String CONST_NAME = "Name";

当前回答

除了上面的两个答案(只使用大写的变量名,或者使用属性使值为只读),我想提到的是,可以使用元类来实现命名常量。我在GitHub提供了一个使用元类的非常简单的解决方案,如果你想让值的类型/名称更有信息,这可能会很有帮助:

>>> from named_constants import Constants
>>> class Colors(Constants):
...     black = 0
...     red = 1
...     white = 15
...
>>> c = Colors.black
>>> c == 0
True
>>> c
Colors.black
>>> c.name()
'black'
>>> Colors(0) is c
True

这是稍微高级一些的Python,但仍然非常容易使用和方便。(该模块有更多的特性,包括常量是只读的,请参阅它的README。)

在各种存储库中也有类似的解决方案,但据我所知,它们要么缺乏我对常量所期望的基本特性之一(比如是常量,或者是任意类型),要么添加了一些深奥的特性,使它们不那么普遍适用。但是YMMV,我很感激你的反馈。: -)

其他回答

python声明“常量”的方式基本上是一个模块级别的变量:

RED = 1
GREEN = 2
BLUE = 3

然后编写类或函数。因为常量几乎都是整数,而且它们在Python中也是不可变的,所以你几乎没有机会改变它。

当然,除非显式地设置RED = 2。

在我的例子中,我需要不可变字节数组来实现包含许多文字数字的加密库,我想确保这些数字是常量。

这个答案是有效的,但是尝试重赋bytearray元素不会引发错误。

def const(func):
    '''implement const decorator'''
    def fset(self, val):
        '''attempting to set a const raises `ConstError`'''
        class ConstError(TypeError):
            '''special exception for const reassignment'''
            pass

        raise ConstError

    def fget(self):
        '''get a const'''
        return func()

    return property(fget, fset)


class Consts(object):
    '''contain all constants'''

    @const
    def C1():
        '''reassignment to C1 fails silently'''
        return bytearray.fromhex('deadbeef')

    @const
    def pi():
        '''is immutable'''
        return 3.141592653589793

常量是不可变的,但是常量bytearray赋值默默失败:

>>> c = Consts()
>>> c.pi = 6.283185307179586  # (https://en.wikipedia.org/wiki/Tau_(2%CF%80))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "consts.py", line 9, in fset
    raise ConstError
__main__.ConstError
>>> c.C1[0] = 0
>>> c.C1[0]
222
>>> c.C1
bytearray(b'\xde\xad\xbe\xef')

一种更强大、更简单,甚至可能更“python化”的方法涉及使用memoryview对象(<= python-2.6中的缓冲区对象)。

import sys

PY_VER = sys.version.split()[0].split('.')

if int(PY_VER[0]) == 2:
    if int(PY_VER[1]) < 6:
        raise NotImplementedError
    elif int(PY_VER[1]) == 6:
        memoryview = buffer

class ConstArray(object):
    '''represent a constant bytearray'''
    def __init__(self, init):
        '''
        create a hidden bytearray and expose a memoryview of that bytearray for
        read-only use
        '''
        if int(PY_VER[1]) == 6:
            self.__array = bytearray(init.decode('hex'))
        else:
            self.__array = bytearray.fromhex(init)

        self.array = memoryview(self.__array)

    def __str__(self):
        return str(self.__array)

    def __getitem__(self, *args, **kwargs):
       return self.array.__getitem__(*args, **kwargs)

ConstArray项赋值是一个TypeError:

>>> C1 = ConstArray('deadbeef')
>>> C1[0] = 0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'ConstArray' object does not support item assignment
>>> C1[0]
222

这并不完全是常数,但从python 3.7开始,你可以使用如下所示的数据类模块:

from dataclasses import dataclass
from typing import Final

@dataclass(frozen=True)
class A():
    a1:Final = 3

a = A()

a.a1 = 4

---------------------------------------------------------------------------
FrozenInstanceError                       Traceback (most recent call last)
<ipython-input-14-5f7f4efc5bf0> in <module>
----> 1 a.a1 = 4

<string> in __setattr__(self, name, value)

FrozenInstanceError: cannot assign to field 'a1'

您可以在下一个类的帮助下模拟常量变量。用法示例:

# 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)

我为python const写了一个util lib: Kkconst - pypi 支持str, int, float, datetime

const字段实例将保持其基类型行为。

例如:

from __future__ import print_function
from kkconst import (
    BaseConst,
    ConstFloatField,
)

class MathConst(BaseConst):
    PI = ConstFloatField(3.1415926, verbose_name=u"Pi")
    E = ConstFloatField(2.7182818284, verbose_name=u"mathematical constant")  # Euler's number"
    GOLDEN_RATIO = ConstFloatField(0.6180339887, verbose_name=u"Golden Ratio")

magic_num = MathConst.GOLDEN_RATIO
assert isinstance(magic_num, ConstFloatField)
assert isinstance(magic_num, float)

print(magic_num)  # 0.6180339887
print(magic_num.verbose_name)  # Golden Ratio

更多详细用法,你可以阅读pypi url: Pypi或github