如何在Python中创建常量?

zfr*_*cus 903 python constants

有没有办法在Python中声明一个常量?在Java中,我们可以用这种方式创建常量值:

public static final String CONST_NAME = "Name";
Run Code Online (Sandbox Code Playgroud)

Python中上述Java常量声明的等价物是什么?

Fel*_*ing 887

不,那里没有.您不能在Python中将变量或值声明为常量.只是不要改变它.

如果你在一个班级,相当于:

class Foo(object):
    CONST_NAME = "Name"
Run Code Online (Sandbox Code Playgroud)

如果没有,那就是

CONST_NAME = "Name"
Run Code Online (Sandbox Code Playgroud)

但是你可能想看看Alex Martelli的Python代码片段常量.

  • 给初学者的建议:了解为什么不变性是一种代码质量机制。对于认为 Python 中缺少常量没有问题的高级程序员,也可以这样做。 (98认同)
  • **只是不要改变它.**你做了我的一天 (79认同)
  • "只是不要改变它"根本没用.它没有回答这个问题,我建议将其删除. (68认同)
  • 而不是做"Python中的常量",你应该使用"属性"函数或装饰器. (24认同)
  • 人们在Perl中询问相同的功能.有一个名为"use constant"的导入模块,但是(AFAIK)它只是一个包装器来创建一个返回值的微小函数.我在Python中也这样做.示例:`def MY_CONST_VALUE():返回123` (23认同)
  • “只是不要改变它。” 真的吗?只是讽刺,不是吗? (9认同)
  • "不,那里没有." 没错,但是建立在其他人的工作基础之上,我已经添加了一个答案,远远低于它,为python 2.7(缺少"枚举")提供了一个简短的"常量"实现.这些是枚举式的只读`name.attribute`,可以包含任何值.声明很简单`Nums = Constants(ONE = 1,PI = 3.14159,DefaultWidth = 100.0)`,用法很简单`print 10 + Nums.PI`,尝试更改异常中的结果`Nums.PI = 22` => ValueError (..). (8认同)
  • 我不得不在答复中查看日期不是4月1日.那么,声明一个变量并"只是不要改变它"?大声笑.像许多语言一样,Python没有注意到所有最好的函数式编程语言(Python不是)的最基本属性:不变性.在不可变数据的规模和可靠性方面的重要性不容小觑. (5认同)
  • 这是一个很好的答案,这可能是许多/大多数情况下的惯用/ pythonic答案.字符串文字是"常量",因为它们是[immutable](https://docs.python.org/2/glossary.html#term-immutable).但是变量名称,通过对该字符串对象的引用在技术上是一个绑定,是*不*.作为接受略有误导.[property](https://docs.python.org/2/library/functions.html#property)函数是一种提供对某些名称绑定(即变量名)的只读访问的方法.Meta:我不知何故希望我可以将*accept*本身投票,让我赞成*答案*. (3认同)
  • @Felix Kling你好.我在'常量在Python'中尝试了这个例子,但它似乎对我有效...我运行时遇到错误:`Traceback(最近调用最后一次):文件"E:\ Workspaces\Python\Const_in_python\test .py",第4行,在<module> const.magic = 23文件"E:\ Workspaces\Python\Const_in_python\const.py",第5行,在__setattr__中,如果self .__ dict __.has_key(name):AttributeError:' dict'对象没有属性'has_key'.我确信我没有做错任何事. (2认同)
  • @BartekBanachewicz,但这不​​是答案。答案是“ **没有没有**。”剩下的只是建议或其他信息。 (2认同)
  • @ user37222 尝试阅读任何可以改变的代码。意图很重要,表达意图的代码越多越好。 (2认同)

inv*_*inv 338

const在其他语言中没有关键字,但是可以创建一个具有"getter函数"来读取数据的Property ,但是没有"setter函数"来重写数据.这实质上保护标识符不被更改.

以下是使用class属性的替代实现:

请注意,对于想知道常量的读者而言,代码并不容易.见下面的解释

def constant(f):
    def fset(self, value):
        raise TypeError
    def fget(self):
        return f()
    return property(fget, fset)

class _Const(object):
    @constant
    def FOO():
        return 0xBAADFACE
    @constant
    def BAR():
        return 0xDEADBEEF

CONST = _Const()

print CONST.FOO
##3131964110

CONST.FOO = 0
##Traceback (most recent call last):
##    ...
##    CONST.FOO = 0
##TypeError: None
Run Code Online (Sandbox Code Playgroud)

代码说明:

  1. 定义一个constant接受表达式的函数,并使用它来构造一个"getter" - 一个只返回表达式值的函数.
  2. setter函数引发TypeError,因此它是只读的
  3. 使用constant我们刚刚创建的函数作为装饰来快速定义只读属性.

而在其他一些更老式的方式:

(代码非常棘手,下面有更多解释)

class _Const(object):
    @apply
    def FOO():
        def fset(self, value):
            raise TypeError
        def fget(self):
            return 0xBAADFACE
        return property(**locals())

CONST = _Const()

print CONST.FOO
##3131964110

CONST.FOO = 0
##Traceback (most recent call last):
##    ...
##    CONST.FOO = 0
##TypeError: None
Run Code Online (Sandbox Code Playgroud)

请注意,@ apply装饰器似乎已被弃用.

  1. 要定义标识符FOO,firs定义两个函数(fset,fget - 名称由我选择).
  2. 然后使用内置property函数构造一个可以"设置"或"获取"的对象.
  3. 注意,property函数的前两个参数是命名的fsetfget.
  4. 使用我们为我们自己的getter和setter选择这些名称的事实,并使用应用于该作用域的所有本地定义的**(双星号)创建关键字字典,以将参数传递给property函数

  • 基于`AttributeError`和`TypeError`的文档,我认为引发的异常应该是一个新的错误,我建议命名`ConstantError`或类似的东西,它是`TypeError`的子类.文档中让我想到的部分:https://docs.python.org/2/library/exceptions.html (10认同)
  • @OscarSmith,我认为它会改进'自编代码'设计.当我明确地将代码明确表示某些值无法改变时,比阅读所有源代码并意识到某些值永远不会改变更容易理解.此外,它阻止了某人更改应该是,不变的值的可能性.记住:显式优于隐式. (10认同)
  • 这些长度确实勾勒出了python语言的明显缺陷.为什么他们不觉得有必要将它添加到Python 3.我不敢相信没有人建议它,我根本看不到一些委员会背后的逻辑'不行,常量?罗". (9认同)
  • 并且你的解决方案仍然可以由一个确定的python程序员通过使用`CONST .__ dict __ ['FOO'] = 7来修改. (7认同)
  • 我对此代码感到惊讶.为什么FOO()和BAR()方法字体有自己作为参数?我的IDE强调红色括号("编译"错误).我厌倦了把自己放进去,但后来我得到了一个错误. (3认同)

Anu*_*yal 103

在Python中,而不是语言强制执行某些东西,人们使用命名约定,例如__method私有方法和_method用于受保护的方法.

因此,以同样的方式,您可以简单地将常量声明为全部大写,例如

MY_CONSTANT = "one"
Run Code Online (Sandbox Code Playgroud)

如果你想要这个常量永远不会改变,你可以挂钩属性访问并做一些技巧,但更简单的方法是声明一个函数

def MY_CONSTANT():
    return "one"
Run Code Online (Sandbox Code Playgroud)

只有问题无处不在,你必须做MY_CONSTANT(),但MY_CONSTANT = "one"在python(通常)中再次是正确的方法.

您还可以使用namedtuple来创建常量:

>>> from collections import namedtuple
>>> Constants = namedtuple('Constants', ['pi', 'e'])
>>> constants = Constants(3.14, 2.718)
>>> constants.pi
3.14
>>> constants.pi = 3
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: can't set attribute
Run Code Online (Sandbox Code Playgroud)

  • 做`def MY_CONSTANT():返回"一个"`不会阻止某人,稍后在代码中执行`MY_CONSTANT ="two"`(或重新声明该函数). (17认同)
  • 使用 def MY_CONSTANT(): return "one" 不会阻止方法引用被重新分配,对吗?这不正是鸭子打字的工作原理吗? (7认同)
  • @MatthewSchinckel它是关于约定的,也改变MY_CONSTANT不会改变用法MY_CONSTANT()但会抛出错误,而在python中如果你想要你可以改变任何东西,没有聪明的技巧可以保护你. (6认同)
  • 感谢您提出了namedtuple方法.绝对创新.您可能还会发现我的["评论"在此处](/sf/answers/3653636521/)相关. (2认同)
  • `constants.pi = 3` 失败,但 `constants = Constants(3, 2)` 则不会。 (2认同)

Jon*_*tts 57

我可能在这里错过了一个技巧,但这似乎对我有用:

class CONST(object):
    __slots__ = ()
    FOO = 1234

CONST = CONST()

# ----------

print(CONST.FOO)    # 1234

CONST.FOO = 4321              # AttributeError: 'CONST' object attribute 'FOO' is read-only
CONST.__dict__['FOO'] = 4321  # AttributeError: 'CONST' object has no attribute '__dict__'
CONST.BAR = 5678              # AttributeError: 'CONST' object has no attribute 'BAR'
Run Code Online (Sandbox Code Playgroud)

创建实例允许魔术__dict__方法启动并拦截设置__dict__变量的尝试.如果你愿意,可以在这里抛出异常.通过类名实例化实例可以防止直接通过类进行访问.

这对于一个值来说是一种痛苦,但是你可以将很多东西附加到你的__setattr__对象上.上流社会,班级名称似乎也有点粗鲁,但我认为整体而言非常简洁.

  • 这是最好和最明确的答案,因为它具有最少的"机制",但功能最多.提出异常很重要,但不是一种选择. (11认同)

Sae*_*aig 29

你可能已经知道,Python没有常量:(

也许最简单的选择是为它定义一个函数.例如

def MY_CONSTANT():
    return 42
Run Code Online (Sandbox Code Playgroud)

MY_CONSTANT() 现在具有常量的所有功能(加上一些恼人的大括号).

  • 我只是想添加这个建议,但幸运的是我向下滚动到评分较低的答案。我希望它会得到进一步的支持,我完全同意它具有常量的所有功能,并且非常简单明了。查看所有复杂解决方案中样板代码的数量,我发现大括号相对来说并不烦人。 (2认同)
  • 你可以在 python 中重新声明函数,所以这实际上并不能解决任何问题 (2认同)

han*_*ine 19

除了两个最佳答案(只使用带有大写名称的变量,或使用属性使值只读),我想提一下,为了实现命名常量,可以使用元类.我在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
Run Code Online (Sandbox Code Playgroud)

这是稍微高级的Python,但仍然非常易于使用和方便.(该模块具有更多功能,包括常量只读,请参阅其自述文件.)

在各种存储库中都有类似的解决方案,但据我所知,它们要么缺少我期望从常量中获得的基本特征之一(如常量,或者是任意类型),要么它们具有深奥的功能.使它们不太普遍适用.但YMMV,我很感激反馈.:-)

  • 我喜欢你在GitHub上的实现.我几乎准备好编写一个实现反向查找功能的基本类,但我看到你已经完成了这个以及更多! (3认同)

dou*_*rve 17

编辑:添加了Python 3的示例代码

注意:这个其他答案看起来像是提供了一个更完整的实现,类似于以下(具有更多功能).

首先,制作一个元类:

class MetaConst(type):
    def __getattr__(cls, key):
        return cls[key]

    def __setattr__(cls, key, value):
        raise TypeError
Run Code Online (Sandbox Code Playgroud)

这可以防止更改静态属性.然后创建另一个使用该元类的类:

class Const(object):
    __metaclass__ = MetaConst

    def __getattr__(self, name):
        return self[name]

    def __setattr__(self, name, value):
        raise TypeError
Run Code Online (Sandbox Code Playgroud)

或者,如果您使用的是Python 3:

class Const(object, metaclass=MetaConst):
    def __getattr__(self, name):
        return self[name]

    def __setattr__(self, name, value):
        raise TypeError
Run Code Online (Sandbox Code Playgroud)

这应该可以防止实例道具被更改.要使用它,继承:

class MyConst(Const):
    A = 1
    B = 2
Run Code Online (Sandbox Code Playgroud)

现在直接或通过实例访问的道具应该是不变的:

MyConst.A
# 1
my_const = MyConst()
my_const.A
# 1

MyConst.A = 'changed'
# TypeError
my_const.A = 'changed'
# TypeError
Run Code Online (Sandbox Code Playgroud)

是上面一个例子.这是 Python 3 另一个例子.


小智 16

属性是创建常量的一种方法.您可以通过声明getter属性来执行此操作,但忽略setter.例如:

class MyFinalProperty(object):

    @property
    def name(self):
        return "John"
Run Code Online (Sandbox Code Playgroud)

您可以查看我编写的文章,以找到使用Python属性的更多方法.


小智 15

PEP 591具有“最终”限定符。强制执行取决于类型检查器。

所以你可以这样做:

MY_CONSTANT: Final = 12407
Run Code Online (Sandbox Code Playgroud)

注意: Final关键字仅适用于 Python 3.8 版本

  • “Final”类型是通用的,如果您已经进行类型检查来强制执行此操作,他们可能会对示例中的非类型化用法感到不安。我相信这种用法的正确示例应该是:`MY_CONSTANT: Final[int] = 12407` (2认同)

jas*_*uuu 15

我使用冻结数据类声明常量值,如下所示:

from dataclasses import dataclass

@dataclass(frozen=True)
class _Const:
    SOME_STRING = 'some_string'
    SOME_INT = 5
    
Const = _Const()

# In another file import Const and try
print(Const.SOME_STRING)  # ITS OK!
Const.SOME_INT = 6  # dataclasses.FrozenInstanceError: cannot assign to field 'SOME_INT'
Run Code Online (Sandbox Code Playgroud)


Too*_*eve 8

这是"常量"类的实现,它使用只读(常量)属性创建实例.例如,可以使用Nums.PI获取已初始化为的值3.14159,并Nums.PI = 22引发异常.

# ---------- Constants.py ----------
class Constants(object):
    """
    Create objects with read-only (constant) attributes.
    Example:
        Nums = Constants(ONE=1, PI=3.14159, DefaultWidth=100.0)
        print 10 + Nums.PI
        print '----- Following line is deliberate ValueError -----'
        Nums.PI = 22
    """

    def __init__(self, *args, **kwargs):
        self._d = dict(*args, **kwargs)

    def __iter__(self):
        return iter(self._d)

    def __len__(self):
        return len(self._d)

    # NOTE: This is only called if self lacks the attribute.
    # So it does not interfere with get of 'self._d', etc.
    def __getattr__(self, name):
        return self._d[name]

    # ASSUMES '_..' attribute is OK to set. Need this to initialize 'self._d', etc.
    #If use as keys, they won't be constant.
    def __setattr__(self, name, value):
        if (name[0] == '_'):
            super(Constants, self).__setattr__(name, value)
        else:
            raise ValueError("setattr while locked", self)

if (__name__ == "__main__"):
    # Usage example.
    Nums = Constants(ONE=1, PI=3.14159, DefaultWidth=100.0)
    print 10 + Nums.PI
    print '----- Following line is deliberate ValueError -----'
    Nums.PI = 22
Run Code Online (Sandbox Code Playgroud)

感谢@MikeGraham的FrozenDict,我将其作为起点.已更改,因此不是Nums['ONE']使用语法Nums.ONE.

感谢@ Raufio的回答,想要覆盖__ setattr __.

或者对于具有更多功能的实现,请参阅GitHub上的 @Hans_meine的 named_constants

  • Python是一种同意成年人的语言.对此类事情没有任何保护.`Nums._d ['PI'] = 22`我相信,语言本身并没有提供任何方式将事物标记为非变异. (2认同)

Gar*_*son 8

您可以使用namedtuple作为变通方法来有效地创建一个常量,该常量与Java中的静态最终变量(Java"常量")的工作方式相同.随着变通办法,它有点优雅.(更优雅的方法是简单地改进Python语言 - 什么样的语言可以让你重新定义math.pi? - 但我离题了.)

(在我写这篇文章时,我意识到这个问题的另一个答案提到了namedtuple,但我将继续在这里,因为我将展示一种语法,与Java中你所期望的更为平行,因为不需要创建一个命名类型为namedtuple迫使你去做.)

按照你的例子,你会记得在Java中我们必须在某个类中定义常量; 因为你没有提到一个班级名称,让我们称之为Foo.这是Java类:

public class Foo {
  public static final String CONST_NAME = "Name";
}
Run Code Online (Sandbox Code Playgroud)

这是等效的Python.

from collections import namedtuple
Foo = namedtuple('_Foo', 'CONST_NAME')('Name')
Run Code Online (Sandbox Code Playgroud)

我想在这里添加的关键点是你不需要一个单独的Foo类型(一个"匿名命名元组"会很好,即使这听起来像一个矛盾),所以我们命名我们的namedtuple _Foo所以希望它不会逃到导入模块.

这里的第二点是我们立即创建 nametuple的一个实例,调用它Foo; 没有必要在单独的步骤中执行此操作(除非您愿意).现在,您可以在Java中执行以下操作:

>>> Foo.CONST_NAME
'Name'
Run Code Online (Sandbox Code Playgroud)

但你无法分配给它:

>>> Foo.CONST_NAME = 'bar'
…
AttributeError: can't set attribute
Run Code Online (Sandbox Code Playgroud)

致谢:我以为我发明了一种命名方法,但后来我发现别人给出了类似的(虽然不那么紧凑)答案.然后我也注意到Python中的"命名元组"是什么?,它指出sys.version_info现在是一个命名元组,所以也许Python标准库早就提出了这个想法.

请注意,遗憾的是(这仍然是Python),您可以Foo完全删除整个作业:

>>> Foo = 'bar'
Run Code Online (Sandbox Code Playgroud)

(捂脸)

但至少我们阻止Foo.CONST_NAME价值被改变,这比没有好.祝好运.


MVP*_*MVP 7

我们可以创建一个描述符对象。

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")
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

如果我创建 Foo 的子类,这个类将继承常量而不能修改它们

class Bar(Foo): pass

>>> Bar.NUM  #=> 255
>>> Bar.NUM = 0  #=> ValueError: You can't change a constant
Run Code Online (Sandbox Code Playgroud)


Xav*_* Ho 6

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

RED = 1
GREEN = 2
BLUE = 3
Run Code Online (Sandbox Code Playgroud)

然后编写您的类或函数。由于常量几乎总是整数,并且它们在 Python 中也是不可变的,因此您几乎没有机会改变它。

当然,除非您明确设置RED = 2.

  • 是的,但是**阻止**“显式设置`RED = 2`”的能力是能够将变量名称声明为“常量”的全部好处(在其他语言中)! (23认同)
  • 根本问题是,有些人可能将其视为无法更改的真实来源值,并在整个代码中将其用作真实来源,而不是引入魔法值(我在 Python 中看到很多) - 其他人可能会将其视为可以随意更改的内容。当有人更改了一个全局变量,而你无法分辨它的更改位置,并且应用程序因为 RED="blue" 而不是 "red" 而崩溃时,你正在引入一个完全不必要的问题,这个问题已经被如此简单地解决了被普遍理解。 (8认同)
  • 你会从阻止它中受益吗?const 最有用的东西通常是编译器优化,这在 Python 中并不是真正的东西。想要一些东西是恒定的吗?只是不要改变它。如果你担心别人改变它,你可以把它放在他们的范围之外,或者只是意识到,如果有人改变它,那是他们的问题,他们需要处理它,而不是你。 (6认同)

Rau*_*fio 6

我会创建一个类来覆盖__setattr__基础对象类的方法并用它包装我的常量,请注意我使用的是python 2.7:

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)
Run Code Online (Sandbox Code Playgroud)

