我应该在Python中使用“public”属性还是“public”属性?
在Python中,我有一个简单的类示例:
class Foo:
self._attr = 0
@property
def attr(self):
return self._attr
@attr.setter
def attr(self, value):
self._attr = value
@attr.deleter
def attr(self):
del self._attr
如你所见,我有一个简单的“私有”属性“_attr”,还有一个属性可以用来访问它。为了声明这样一个简单的私有属性,我写了很多代码,我觉得这样做不符合“KISS”原则(保持简单,愚蠢)。
那么,如果我不需要特定的获取器、设置器或删除器,为什么不把我的所有属性都声明为公共属性呢?
我的回答是:因为封装原则(面向对象编程)是这样说的!
那么,最好的做法是什么呢?
9 个回答
简单来说,面向对象编程的原则其实是有问题的。为什么会这样,这个话题很复杂,讨论起来可能会引发争论,而且可能不太适合这个网站。:-)
在Python中,没有所谓的私有属性,你无法保护它们,但这其实从来都不是个大问题。所以就这样吧,简单!:)
接下来就是一个问题:你是否应该在属性前加下划线。根据你这里的例子,绝对不需要。在Python中,前面加下划线是一种约定,表示这个东西是内部使用的,不是API的一部分,使用它要自担风险。显然在这里并不适用,但这是一个常见且有用的约定。
这个“dunder”(双下划线,__
)前缀可以阻止直接访问属性,除了通过特定的方法来访问。
class Foo():
def __init__(self):
self.__attr = 0
@property
def attr(self):
return self.__attr
@attr.setter
def attr(self, value):
self.__attr = value
@attr.deleter
def attr(self):
del self.__attr
这里有一些例子:
>>> f = Foo()
>>> f.__attr # Not directly accessible.
Traceback (most recent call last):
File "<input>", line 1, in <module>
AttributeError: 'Foo' object has no attribute '__attr'
>>> '__attr' in f.__dir__() # Not listed by __dir__()
False
>>> f.__getattribute__('__attr') # Not listed by __getattribute__()
Traceback (most recent call last):
File "<input>", line 1, in <module>
AttributeError: 'Foo' object has no attribute '__attr'
>>> f.attr # Accessible by implemented getter.
0
>>> f.attr = 'Presto' # Can be set by implemented setter.
>>> f.attr
'Presto'
>>> f.__attr = 'Tricky?' # Can we set it explicitly?
>>> f.attr # No. By doing that we have created a
'Presto' # new but unrelated attribute, same name.
不过,你可以通过一种叫做名称改编的方式来访问这种属性(_classname__attribute
),这是Python在后台自动处理的:
>>> f._Foo__attr
0
>>> f.__getattribute__('_Foo__attr')
0
通常情况下,Python代码会遵循一个叫做统一访问原则的规则。具体来说,大家通常会这样做:
- 直接暴露你的实例变量,比如可以直接写
foo.x = 0
,而不是foo.set_x(0)
- 如果你需要把访问放在方法里,不管出于什么原因,可以使用
@property
,这样就能保持访问的方式不变。也就是说,foo.x = 0
现在会调用foo.set_x(0)
。
这种做法的主要好处是,调用者可以这样写:
foo.x += 1
尽管实际上代码可能是这样在运行:
foo.set_x(foo.get_x() + 1)
第一种写法看起来要简单得多,更容易理解。不过,使用属性后,你可以在开始时或者后面加上访问控制,就像第二种方法那样。
另外,注意以单个下划线开头的实例变量通常被认为是私有的。也就是说,下划线告诉其他开发者,你认为这个值是私有的,他们不应该直接去改动;不过,语言本身并没有阻止他们直接去改动。
如果你使用双下划线开头的变量(比如 __x
),Python会对这个名字做一些模糊处理。虽然这个变量仍然可以通过它模糊后的名字在类外访问,但它并不是真正的私有。它只是看起来... 更复杂一些。而且,使用双下划线也有一些反对的理由;比如,它可能会让调试变得更困难。