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

在 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

如您所见,我有一个简单的“ private”属性“ _ attr”和一个访问它的属性。有很多代码可以声明一个简单的私有属性,我认为像这样声明所有属性是不尊重“ KISS”哲学的。

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

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

最好的办法是什么?

132436 次浏览

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

self.attr = 0 #Done

您的方法并没有以任何方式使 _ attr 私有化,它只是有点模糊。

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

在 Python 中,除非您需要属性的特殊行为,否则不需要将其隐藏在访问器方法之后。如果属性仅供内部使用,则在其前面加上下划线。

属性的好处在于,它们为您提供了一个非常酷的界面来使用它们。有时,基于其他属性(例如:。体重指数由体重和身高决定)。当然,界面的用户不必知道这一点。

我更喜欢这种方式,而不是像 Java 中那样使用显式的 getter 和 setter

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

在 Python 中没有私有属性,您无法保护它们,而且这从来不是一个真正的问题。那就不要。放松!:)

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

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

要使属性私有化,只需执行 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

“ 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

请看这个链接: https://docs.python.org/2/tutorial/classes.html

9.6. 私有变量和类-局部引用

Python 中不存在只能从对象内部访问的“私有”实例变量。然而,大多数 Python 代码都遵循一个约定: 名称前加下划线(例如 _ spam)应该被视为 API 的非公共部分(无论是函数、方法还是数据成员)。它应该被认为是一个实现细节,可以在没有通知的情况下进行更改。

由于类-私有成员有一个有效的用例(即避免名称与子类定义的名称的名称冲突) ,因此对这种机制的支持有限,称为名称错位。表单 _ _ spam 的任何标识符(至少两个前导下划线,至多一个后导下划线)在文本上被替换为 _ classname _ _ spam,其中 classname 是当前的类名,前导下划线被去掉。这种破坏是不考虑标识符的语法位置的,只要它发生在类的定义中。