问题:如何在Python中创建常量?

有没有办法在Python中声明常量?在Java中,我们可以按以下方式创建常量值:

public static final String CONST_NAME = "Name";

Python中上述Java常量声明的等效项是什么?

Is there a way to declare a constant in Python? In Java we can create constant values in this manner:

public static final String CONST_NAME = "Name";

What is the equivalent of the above Java constant declaration in Python?


回答 0

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

如果您在上课,则等效项为:

class Foo(object):
    CONST_NAME = "Name"

如果不是,那只是

CONST_NAME = "Name"

但是您可能想看看Alex Martelli 编写的Python中的代码片段Constants


从Python 3.8开始,有一个变量注释,它将告诉静态类型检查器(如mypy)不要重新分配变量。这与Java的最接近final。但是,它实际上并不能阻止重新分配

from typing import Final

a: Final = 1

# Executes fine, but mypy will report an error if you run mypy on this:
a = 2

No there is not. You cannot declare a variable or value as constant in Python. Just don’t change it.

If you are in a class, the equivalent would be:

class Foo(object):
    CONST_NAME = "Name"

if not, it is just

CONST_NAME = "Name"

But you might want to have a look at the code snippet Constants in Python by Alex Martelli.


As of Python 3.8, there’s a variable annotation that will tell static type checkers (like mypy) that your variable shouldn’t be reassigned. This is the closest equivalent to Java’s final. However, it does not actually prevent reassignment:

from typing import Final

a: Final = 1

# Executes fine, but mypy will report an error if you run mypy on this:
a = 2

回答 1

没有const其他语言中的关键字,但是可以创建一个具有“ getter函数”的属性来读取数据,而没有“ setter函数”的属性来重写数据。实质上,这可以防止标识符被更改。

这是使用类属性的替代实现:

请注意,对于想知道常量的读者来说,这段代码远非易事。请参阅下面的说明

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

代码说明:

  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

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

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

There’s no const keyword as in other languages, however it is possible to create a Property that has a “getter function” to read the data, but no “setter function” to re-write the data. This essentially protects the identifier from being changed.

Here is an alternative implementation using class property:

Note that the code is far from easy for a reader wondering about constants. See explanation below

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

Code Explanation:

  1. Define a function constant that takes an expression, and uses it to construct a “getter” – a function that solely returns the value of the expression.
  2. The setter function raises a TypeError so it’s read-only
  3. Use the constant function we just created as a decoration to quickly define read-only properties.

And in some other more old-fashioned way:

(The code is quite tricky, more explanations below)

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

Note that the @apply decorator seems to be deprecated.

  1. To define the identifier FOO, firs define two functions (fset, fget – the names are at my choice).
  2. Then use the built-in property function to construct an object that can be “set” or “get”.
  3. Note hat the property function’s first two parameters are named fset and fget.
  4. Use the fact that we chose these very names for our own getter & setter and create a keyword-dictionary using the ** (double asterisk) applied to all the local definitions of that scope to pass parameters to the property function

回答 2

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

因此,以相同的方式,您可以简单地将常量声明为所有大写字母,例如

MY_CONSTANT = "one"

如果希望此常量永远不变,则可以加入属性访问并进行技巧,但是更简单的方法是声明一个函数

def MY_CONSTANT():
    return "one"

唯一的问题是您将必须要做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

In Python instead of language enforcing something, people use naming conventions e.g __method for private methods and using _method for protected methods.

So in same manner you can simply declare the constant as all caps e.g.

MY_CONSTANT = "one"

If you want that this constant never changes, you can hook into attribute access and do tricks, but a simpler approach is to declare a function

def MY_CONSTANT():
    return "one"

Only problem is everywhere you will have to do MY_CONSTANT(), but again MY_CONSTANT = "one" is the correct way in python(usually).

You can also use namedtuple to create constants:

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

回答 3

我最近发现了一个非常简洁的更新,它会自动引发有意义的错误消息并阻止通过__dict__以下方式进行访问:

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'

我们将自己定义为使自己成为实例,然后使用插槽确保不会添加任何其他属性。这也将删除__dict__访问路由。当然,整个对象仍然可以重新定义。

编辑-原始解决方案

我可能在这里缺少技巧,但这似乎对我有用:

class CONST(object):
    FOO = 1234

    def __setattr__(self, *_):
        pass

