Python:我应该使用常量还是属性?

Python:我应该使用常量还是属性?,python,syntax,constants,Python,Syntax,Constants,以下哪两种方法被视为最佳实践?两者的效果相同 class Foo(): LABELS = ('One','Two','Three') class Bar(): def __init__(self): self.__labels = ('One','Two','Three') @property def labels(self): return self.__labels 如果您不需要自定义获取或设置行为,那么将某些内容设置为属

以下哪两种方法被视为最佳实践?两者的效果相同

class Foo():
    LABELS = ('One','Two','Three')

class Bar():
    def __init__(self):
        self.__labels = ('One','Two','Three')

    @property
    def labels(self):
        return self.__labels

如果您不需要自定义获取或设置行为,那么将某些内容设置为属性是没有意义的。第一个版本更好

class ChessBoardTile:
    BLACK = 'black'
    WHITE = 'white'
    def __init__(self,clr):
        self._color = clr

t = ChessBoardTile(ChessBoardTile.BLACK)

而且,它们的行为并不完全相同。在
Foo
中,标签有一个类级属性。在
栏中
,没有。引用
Foo.LABELS
将正常工作,引用
Bar.LABELS
将引发异常。

第一个变量表示在类的所有实例上声明标签

class Foo():
    LABELS = ('One','Two','Three')

而第二个看起来像是每个实例的标签都是特殊的。如果标签是常量,我会选择第一个,它更连贯。

这取决于变量使用的范围。我使用类变量作为常量的引用,很像C中的
#define SOMETHING
。另一方面,实例变量对于不同的实例可能有不同的值。也许一个例子可以更好地解释这一点

class ChessBoardTile:
    BLACK = 'black'
    WHITE = 'white'
    def __init__(self,clr):
        self._color = clr

t = ChessBoardTile(ChessBoardTile.BLACK)

我举了一个例子试图说服你采取第二种方法,但我感到困惑

>>> class Foo():
...     LABELS = ('One','Two','Three')
... 
>>> Foo.LABELS
('One', 'Two', 'Three')
>>> Foo.LABELS = (1,2,3)
>>> Foo.LABELS
(1, 2, 3)
>>> f = Foo()
>>> g = Foo()
>>> f.LABELS = ('a','b','c')
>>> g.LABELS
(1, 2, 3)
>>> Foo.LABELS
(1, 2, 3)
>>> f.LABELS
('a', 'b', 'c')
“怎么回事?”我心想。然后我意识到行为取决于对象ID

>>> id(Foo.LABELS)
4562309280
>>> id(g.LABELS)
4562309280
>>> id(f.LABELS)
4562068336
>>> ('a','b','c') is ('a','b','c')
False
>>> Foo.LABELS = (4,5,6)
>>> g.LABELS
(4, 5, 6)
>>> f.LABELS
('a', 'b', 'c')
>>> id(Foo.LABELS)
4562309200
>>> id(g.LABELS)
4562309200
>>> id(f.LABELS)
4562068336
所以,回到我最初的答案:不要采用第一种方法,除非你不在乎你的变量是否被重新赋值,因为你将得到的不是你所期望的。第一种方法使变量属于类,第二种方法使变量属于实例——但是如果有人在第一种情况下重新分配变量,您将得到一些非常奇怪的结果

推论-如果您的类方法只引用类的变量(即
Foo.LABELS
),那么您显然会得到您期望的结果,但是如果有人以不同的方式重复使用您的代码,那么谁知道他们会得到什么呢


推论#2-python中没有强制引用不变性的方法。所以你真的应该选择第二种方法。

肯定是第一种,因为:

  • 它更简单,更好地传达您的意图

  • 它可能使用更少的内存,因为它是只计算一次的类变量。而第二个示例一遍又一遍地创建相同的元组,它可能不会被缓存(内部化)

  • 正如其他人所指出的,您可以请求Foo.label,它可能用于向常量添加结构

    • 提供了第三种方式,并且得到了客户的同意

      他们建议添加一个非常简单的模块,创建一个名称空间来定义常量

      整个内容,例如包/常量.py:

      LABELS = ('One', 'Two', 'Three')
      
      用法示例:

      from package.constants import LABELS
      print(LABELS)  # -> ('One', 'Two', 'Three')
      

      注意,这里没有任何明确的常量“保护”。你可以通过跳环来获得常数。。。或者你可以接受,你所采取的任何保护措施都可能被真正想保护你的人忽视,然后,只要重新引用那些关于成年人同意的内容,并遵循一个非常明智的概念,即
      所有的_CAPS
      中的变量都受到足够多的开发人员的适当尊重,您就不必担心强制执行常量的概念。

      这实际上是一个属性分配到哪里的问题,而不是分配到哪里的问题与属性无关。如果希望属性具有独立的实例级值,请在
      \uuuu init\uuuu
      方法中分配它,而不是将其留在类中。通常,这对于任何可能发生变化的事情都是个好主意。但最好将其赋值为常量-只需在
      \uuuu init\uuuuu
      中设置
      标签=('1'、'2'、'3')
      。为了便于参考,实际上,它们不会在实例上声明。这些是静态变量。尝试在实例上查找
      标签
      在内部失败,但在类上查找时成功。例如: