Python中的“public”或“private”属性?什么是最好的方法?

时间:2010-12-29 16:41:39

标签: python oop properties encapsulation

在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”哲学来声明所有属性。

那么,如果我不需要特定的getter / setter / deleter,为什么不将我的所有属性声明为公共属性?

我的回答是: 因为封装原则(OOP)说不然!

最好的方法是什么?

9 个答案:

答案 0 :(得分:77)

通常,Python代码努力遵守Uniform Access Principle。具体而言,接受的方法是:

  • 直接公开您的实例变量,例如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会对名称进行一些模糊处理。但是,仍然可以通过其混淆的名称从类外部访问该变量。这不是真正私密的。它只是......更不透明。并且有反对使用双下划线的有效论据;首先,它可以使调试更加困难。

答案 1 :(得分:14)

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

答案 2 :(得分:9)

很简单,OOP原则是错误的。为什么这是一个长期的讨论,导致火焰战争,可能是该网站的主题。 : - )

在Python中没有私有属性,你无法保护它们,这绝不是一个真正的问题。所以不要。简单! :)

接下来的问题是:你是否应该有一个领先的下划线。在你在这里的例子中你绝对不应该。 Python中的一个主要下划线是一种约定,表明某些内容是内部的,而不是API的一部分,并且您应该自担风险使用它。这显然不是这里的情况,但它是一种常见且有用的惯例。

答案 3 :(得分:6)

Python没有公共OR私有属性。所有代码都可以访问所有属性。

self.attr = 0 #Done

你的方法不会以任何方式使_attr私有,这只是一点混淆。

答案 4 :(得分:4)

正如其他人所说,Python中的私有属性仅仅是一种约定。在绑定,修改或删除属性时,应使用property语法进行特殊处理。 Python的优点在于你可以通过使用普通的属性绑定开始,例如self.attr = 0,如果在以后你决定要将attr的值限制为0 <= attr <=100,你可以使attr成为一个属性并定义一个方法,以确保此条件为真,而无需更改任何用户代码。

答案 5 :(得分:4)

请参阅此链接:https://docs.python.org/2/tutorial/classes.html

9.6。私有变量和类本地引用

Python中不存在除对象内部之外无法访问的“私有”实例变量。但是,大多数Python代码都遵循一个约定:前缀为下划线的名称(例如_spam)应被视为API的非公共部分(无论是函数,方法还是数据成员) 。它应被视为实施细节,如有更改,恕不另行通知。

由于类私有成员有一个有效的用例(即为了避免名称与子类定义的名称冲突),对这种称为名称修改的机制的支持有限。形式__spam的任何标识符(至少两个前导下划线,最多一个尾随下划线)在文本上用_classname__spam替换,其中classname是当前类名,其中前导下划线被剥离。只要它出现在类的定义中,就可以在不考虑标识符的句法位置的情况下完成这种修改。

答案 6 :(得分:1)

要将属性设为私有,您只需执行self.__attr

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

答案 7 :(得分:0)

在Python中,除非您需要从属性中获取特殊行为,否则无需将其隐藏在访问器方法之后。如果某个属性仅供内部使用,请在前面添加下划线。

答案 8 :(得分:0)

关于属性的好处是它们为您提供了一个非常酷的界面。有时,基于其他一些属性(即BMI由重量和高度定义)导出属性是很方便的。当然,界面的用户不必知道这一点。

我喜欢这种方式,而不是像Java那样有明确的getter和setter。方式更好。 :)