Python对象属性.访问方法

2024-09-28 17:27:39 发布

您现在位置:Python中文网/ 问答频道 /正文

假设我有一个具有某些属性的类。在Pythonic OOP中,访问这些属性的最佳方式是什么?就像obj.attr?或者编写get访问器? 这些东西的命名方式是什么?

编辑: 您能详细说明用单或双前导下划线命名属性的最佳实践吗?我在大多数模块中看到使用了一个下划线。


如果这个问题已经被问过了(我有预感,虽然搜索并没有带来结果),请指向它-我将关闭这个问题。


Tags: 模块obj编辑get属性方式pythonic命名
3条回答

人们普遍接受的做事方式就是使用简单的属性,比如

>>> class MyClass:
...     myAttribute = 0
... 
>>> c = MyClass()
>>> c.myAttribute 
0
>>> c.myAttribute = 1
>>> c.myAttribute
1

如果您确实发现自己需要能够编写getter和setter,那么您需要寻找的是“python类属性”,并且Ryan Tomayko's article on Getters/Setters/Fuxors是一个很好的起点(尽管有点长)

Edit: Can you elaborate on the best-practices of naming attributes with a single or double leading underscore ? I see in most modules that a single underscore is used.

单下划线对python来说并不意味着什么特别的东西,它只是最好的实践,告诉“嘿,除非你知道自己在做什么,否则你可能不想访问它”。但是,双下划线使python在内部损坏名称,使其只能从定义它的类访问。

双前导下划线和尾随下划线表示一个特殊函数,例如__add__,在使用+运算符时调用该函数。

阅读PEP 8中的更多内容,特别是“命名约定”部分。

关于单前导下划线和双前导下划线:两者都表示相同的“隐私”概念。也就是说,人们会知道属性(无论是方法或“普通”数据属性还是其他什么)不是对象的公共API的一部分。人们会知道,直接触碰它就是招致灾难。

除此之外,双前导下划线属性(而不是单前导下划线属性)是name mangled使从子类或当前类之外的任何地方意外访问它们的可能性降低。你仍然可以访问它们,但不是那么简单。例如:

>>> 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._singleb._single,并获取由ClassA创建的_single属性:

>>> a._single, b._single
('Single', 'Single')
>>> a.getSingle(), b.getSingle(), b.getSingle_B()
('Single', 'Single', 'Single')

但是试图直接访问ab实例上的__double属性将不起作用:

>>> 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时,实际上它在compiletime将变成self._ClassA__double的访问,对ClassB也是如此。(如果ClassB中的方法要分配给__double,为了简洁起见,代码中没有包含该方法,因此它不会触及ClassA__double,而是创建一个新的属性。)此属性没有其他保护,因此,如果知道正确的名称,您仍然可以直接访问它:

>>> a._ClassA__double, b._ClassA__double
('Double', 'Double')

那为什么这是个问题?

好吧,当您想继承和更改处理此属性的任何代码的行为时,这都是一个问题。您要么必须直接重新实现与此双下划线属性相关的所有内容,要么必须猜测类名并手动更改名称。当这个双下划线属性实际上是一个方法时,问题变得更糟:重写方法或调用子类中的方法意味着手动损坏名称,或重新实现调用该方法的所有代码以不使用双下划线名称。更不用说使用getattr()动态地访问属性了:您还必须在那里手动地进行管理。

另一方面,由于该属性只是简单地重写,因此它只提供表面的“保护”。任何一段代码仍然可以通过手动损坏来获取属性,尽管这会使它们的代码依赖于您的类的名称,并且您这边重构代码或重命名您的类的努力(同时保持相同的用户可见名称,这是Python中的一种常见做法)将不必要地破坏它们的代码。它们还可以通过将它们的类命名为与您的类相同的名称来“诱骗”Python为它们进行名称损坏:注意损坏的属性名称中没有包含模块名称。最后,双下划线属性在所有属性列表和所有形式的内省中仍然可见,这些内省不需要跳过以(single下划线)开头的属性。

因此,如果使用双下划线名称,请非常谨慎地使用它们,因为它们可能会变得非常不方便,并且永远不要将它们用于方法或子类可能希望直接重新实现、重写或访问的任何其他项。并认识到双前导下划线名称管理提供了没有真正的保护。最后,使用一个单独的前导下划线可以获得同样多的效果,并减少(潜在的,未来的)痛苦。使用一个前导下划线。

相关问题 更多 >