CONST = CONST()

#----------

print CONST.FOO    # 1234

CONST.FOO = 4321
CONST.BAR = 5678

print CONST.FOO    # Still 1234!
print CONST.BAR    # Oops AttributeError

创建实例可以使magic __setattr__方法生效并拦截设置FOO变量的尝试。如果愿意,可以在这里抛出异常。通过类名称实例化实例可防止直接通过类进行访问。

一个值总让人痛苦,但是您可以将很多东西附加到您的CONST对象上。拥有上流社会的阶级名称似乎也有点古怪,但我认为总体上来说它是很简洁的。

I’ve recently found a very succinct update to this which automatically raises meaningful error messages and prevents access via __dict__:

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'

We define over ourselves as to make ourselves an instance and then use slots to ensure that no additional attributes can be added. This also removes the __dict__ access route. Of course, the whole object can still be redefined.

Edit – Original solution

I’m probably missing a trick here, but this seems to work for me:

class CONST(object):
    FOO = 1234

    def __setattr__(self, *_):
        pass

CONST = CONST()

#----------

print CONST.FOO    # 1234

CONST.FOO = 4321
CONST.BAR = 5678

print CONST.FOO    # Still 1234!
print CONST.BAR    # Oops AttributeError

Creating the instance allows the magic __setattr__ method to kick in and intercept attempts to set the FOO variable. You could throw an exception here if you wanted to. Instantiating the instance over the class name prevents access directly via the class.

It’s a total pain for one value, but you could attach lots to your CONST object. Having an upper class, class name also seems a bit grotty, but I think it’s quite succinct overall.


回答 4

Python没有常数。

也许最简单的选择是为其定义一个函数:

def MY_CONSTANT():
    return 42

MY_CONSTANT() 现在具有常量的所有功能(加上一些讨厌的花括号)。

Python doesn’t have constants.

Perhaps the easiest alternative is to define a function for it:

def MY_CONSTANT():
    return 42

MY_CONSTANT() now has all the functionality of a constant (plus some annoying braces).


回答 5

除了两个最重要的答案(仅使用带大写名称的变量,或使用属性将值设置为只读)外,我还要提到可以使用元类来实现命名常量。我提供了一个使用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,但仍然非常易于使用和方便。(该模块具有更多功能,包括常量为只读,请参见其自述文件。)

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

In addition to the two top answers (just use variables with UPPERCASE names, or use properties to make the values read-only), I want to mention that it’s possible to use metaclasses in order to implement named constants. I provide a very simple solution using metaclasses at GitHub which may be helpful if you want the values to be more informative about their type/name:

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

This is slightly more advanced Python, but still very easy to use and handy. (The module has some more features, including constants being read-only, see its README.)

There are similar solutions floating around in various repositories, but to the best of my knowledge they either lack one of the fundamental features that I would expect from constants (like being constant, or being of arbitrary type), or they have esoteric features added that make them less generally applicable. But YMMV, I would be grateful for feedback. :-)


回答 6

属性是创建常量的一种方法。您可以通过声明一个getter属性,而忽略setter来做到这一点。例如:

class MyFinalProperty(object):

    @property
    def name(self):
        return "John"

您可以看一下我写的一篇文章,以找到更多使用Python属性的方法。

Properties are one way to create constants. You can do it by declaring a getter property, but ignoring the setter. For example:

class MyFinalProperty(object):

    @property
    def name(self):
        return "John"

You can have a look at an article I’ve written to find more ways to use Python properties.


回答 7

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

注意:这个其他答案似乎提供了与以下类似的更完整的实现(具有更多功能)。

首先,创建一个元类

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

    def __setattr__(cls, key, value):
        raise TypeError

这样可以防止更改静态属性。然后制作另一个使用该元类的类:

class Const(object):
    __metaclass__ = MetaConst

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

    def __setattr__(self, name, value):
        raise TypeError

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

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

    def __setattr__(self, name, value):
        raise TypeError

这样可以防止实例道具被更改。要使用它,请继承:

class MyConst(Const):
    A = 1
    B = 2

现在,直接或通过实例访问的道具应该是恒定的:

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

MyConst.A = 'changed'
# TypeError
my_const.A = 'changed'
# TypeError

是上面的例子。这是 Python 3 另一个示例。

