在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)说不然!
最好的方法是什么?
答案 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
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。方式更好。 :)