要包装一个字符串:

>>> constObj = const("Try to change me")
>>> constObj.value
'Try to change me'
>>> constObj.value = "Changed"
Traceback (most recent call last):
   ...
ValueError: Trying to change a constant value
>>> constObj2 = const(" or not")
>>> mutableObj = constObj.value + constObj2.value
>>> mutableObj #just a string
'Try to change me or not'
Run Code Online (Sandbox Code Playgroud)

它非常简单,但是如果你想使用与非常量对象相同的常量(不使用constObj.value),它会更加密集.这可能会导致问题,因此最好保持.value显示并知道您正在使用常量进行操作(可能不是最"pythonic"方式).


tob*_*hhh 6

元组在技术上有资格作为常量,因为如果您尝试更改其中一个值,元组将引发错误.如果你想用一个值声明一个元组,那么在它唯一的值之后放一个逗号,如下所示:

my_tuple = (0 """Or any other value""",)
Run Code Online (Sandbox Code Playgroud)

要检查此变量的值,请使用与此类似的内容:

if my_tuple[0] == 0:
    #Code goes here
Run Code Online (Sandbox Code Playgroud)

如果您尝试更改此值,则会引发错误.


PAD*_*MKO 6

不幸的是,Python还没有常量,这很遗憾.ES6已经为JavaScript添加了支持常量(https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Statements/const),因为它在任何编程语言中都是非常有用的东西.正如在Python社区的其他答案中所回答的那样,使用约定 - 用户大写变量作为常量,但它不能防止代码中的任意错误.如果您愿意,可能会发现下一个单文件解决方案很有用(请参阅docstrings如何使用它).