Edit: Added sample code for Python 3

Note: this other answer looks like it provides a much more complete implementation similar to the following (with more features).

First, make a metaclass:

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

    def __setattr__(cls, key, value):
        raise TypeError

This prevents statics properties from being changed. Then make another class that uses that metaclass:

class Const(object):
    __metaclass__ = MetaConst

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

    def __setattr__(self, name, value):
        raise TypeError

Or, if you’re using Python 3:

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

    def __setattr__(self, name, value):
        raise TypeError

This should prevent instance props from being changed. To use it, inherit:

class MyConst(Const):
    A = 1
    B = 2

Now the props, accessed directly or via an instance, should be constant:

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

MyConst.A = 'changed'
# TypeError
my_const.A = 'changed'
# TypeError

Here’s an example of above in action. Here’s another example for Python 3.


回答 8

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

(在撰写本文时,我意识到提到了namedtuple这个问题的另一个答案,但是我将继续在这里,因为我将展示一种语法,该语法与Java期望的语法更加相似,因为无需创建named 以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类型(即使听起来像是矛盾的词,“匿名命名的元组”也很好),所以我们将我们的namedtuple命名为_Foo希望它不会转至导入模块。

这里的第二点是,我们立即创建 nametuple 的实例,将其调用Foo;无需单独执行此操作(除非您愿意)。现在,您可以执行Java中的操作:

>>> Foo.CONST_NAME
'Name'

但是您不能分配给它:

>>> Foo.CONST_NAME = 'bar'

AttributeError: can't set attribute

致谢:我以为我发明了namedtuple方法,但是后来我看到别人也给出了类似的答案(尽管不太紧凑)。然后我还注意到Python中什么是“命名元组”?,它指出sys.version_info现在是一个namedtuple,因此Python标准库也许早就提出了这个想法。

请注意,不幸的是(仍然是Python),您可以Foo完全擦除整个分配:

>>> Foo = 'bar'

(facepalm)

但是,至少我们阻止了Foo.CONST_NAME价值的改变,这总比没有好。祝好运。

You can use a namedtuple as a workaround to effectively create a constant that works the same way as a static final variable in Java (a Java “constant”). As workarounds go, it’s sort of elegant. (A more elegant approach would be to simply improve the Python language — what sort of language lets you redefine math.pi? — but I digress.)

(As I write this, I realize another answer to this question mentioned namedtuple, but I’ll continue here because I’ll show a syntax that more closely parallels what you’d expect in Java, as there is no need to create a named type as namedtuple forces you to do.)

Following your example, you’ll remember that in Java we must define the constant inside some class; because you didn’t mention a class name, let’s call it Foo. Here’s the Java class:

public class Foo {
  public static final String CONST_NAME = "Name";
}

Here’s the equivalent Python.

from collections import namedtuple
Foo = namedtuple('_Foo', 'CONST_NAME')('Name')

The key point I want to add here is that you don’t need a separate Foo type (an “anonymous named tuple” would be nice, even though that sounds like an oxymoron), so we name our namedtuple _Foo so that hopefully it won’t escape to importing modules.

The second point here is that we immediately create an instance of the nametuple, calling it Foo; there’s no need to do this in a separate step (unless you want to). Now you can do what you can do in Java:

>>> Foo.CONST_NAME
'Name'

But you can’t assign to it:

>>> Foo.CONST_NAME = 'bar'
…
AttributeError: can't set attribute

Acknowledgement: I thought I invented the namedtuple approach, but then I see that someone else gave a similar (although less compact) answer. Then I also noticed What are “named tuples” in Python?, which points out that sys.version_info is now a namedtuple, so perhaps the Python standard library already came up with this idea much earlier.

Note that unfortunately (this still being Python), you can erase the entire Foo assignment altogether:

>>> Foo = 'bar'

(facepalm)

But at least we’re preventing the Foo.CONST_NAME value from being changed, and that’s better than nothing. Good luck.


回答 9

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

因此,您可以执行以下操作:

MY_CONSTANT: Final = 12407

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

PEP 591 has the ‘final’ qualifier. Enforcement is down to the type checker.

So you can do:

MY_CONSTANT: Final = 12407

Note: Final keyword is only applicable for Python 3.8 version


回答 10

这是“常量”类的实现,该类创建具有只读(常量)属性的实例。例如,可以使用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

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

并感谢@Raufio的回答,以提供覆盖__ setattr __的想法。

有关更多功能的实现,请参见GitHub上的 @Hans_meine的 named_constants

Here is an implementation of a “Constants” class, which creates instances with read-only (constant) attributes. E.g. can use Nums.PI to get a value that has been initialized as 3.14159, and Nums.PI = 22 raises an exception.

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

Thanks to @MikeGraham ‘s FrozenDict, which I used as a starting point. Changed, so instead of Nums['ONE'] the usage syntax is Nums.ONE.

And thanks to @Raufio’s answer, for idea to override __ setattr __.

Or for an implementation with more functionality, see @Hans_meine ‘s named_constants at GitHub


回答 11

从技术上讲,元组可以视为常量,因为如果尝试更改其值之一,则元组会引发错误。如果要声明具有一个值的元组,则在其唯一值后放置一个逗号,如下所示:

my_tuple = (0 """Or any other value""",)

要检查此变量的值,请使用类似于以下内容的方法:

if my_tuple[0] == 0:
    #Code goes here

如果尝试更改该值,将引发错误。

A tuple technically qualifies as a constant, as a tuple will raise an error if you try to change one of its values. If you want to declare a tuple with one value, then place a comma after its only value, like this:

my_tuple = (0 """Or any other value""",)

To check this variable’s value, use something similar to this:

if my_tuple[0] == 0:
    #Code goes here

If you attempt to change this value, an error will be raised.


回答 12

我将创建一个覆盖__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)

要包装字符串:

>>> 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'

这很简单,但是如果您要像使用非常量对象一样使用常量(不使用constObj.value),则会更加费劲。这可能会引起问题,因此最好保持.value显示状态并知道您正在使用常量进行操作(尽管这不是最“ pythonic”的方式)。

I would make a class that overrides the __setattr__ method of the base object class and wrap my constants with that, note that I’m using 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)

To wrap a string:

>>> 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'

It’s pretty simple, but if you want to use your constants the same as you would a non-constant object (without using constObj.value), it will be a bit more intensive. It’s possible that this could cause problems, so it might be best to keep the .value to show and know that you are doing operations with constants (maybe not the most ‘pythonic’ way though).


回答 13

不幸的是,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()

如果这还不够,请查看完整的测试用例。

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

优点: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

Unfortunately the Python has no constants so yet and it is shame. ES6 already added support constants to JavaScript (https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Statements/const) since it is a very useful thing in any programming language. As answered in other answers in Python community use the convention – user uppercase variable as constants, but it does not protect against arbitrary errors in code. If you like, you may be found useful a single-file solution as next (see docstrings how use it).

file 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()

If this is not enough, see full testcase for it.

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

Advantages: 1. Access to all constants for whole project 2. Strict control for values of constants

Lacks: 1. Not support for custom types and the type ‘dict’

Notes:

  1. Tested with Python3.4 and Python3.5 (I am use the ‘tox’ for it)

  2. Testing environment:

.

$ 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

回答 14

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

RED = 1
GREEN = 2
BLUE = 3

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

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

The Pythonic way of declaring “constants” is basically a module level variable:

RED = 1
GREEN = 2
BLUE = 3

And then write your classes or functions. Since constants are almost always integers, and they are also immutable in Python, you have a very little chance of altering it.

Unless, of course, if you explicitly set RED = 2.


回答 15

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

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

We can create a descriptor object.

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) If we wanted to work with constants at the instance level then:

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) if we wanted to create constants only at the class level, we could use a metaclass that serves as a container for our constants (our descriptor objects); all the classes that descend will inherit our constants (our descriptor objects) without any risk that can be modified.

# 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

If I create a subclass of Foo, this class will inherit the constant without the possibility of modifying them

class Bar(Foo): pass

>>> Bar.NUM  #=> 255
>>> Bar.NUM = 0  #=> ValueError: You can't change a constant

回答 16

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

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

Python dictionaries are mutable, so they don’t seem like a good way to declare constants:

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

回答 17

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

只需定义空类。

例如:

class RED: 
    pass
class BLUE: 
    pass

Here’s a trick if you want constants and don’t care their values:

Just define empty classes.

e.g:

class RED: 
    pass
class BLUE: 
    pass

回答 18

