假设我有一个具有某些属性的类。如何最好地(在 Pythonic-OOP 中)访问这些属性?就像 obj.attr
?或者写 get 访问器?
这些东西被接受的命名风格是什么?
编辑:
您能否详细说明使用单下划线或双前导下划线命名属性的最佳做法?我在大多数模块中看到使用单个下划线。
如果已经问过这个问题(我有预感,虽然搜索没有带来结果),请指出它 - 我将关闭这个问题。
最佳答案
关于单下划线和双下划线:两者都表示相同的“隐私”概念。也就是说,人们会知道该属性(无论是方法还是“正常”数据属性或其他任何东西)不是对象公共(public) API 的一部分。人们会知道,直接触摸它会招来灾难。
最重要的是,双前导下划线属性(但不是单前导下划线属性)经过名称修改,以减少从子类或当前类之外的任何其他地方意外访问它们的可能性。您仍然可以访问它们,但不是那么简单。例如:
>>> class ClassA:
... def __init__(self):
... self._single = "Single"
... self.__double = "Double"
... def getSingle(self):
... return self._single
... def getDouble(self):
... return self.__double
...
>>> class ClassB(ClassA):
... def getSingle_B(self):
... return self._single
... def getDouble_B(self):
... return self.__double
...
>>> a = ClassA()
>>> b = ClassB()
您现在可以轻松访问
a._single
和 b._single
并获得 _single
由 ClassA
创建的属性:>>> a._single, b._single
('Single', 'Single')
>>> a.getSingle(), b.getSingle(), b.getSingle_B()
('Single', 'Single', 'Single')
但是试图访问
__double
a
上的属性或 b
实例直接不起作用:>>> a.__double
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: ClassA instance has no attribute '__double'
>>> b.__double
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: ClassB instance has no attribute '__double'
虽然在
ClassA
中定义了方法可以直接获取它(在任一实例上调用时):>>> a.getDouble(), b.getDouble()
('Double', 'Double')
在
ClassB
上定义的方法不能:>>> b.getDouble_B()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in getDouble_B
AttributeError: ClassB instance has no attribute '_ClassB__double'
就在那个错误中,您会得到有关正在发生的事情的提示。
__double
属性名称,当在类内部访问时,会被名称修改以包括正在访问它的类的名称。当 ClassA
尝试访问 self.__double
,它实际上在编译时变成了对 self._ClassA__double
的访问,同样适用于 ClassB
. (如果 ClassB
中的方法要分配给 __double
,为简洁起见未包含在代码中,因此它不会触及 ClassA
的 __double
而是创建一个新属性。)没有其他保护这个属性,所以如果你知道正确的名字,你仍然可以直接访问它:>>> a._ClassA__double, b._ClassA__double
('Double', 'Double')
那么为什么这是一个问题呢?
好吧,任何时候您想要继承和更改处理此属性的任何代码的行为都是一个问题。您要么必须直接重新实现与此双下划线属性相关的所有内容,要么必须猜测类名并手动修改名称。当这个双下划线属性实际上是一个方法时,问题会变得更糟:覆盖该方法或在子类中调用该方法意味着手动进行名称修改,或者重新实现调用该方法的所有代码以不使用双下划线名称.更不用说动态访问属性了,使用
getattr()
: 你也必须在那里手动处理。另一方面,因为属性只是简单地重写,它只提供表面的“保护”。任何一段代码仍然可以通过手动修改来获取属性,尽管这会使它们的代码依赖于您的类的名称,以及您努力重构您的代码或重命名您的类(同时仍然保持相同的用户可见)名称,Python 中的一种常见做法)会不必要地破坏他们的代码。他们还可以通过将它们的类命名为与您的类相同的名称来“欺骗”Python 为它们进行名称修改:注意如何在损坏的属性名称中不包含模块名称。最后,双下划线属性在所有属性列表和所有不注意跳过以(单)下划线开头的属性的自省(introspection)形式中仍然可见。
因此,如果您使用双下划线名称,请非常谨慎地使用它们,因为它们会变得非常不方便,并且永远不要将它们用于方法 或任何其他子类可能想要重新实现、覆盖或直接访问的内容 .并意识到双前导下划线名称修改并不能提供真正的保护。最后,使用单个前导下划线可以让您获得同样的 yield ,并减少(潜在的、 future 的)痛苦。使用单个前导下划线。
关于Python 对象属性 - 访问方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/165883/