文件constants.py

import collections


__all__ = ('const', )


class Constant(object):
    """
    Implementation strict constants in Python 3.

    A constant can be set up, but can not be changed or deleted.
    Value of constant may any immutable type, as well as list or set.
    Besides if value of a constant is list or set, it will be converted in an immutable type as next:
        list -> tuple
        set -> frozenset
    Dict as value of a constant has no support.

    >>> const = Constant()
    >>> del const.temp
    Traceback (most recent call last):
    NameError: name 'temp' is not defined
    >>> const.temp = 1
    >>> const.temp = 88
    Traceback (most recent call last):
        ...
    TypeError: Constanst can not be changed
    >>> del const.temp
    Traceback (most recent call last):
        ...
    TypeError: Constanst can not be deleted
    >>> const.I = ['a', 1, 1.2]
    >>> print(const.I)
    ('a', 1, 1.2)
    >>> const.F = {1.2}
    >>> print(const.F)
    frozenset([1.2])
    >>> const.D = dict()
    Traceback (most recent call last):
        ...
    TypeError: dict can not be used as constant
    >>> del const.UNDEFINED
    Traceback (most recent call last):
        ...
    NameError: name 'UNDEFINED' is not defined
    >>> const()
    {'I': ('a', 1, 1.2), 'temp': 1, 'F': frozenset([1.2])}
    """

    def __setattr__(self, name, value):
        """Declaration a constant with value. If mutable - it will be converted to immutable, if possible.
        If the constant already exists, then made prevent againt change it."""

        if name in self.__dict__:
            raise TypeError('Constanst can not be changed')

        if not isinstance(value, collections.Hashable):
            if isinstance(value, list):
                value = tuple(value)
            elif isinstance(value, set):
                value = frozenset(value)
            elif isinstance(value, dict):
                raise TypeError('dict can not be used as constant')
            else:
                raise ValueError('Muttable or custom type is not supported')
        self.__dict__[name] = value

    def __delattr__(self, name):
        """Deny against deleting a declared constant."""

        if name in self.__dict__:
            raise TypeError('Constanst can not be deleted')
        raise NameError("name '%s' is not defined" % name)

    def __call__(self):
        """Return all constans."""

        return self.__dict__