在python中,常数只是一个变量,其名称全部用大写字母表示,单词之间用下划线字符分隔,

例如

DAYS_IN_WEEK = 7

该值是可变的,因为您可以更改它。但是,鉴于名称规则告诉您一个常量,为什么呢?我的意思是,这毕竟是您的程序!

这是整个python采取的方法。没有private出于相同原因,关键字。在名称前加上下划线,您将知道该名称是私有的。代码可能会违反规则……就像程序员可以删除私有关键字一样。

Python本可以添加一个 const关键字…但是程序员可以删除关键字,然后根据需要更改常量,但是为什么这样做呢?如果您想违反规则,则可以随时更改规则。但是,如果名称使意图清楚,为什么还要烦扰规则呢?

也许在某些单元测试中,对价值进行更改有意义吗?即使在现实世界中,一周中的天数无法更改,也要查看一周8天的情况。如果这种语言阻止了您的出现,那么在这种情况下您就需要打破规则……您将不得不停止将其声明为常量,即使它在应用程序中仍然是常量,并且只是这个测试用例,查看更改后会发生什么。

所有大写的名称告诉您它应为常数。那很重要。不是一种语言会强制对代码施加约束,但是您仍然可以更改代码。

那就是python的理念。

In python, a constant is simply a variable with a name in all capitals, with words separated by the underscore character,

e.g

DAYS_IN_WEEK = 7

The value is mutable, as in you can change it. But given the rules for the name tell you is a constant, why would you? I mean, it is your program after all!

This is the approach taken throughout python. There is no private keyword for the same reason. Prefix the name with an underscore and you know it is intended to be private. Code can break the rule….just as a programmer could remove the private keyword anyway.

Python could have added a const keyword… but a programmer could remove keyword and then change the constant if they want to, but why do that? If you want to break the rule, you could change the rule anyway. But why bother to break the rule if the name makes the intention clear?

Maybe there is some unit test where it makes sense to apply a change to value? To see what happens for an 8 day week even though in the real world the number of days in the week cannot be changed. If the language stopped you making an exception if there is just this one case you need to break the rule…you would then have to stop declaring it as a constant, even though it still is a constant in the application, and there is just this one test case that sees what happens if it is changed.

The all upper case name tells you it is intended to be a constant. That is what is important. Not a language forcing constraints on code you have the power to change anyway.

That is the philosophy of python.


回答 19

没有完美的方法可以做到这一点。据我了解,大多数程序员只会将标识符大写,因此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"))

看起来它将使一个PHP样式的常量。

实际上,某人更改值所需的一切是这样的:

globals()["PI"+str(id("PI"))] = 3.1415

在这里可以找到的所有其他解决方案都是相同的,即使是聪明的解决方案也可以创建类并重新定义set属性方法,但总会有解决之道。Python就是这样。

我的建议是避免所有麻烦,只使用标识符大写。它实际上不是一个适当的常数,但是再也没有。

There’s no perfect way to do this. As I understand it most programmers will just capitalize the identifier, so PI = 3.142 can be readily understood to be a constant.

On the otherhand, if you want something that actually acts like a constant, I’m not sure you’ll find it. With anything you do there will always be some way of editing the “constant” so it won’t really be a constant. Here’s a very simple, dirty example:

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

This looks like it will make a PHP-style constant.

In reality all it takes for someone to change the value is this:

globals()["PI"+str(id("PI"))] = 3.1415

This is the same for all the other solutions you’ll find on here – even the clever ones that make a class and redefine the set attribute method – there will always be a way around them. That’s just how Python is.

My recommendation is to just avoid all the hassle and just capitalize your identifiers. It wouldn’t really be a proper constant but then again nothing would.


回答 20

有一个更干净的方法可以使用namedtuple做到这一点:

from collections import namedtuple


def make_consts(name, **kwargs):
    return namedtuple(name, kwargs.keys())(**kwargs)

使用范例

CONSTS = make_consts("baz1",
                     foo=1,
                     bar=2)

通过这种精确的方法,您可以为常量命名空间。

There is a cleaner way to do this with namedtuple:

from collections import namedtuple


def make_consts(name, **kwargs):
    return namedtuple(name, kwargs.keys())(**kwargs)

Usage Example

CONSTS = make_consts("baz1",
                     foo=1,
                     bar=2)

