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

4
以下两种方法哪一种被认为是最佳实践?它们都可以实现相同的结果。
class Foo():
    LABELS = ('One','Two','Three')

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

    @property
    def labels(self):
        return self.__labels
6个回答

5
如果您不需要自定义获取或设置行为,那么将某些内容设置为属性就没有意义。第一个版本更好。
此外,它们的行为并不完全相同。在Foo中,有一个类级别的标签属性,在Bar中则没有。引用Foo.LABELS将正常工作,引用Bar.labels将引发异常。

2
“PEP 8 Python 代码风格指南”提供了第三种方法,而“Python 之禅”也同意这一点。他们建议添加一个非常简单的模块,创建一个命名空间来定义常量。例如,“package/constants.py”的整个内容如下:
LABELS = ('One', 'Two', 'Three')

使用示例:

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

请注意,这里没有任何明确的“保护”常量。您可以尝试跳过各种障碍来获取常量... 或者您可以接受这样一个事实:任何您设置的保护都可以被真正想要绕过它的人绕过,然后引用那些关于自愿成年人的话语,并采用非常明智的观点,即在 ALL_CAPS 中的变量得到了足够开发人员的正确尊重,所以您不必担心强制执行您对常量的概念。

1
第一个变体表明,标签在类的所有实例上声明,...
class Foo():
    LABELS = ('One','Two','Three')

如果标签是恒定不变的,我会选择第一个,因为它更连贯。而第二个看起来像标签是特殊的,例如每个实例都有不同的标签。


1
实际上,它们不是在实例上声明的,而是静态变量。试图在实例上查找“LABELS”会在内部失败,但在类上查找时会成功。例如:http://ideone.com/qfWlbe - Noctis Skytower

0
这取决于变量使用的范围。我将类变量用作常量的引用,就像C语言中的#define SOMETHING一样。另一方面,实例变量可能对不同的实例具有不同的值。也许一个例子会更好地解释这个问题。
class ChessBoardTile:
    BLACK = 'black'
    WHITE = 'white'
    def __init__(self,clr):
        self._color = clr

t = ChessBoardTile(ChessBoardTile.BLACK)

0

我做了一个例子,试图说服你采取第二种方法,但是我自己也感到困惑了...

>>> 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中没有办法强制实现引用不可变性。因此,您真的应该采用第二种方法。


这实际上是一个属性分配的问题,而与属性本身无关。如果您想为属性获得独立的实例级值,请在__init__方法中分配它,而不是将其保留在类上。对于任何可能发生更改的内容,这通常是个好主意。但最好将其分配给常量值 - 只需在__init__中设置LABELS = ('One', 'Two', 'Three')即可。 - Peter DeGlopper

0

肯定是第一个,因为:

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

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

  • 正如其他人指出的那样,您可以要求Foo.LABELS,这可能是为常量添加结构。


为了参考,Python有枚举 - 2rs2ts

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接