const = Constant()


if __name__ == '__main__':
    import doctest
    doctest.testmod()
Run Code Online (Sandbox Code Playgroud)

如果这还不够,请参阅完整的测试用例.

import decimal
import uuid
import datetime
import unittest

from ..constants import Constant


class TestConstant(unittest.TestCase):
    """
    Test for implementation constants in the Python
    """

    def setUp(self):

        self.const = Constant()

    def tearDown(self):

        del self.const

    def test_create_constant_with_different_variants_of_name(self):

        self.const.CONSTANT = 1
        self.assertEqual(self.const.CONSTANT, 1)
        self.const.Constant = 2
        self.assertEqual(self.const.Constant, 2)
        self.const.ConStAnT = 3
        self.assertEqual(self.const.ConStAnT, 3)
        self.const.constant = 4
        self.assertEqual(self.const.constant, 4)
        self.const.co_ns_ta_nt = 5
        self.assertEqual(self.const.co_ns_ta_nt, 5)
        self.const.constant1111 = 6
        self.assertEqual(self.const.constant1111, 6)

    def test_create_and_change_integer_constant(self):

        self.const.INT = 1234
        self.assertEqual(self.const.INT, 1234)
        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.INT = .211

    def test_create_and_change_float_constant(self):

        self.const.FLOAT = .1234
        self.assertEqual(self.const.FLOAT, .1234)
        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.FLOAT = .211

    def test_create_and_change_list_constant_but_saved_as_tuple(self):

        self.const.LIST = [1, .2, None, True, datetime.date.today(), [], {}]
        self.assertEqual(self.const.LIST, (1, .2, None, True, datetime.date.today(), [], {}))

        self.assertTrue(isinstance(self.const.LIST, tuple))

        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.LIST = .211

    def test_create_and_change_none_constant(self):

        self.const.NONE = None
        self.assertEqual(self.const.NONE, None)
        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.NONE = .211

    def test_create_and_change_boolean_constant(self):

        self.const.BOOLEAN = True
        self.assertEqual(self.const.BOOLEAN, True)
        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.BOOLEAN = False

    def test_create_and_change_string_constant(self):

        self.const.STRING = "Text"
        self.assertEqual(self.const.STRING, "Text")

        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.STRING += '...'

        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.STRING = 'TEst1'

    def test_create_dict_constant(self):

        with self.assertRaisesRegexp(TypeError, 'dict can not be used as constant'):
            self.const.DICT = {}

    def test_create_and_change_tuple_constant(self):

        self.const.TUPLE = (1, .2, None, True, datetime.date.today(), [], {})
        self.assertEqual(self.const.TUPLE, (1, .2, None, True, datetime.date.today(), [], {}))

        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.TUPLE = 'TEst1'

    def test_create_and_change_set_constant(self):

        self.const.SET = {1, .2, None, True, datetime.date.today()}
        self.assertEqual(self.const.SET, {1, .2, None, True, datetime.date.today()})

        self.assertTrue(isinstance(self.const.SET, frozenset))

        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.SET = 3212

    def test_create_and_change_frozenset_constant(self):

        self.const.FROZENSET = frozenset({1, .2, None, True, datetime.date.today()})
        self.assertEqual(self.const.FROZENSET, frozenset({1, .2, None, True, datetime.date.today()}))

        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.FROZENSET = True

    def test_create_and_change_date_constant(self):

        self.const.DATE = datetime.date(1111, 11, 11)
        self.assertEqual(self.const.DATE, datetime.date(1111, 11, 11))

        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.DATE = True

    def test_create_and_change_datetime_constant(self):

        self.const.DATETIME = datetime.datetime(2000, 10, 10, 10, 10)
        self.assertEqual(self.const.DATETIME, datetime.datetime(2000, 10, 10, 10, 10))

        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.DATETIME = None

    def test_create_and_change_decimal_constant(self):

        self.const.DECIMAL = decimal.Decimal(13123.12312312321)
        self.assertEqual(self.const.DECIMAL, decimal.Decimal(13123.12312312321))

        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.DECIMAL = None

    def test_create_and_change_timedelta_constant(self):

        self.const.TIMEDELTA = datetime.timedelta(days=45)
        self.assertEqual(self.const.TIMEDELTA, datetime.timedelta(days=45))

        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.TIMEDELTA = 1

    def test_create_and_change_uuid_constant(self):

        value = uuid.uuid4()
        self.const.UUID = value
        self.assertEqual(self.const.UUID, value)

        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.UUID = []

    def test_try_delete_defined_const(self):

        self.const.VERSION = '0.0.1'
        with self.assertRaisesRegexp(TypeError, 'Constanst can not be deleted'):
            del self.const.VERSION

    def test_try_delete_undefined_const(self):

        with self.assertRaisesRegexp(NameError, "name 'UNDEFINED' is not defined"):
            del self.const.UNDEFINED

    def test_get_all_defined_constants(self):

        self.assertDictEqual(self.const(), {})

        self.const.A = 1
        self.assertDictEqual(self.const(), {'A': 1})

        self.const.B = "Text"
        self.assertDictEqual(self.const(), {'A': 1, 'B': "Text"})
Run Code Online (Sandbox Code Playgroud)

优点:1.访问整个项目的所有常量2.严格控制常量值

缺点:1.不支持自定义类型和'dict'类型

笔记:

  1. 使用Python3.4和Python3.5进行测试(我使用'tox')

  2. 测试环境:

.

$ uname -a
Linux wlysenko-Aspire 3.13.0-37-generic #64-Ubuntu SMP Mon Sep 22 21:28:38 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux
Run Code Online (Sandbox Code Playgroud)


yon*_*ren 6

from enum import Enum
class StringConsts(str,Enum):
    ONE='one'
    TWO='two'

print(f'Truth is  {StringConsts.ONE=="one"}') #Truth is True
StringConsts.ONE="one" #Error: Cannot reassign
Run Code Online (Sandbox Code Playgroud)

Enum 和 str 的这种混合使您无需重新实现 setattr(通过 Enum)和与其他 str 对象(通过 str)进行比较。

这可能会完全弃用http://code.activestate.com/recipes/65207-constants-in-python/?in=user-97991

  • 其他答案要么重新实现 set_attr,要么具有在代码库中的任何位置意外分配的缺点。没有其他答案提到 Enum,更不用说 Enum 和 str 的混合了。 (2认同)

Lym*_*Zoy 5