With this exactly approach you can namespace your constants.


回答 21

也许pconst库会为您提供帮助(github)。

$ pip install pconst

from pconst import const
const.APPLE_PRICE = 100
const.APPLE_PRICE = 200

[Out] Constant value of "APPLE_PRICE" is not editable.

Maybe pconst library will help you (github).

$ pip install pconst

from pconst import const
const.APPLE_PRICE = 100
const.APPLE_PRICE = 200

[Out] Constant value of "APPLE_PRICE" is not editable.


回答 22

您可以使用StringVar或IntVar等,您的常数为const_val

val = 'Stackoverflow'
const_val = StringVar(val)
const.trace('w', reverse)

def reverse(*args):
    const_val.set(val)

You can use StringVar or IntVar, etc, your constant is const_val

val = 'Stackoverflow'
const_val = StringVar(val)
const.trace('w', reverse)

def reverse(*args):
    const_val.set(val)

回答 23

您可以使用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

You can do it with collections.namedtuple and 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

回答 24

(本段的意思是对这些问题的答案评论在这里那里,里面提到namedtuple,但它变得太长,不适合转换为注释,所以,在这里它去。)

上面提到的namedtuple方法绝对是创新的。但是,为了完整起见,在其官方文档的NamedTuple部分的末尾,其内容为:

枚举常量可以用命名元组实现,但是使用简单的类声明更简单,更高效:

class Status:
    open, pending, closed = range(3)

换句话说,官方文档倾向于使用一种实用的方式,而不是实际实现只读行为。我想这成为了Zen Zen的另一个例子:

简单胜于复杂。

实用性胜过纯度。

(This paragraph was meant to be a comment on those answers here and there, which mentioned namedtuple, but it is getting too long to be fit into a comment, so, here it goes.)

The namedtuple approach mentioned above is definitely innovative. For the sake of completeness, though, at the end of the NamedTuple section of its official documentation, it reads:

enumerated constants can be implemented with named tuples, but it is simpler and more efficient to use a simple class declaration:

class Status:
    open, pending, closed = range(3)

In other words, the official documentation kind of prefers to use a practical way, rather than actually implementing the read-only behavior. I guess it becomes yet another example of Zen of Python:

Simple is better than complex.

practicality beats purity.


回答 25

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

我知道常量的使用不是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"

