我应该在Python中使用“public”属性还是“public”属性?

76 投票
9 回答
153465 浏览
提问于 2025-04-16 09:10

在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 个回答

21

简单来说,面向对象编程的原则其实是有问题的。为什么会这样,这个话题很复杂,讨论起来可能会引发争论,而且可能不太适合这个网站。:-)

在Python中,没有所谓的私有属性,你无法保护它们,但这其实从来都不是个大问题。所以就这样吧,简单!:)

接下来就是一个问题:你是否应该在属性前加下划线。根据你这里的例子,绝对不需要。在Python中,前面加下划线是一种约定,表示这个东西是内部使用的,不是API的一部分,使用它要自担风险。显然在这里并不适用,但这是一个常见且有用的约定。

41

这个“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
115

通常情况下,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会对这个名字做一些模糊处理。虽然这个变量仍然可以通过它模糊后的名字在类外访问,但它并不是真正的私有。它只是看起来... 更复杂一些。而且,使用双下划线也有一些反对的理由;比如,它可能会让调试变得更困难。

撰写回答