如果您想要常量并且不关心它们的值,这里有一个技巧:

只需定义空类即可。

例如:

class RED: 
    pass
class BLUE: 
    pass
Run Code Online (Sandbox Code Playgroud)


小智 5

没有完美的方法可以做到这一点。据我了解,大多数程序员只会将标识符大写,因此 PI = 3.142 可以很容易地理解为一个常量。

另一方面,如果你想要一些实际上像常数一样的东西,我不确定你会找到它。无论你做什么,总会有某种方法来编辑“常量”,所以它实际上不会是一个常量。这是一个非常简单、肮脏的例子:

def define(name, value):
  if (name + str(id(name))) not in globals():
    globals()[name + str(id(name))] = value

def constant(name):
  return globals()[name + str(id(name))]

define("PI",3.142)

print(constant("PI"))
Run Code Online (Sandbox Code Playgroud)

这看起来会生成一个 PHP 风格的常量。

事实上,某人改变价值所需要的只是这样:

globals()["PI"+str(id("PI"))] = 3.1415
Run Code Online (Sandbox Code Playgroud)

这对于您在这里找到的所有其他解决方案都是相同的 - 即使是创建类并重新定义设置属性方法的聪明解决方案 - 总会有办法解决它们。Python 就是这样。

我的建议是避免所有麻烦并大写您的标识符。它实际上并不是一个适当的常数,但话又说回来,什么都不是。


Jua*_*hez 5

使用 namedtuple 有一种更简洁的方法:

from collections import namedtuple


def make_consts(name, **kwargs):
    return namedtuple(name, kwargs.keys())(**kwargs)
Run Code Online (Sandbox Code Playgroud)

使用示例

CONSTS = make_consts("baz1",
                     foo=1,
                     bar=2)
Run Code Online (Sandbox Code Playgroud)

使用这种方法,您可以命名常量。


Vic*_*rra 5

这是我创建的一组习语,试图改进一些已经可用的答案。

我知道使用常量不是pythonic,你不应该在家里这样做!

然而,Python 就是这样一种动态语言!这个论坛展示了如何创建看起来和感觉像常量的结构。这个答案的主要目的是探索语言可以表达什么。

请不要对我太苛刻:-)。

有关更多详细信息,我写了一篇关于这些习语伴奏博客

在这篇文章中,我将调用一个常量变量来引用值(不可变或其他)的常量。此外,我说一个变量在引用客户端代码无法更新其值的可变对象时具有冻结值。

一个空间常数(SpaceConstants)

这个习语创建了一个看起来像常量变量(又名 SpaceConstants)的命名空间。它是Alex Martelli对代码片段的修改,以避免使用模块对象。特别是,这个修改使用了我所说的类工厂,因为在SpaceConstants函数中,定义了一个名为SpaceConstants的类,并返回了它的一个实例。

我在stackoverflow博客文章中探索了使用类工厂在 Python 中实现基于策略的设计相似。

def SpaceConstants():
    def setattr(self, name, value):
        if hasattr(self, name):
            raise AttributeError(
                "Cannot reassign members"
            )
        self.__dict__[name] = value
    cls = type('SpaceConstants', (), {
        '__setattr__': setattr
    })
    return cls()

sc = SpaceConstants()

print(sc.x) # raise "AttributeError: 'SpaceConstants' object has no attribute 'x'"
sc.x = 2 # bind attribute x
print(sc.x) # print "2"
sc.x = 3 # raise "AttributeError: Cannot reassign members"
sc.y = {'name': 'y', 'value': 2} # bind attribute y
print(sc.y) # print "{'name': 'y', 'value': 2}"
sc.y['name'] = 'yprime' # mutable object can be changed
print(sc.y) # print "{'name': 'yprime', 'value': 2}"
sc.y = {} # raise "AttributeError: Cannot reassign members"

Run Code Online (Sandbox Code Playgroud)

冻结值的空间 (SpaceFrozenValues)

下一个习惯用法是对SpaceConstants的修改,其中引用的可变对象被冻结。这个实现利用了我所说的setattrgetattr函数之间的共享闭包。可变对象的值由函数共享闭包内部的变量缓存定义复制和引用。它形成了我所说的可变对象闭包保护副本

使用这个习惯用法时必须小心,因为getattr通过执行深度复制返回缓存的值。此操作可能会对大型对象产生显着的性能影响!

from copy import deepcopy

def SpaceFrozenValues():
    cache = {}
    def setattr(self, name, value):
        nonlocal cache
        if name in cache:
            raise AttributeError(
                "Cannot reassign members"
            )
        cache[name] = deepcopy(value)
    def getattr(self, name):
        nonlocal cache
        if name not in cache:
            raise AttributeError(
                "Object has no attribute '{}'".format(name)
            )
        return deepcopy(cache[name])
    cls = type('SpaceFrozenValues', (),{
        '__getattr__': getattr,
        '__setattr__': setattr
    })
    return cls()

fv = SpaceFrozenValues()
print(fv.x) # AttributeError: Object has no attribute 'x'
fv.x = 2 # bind attribute x
print(fv.x) # print "2"
fv.x = 3 # raise "AttributeError: Cannot reassign members"
fv.y = {'name': 'y', 'value': 2} # bind attribute y
print(fv.y) # print "{'name': 'y', 'value': 2}"
fv.y['name'] = 'yprime' # you can try to change mutable objects
print(fv.y) # print "{'name': 'y', 'value': 2}"
fv.y = {} # raise "AttributeError: Cannot reassign members"
Run Code Online (Sandbox Code Playgroud)

一个常数空间(ConstantSpace)

这个习惯用法是常量变量或ConstantSpace的不可变命名空间。它结合了非常简单的 Jon Betts 在stackoverflow 中的答案和class factory

def ConstantSpace(**args):
    args['__slots__'] = ()
    cls = type('ConstantSpace', (), args)
    return cls()

cs = ConstantSpace(
    x = 2,
    y = {'name': 'y', 'value': 2}
)

print(cs.x) # print "2"
cs.x = 3 # raise "AttributeError: 'ConstantSpace' object attribute 'x' is read-only"
print(cs.y) # print "{'name': 'y', 'value': 2}"
cs.y['name'] = 'yprime' # mutable object can be changed
print(cs.y) # print "{'name': 'yprime', 'value': 2}"
cs.y = {} # raise "AttributeError: 'ConstantSpace' object attribute 'x' is read-only"
cs.z = 3 # raise "AttributeError: 'ConstantSpace' object has no attribute 'z'"
Run Code Online (Sandbox Code Playgroud)

冻结空间(FrozenSpace)

这个习惯用法是冻结变量或FrozenSpace的不可变命名空间。它是从前面的模式派生出来的,通过关闭生成的FrozenSpace类使每个变量成为受保护的属性

from copy import deepcopy

def FreezeProperty(value):
    cache = deepcopy(value)
    return property(
        lambda self: deepcopy(cache)
    )

def FrozenSpace(**args):
    args = {k: FreezeProperty(v) for k, v in args.items()}
    args['__slots__'] = ()
    cls = type('FrozenSpace', (), args)
    return cls()

fs = FrozenSpace(
    x = 2,
    y = {'name': 'y', 'value': 2}
)

print(fs.x) # print "2"
fs.x = 3 # raise "AttributeError: 'FrozenSpace' object attribute 'x' is read-only"
print(fs.y) # print "{'name': 'y', 'value': 2}"
fs.y['name'] = 'yprime' # try to change mutable object
print(fs.y) # print "{'name': 'y', 'value': 2}"
fs.y = {} # raise "AttributeError: 'FrozenSpace' object attribute 'x' is read-only"
fs.z = 3 # raise "AttributeError: 'FrozenSpace' object has no attribute 'z'"
Run Code Online (Sandbox Code Playgroud)


小智 5

我正在尝试不同的方法来在 Python 中创建一个真正的常量,也许我找到了很好的解决方案。

例子:

为常量创建容器

>>> DAYS = Constants(
...     MON=0,
...     TUE=1,
...     WED=2,
...     THU=3,
...     FRI=4,
...     SAT=5,
...     SUN=6
... )   
Run Code Online (Sandbox Code Playgroud)

从容器中获取价值

>>> DAYS.MON
0
>>> DAYS['MON']
0  
Run Code Online (Sandbox Code Playgroud)

用纯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}
Run Code Online (Sandbox Code Playgroud)

所有常量都是不可变的

>>> DAYS.MON = 7
...
AttributeError: Immutable attribute

>>> del DAYS.MON 
...
AttributeError: Immutable attribute
Run Code Online (Sandbox Code Playgroud)

仅对常量自动完成

>>> dir(DAYS)
['FRI', 'MON', 'SAT', 'SUN', 'THU', 'TUE', 'WED']
Run Code Online (Sandbox Code Playgroud)

排序就像list.sort

>>> DAYS.sort(key=lambda (k, v): v, reverse=True)
>>> list(DAYS)
['SUN', 'SAT', 'FRI', 'THU', 'WED', 'TUE', 'MON']
Run Code Online (Sandbox Code Playgroud)

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

Run Code Online (Sandbox Code Playgroud)