冻结值空间(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"

常数空间(ConstantSpace)

这个习惯用法是常量变量或ConstantSpace的不变命名空间。这是赫然简单乔恩·贝茨回答的组合计算器类工厂

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

冻结空间(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'"

Here it is a collection of idioms that I created as an attempt to improve some of the already available answers.

I know the use of constant is not pythonic, and you should not do this at home!

However, Python is such a dynamic language! This forum shows how it is possible the creation of constructs that looks and feels like constants. This answer has as the primary purpose to explore what can be expressed by the language.

Please do not be too harsh with me :-).

For more details I wrote a accompaniment blog about these idioms.

In this post, I will call a constant variable to a constant reference to values (immutable or otherwise). Moreover, I say that a variable has a frozen value when it references a mutable object that a client-code cannot update its value(s).

A space of constants (SpaceConstants)

This idiom creates what looks like a namespace of constant variables (a.k.a. SpaceConstants). It is a modification of a code snippet by Alex Martelli to avoid the use of module objects. In particular, this modification uses what I call a class factory because within SpaceConstants function, a class called SpaceConstants is defined, and an instance of it is returned.

I explored the use of class factory to implement a policy-based design look-alike in Python in stackoverflow and also in a blogpost.

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"

A space of frozen values (SpaceFrozenValues)

This next idiom is a modification of the SpaceConstants in where referenced mutable objects are frozen. This implementation exploits what I call shared closure between setattr and getattr functions. The value of the mutable object is copied and referenced by variable cache define inside of the function shared closure. It forms what I call a closure protected copy of a mutable object.

You must be careful in using this idiom because getattr return the value of cache by doing a deep copy. This operation could have a significant performance impact on large objects!

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"

A constant space (ConstantSpace)

This idiom is an immutable namespace of constant variables or ConstantSpace. It is a combination of awesomely simple Jon Betts’ answer in stackoverflow with a 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'"

A frozen space (FrozenSpace)

This idiom is an immutable namespace of frozen variables or FrozenSpace. It is derived from the previous pattern by making each variable a protected property by closure of the generated FrozenSpace class.

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

回答 26

在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;

In Python, constants do not exist, but you can indicate that a variable is a constant and must not be changed by adding CONST_ to the start of the variable name and stating that it is a constant in a comment:

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.

Alternatively, you may create a function that acts like a constant:

def CONST_daysInWeek():
    return 7;

回答 27

就我而言,我需要一个不可变的字节数组来实现一个加密库的实现,该库包含许多我想确保常量的文字数字。

此答案有效,但尝试重新分配字节数组元素不会引发错误。

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

常量是不可变的,但是常量字节数组的分配会静默失败:

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

一种更强大,更简单,甚至可能更多的“ pythonic”方法涉及使用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

In my case, I needed immutable bytearrays for an implementation of a crypto library containing many literal numbers I wanted to ensure were constant.

This answer works but attempted reassignment of bytearray elements does not raise an error.

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

Constants are immutable, but constant bytearray assignment fails silently:

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

A more powerful, simple, and perhaps even more ‘pythonic’ approach involves the use of memoryview objects (buffer objects in <= 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 item assignment is a 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

回答 28

我为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网址: pypigithub

I write a util lib for python const: kkconst – pypi support str, int, float, datetime

the const field instance will keep its base type behavior.

For example:

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

more details usage you can read the pypi url: pypi or github


回答 29

您可以将常量包装在numpy数组中,将其标记为只写,并始终按索引零对其进行调用。

import numpy as np

# declare a constant
CONSTANT = 'hello'

# put constant in numpy and make read only
CONSTANT = np.array([CONSTANT])
CONSTANT.flags.writeable = False
# alternatively: CONSTANT.setflags(write=0)

# call our constant using 0 index    
print 'CONSTANT %s' % CONSTANT[0]

# attempt to modify our constant with try/except
new_value = 'goodbye'
try:
    CONSTANT[0] = new_value
except:
    print "cannot change CONSTANT to '%s' it's value '%s' is immutable" % (
        new_value, CONSTANT[0])

# attempt to modify our constant producing ValueError
CONSTANT[0] = new_value



>>>
CONSTANT hello
cannot change CONSTANT to 'goodbye' it's value 'hello' is immutable
Traceback (most recent call last):
  File "shuffle_test.py", line 15, in <module>
    CONSTANT[0] = new_value
ValueError: assignment destination is read-only

当然,这仅保护numpy的内容,而不保护变量“ CONSTANT”本身;您仍然可以:

CONSTANT = 'foo'

CONSTANT会更改,但是第一次会快速引发TypeErrorCONSTANT[0]稍后会在脚本中调用。

虽然…我想如果您在某个时候将其更改为

CONSTANT = [1,2,3]

现在您不会再收到TypeError了。嗯…

https://docs.scipy.org/doc/numpy/reference/generation/numpy.ndarray.setflags.html

You can wrap a constant in a numpy array, flag it write only, and always call it by index zero.

import numpy as np

# declare a constant
CONSTANT = 'hello'

# put constant in numpy and make read only
CONSTANT = np.array([CONSTANT])
CONSTANT.flags.writeable = False
# alternatively: CONSTANT.setflags(write=0)

# call our constant using 0 index    
print 'CONSTANT %s' % CONSTANT[0]

# attempt to modify our constant with try/except
new_value = 'goodbye'
try:
    CONSTANT[0] = new_value
except:
    print "cannot change CONSTANT to '%s' it's value '%s' is immutable" % (
        new_value, CONSTANT[0])

# attempt to modify our constant producing ValueError
CONSTANT[0] = new_value



>>>
CONSTANT hello
cannot change CONSTANT to 'goodbye' it's value 'hello' is immutable
Traceback (most recent call last):
  File "shuffle_test.py", line 15, in <module>
    CONSTANT[0] = new_value
ValueError: assignment destination is read-only

of course this only protects the contents of the numpy, not the variable “CONSTANT” itself; you can still do:

CONSTANT = 'foo'

and CONSTANT would change, however that would quickly throw an TypeError the first time CONSTANT[0] is later called in the script.

although… I suppose if you at some point changed it to

CONSTANT = [1,2,3]

now you wouldn’t get the TypeError anymore. hmmmm….

https://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.setflags.html


声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。