假设我有一个具有某些属性的类.访问这些属性最好(在Pythonic-OOP中)意义如何?就像obj.attr
?或者也许写get accessors?这些东西的接受命名风格是什么?
编辑: 您能详细说明使用单个或双前导下划线命名属性的最佳实践吗?我在大多数模块中看到使用单个下划线.
如果这个问题已经被提出(我有预感,虽然搜索没有带来结果),请指出它 - 我将关闭这个.
关于单引号和双引号下划线:两者都表示"私有性"的相同概念.也就是说,人们会知道属性(无论是方法还是"普通"数据属性或其他任何东西)都不是对象的公共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 "", line 1, in AttributeError: ClassA instance has no attribute '__double' >>> b.__double Traceback (most recent call last): File " ", line 1, in AttributeError: ClassB instance has no attribute '__double'
虽然定义的方法ClassA
可以直接获取(当在任一实例上调用时):
>>> a.getDouble(), b.getDouble() ('Double', 'Double')
定义的方法ClassB
不能:
>>> b.getDouble_B() Traceback (most recent call last): File "", line 1, in File " ", 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为它们进行名称修改:注意在受损的属性名称中没有包含模块名称.最后,双下划线属性仍然可以在所有属性列表和所有形式的内省中看到单一)下划线.
因此,如果您使用双下划线名称,请极其谨慎地使用它们,因为它们可能会非常不方便,并且永远不会将它们用于方法或其他任何子类可能想要重新实现,覆盖或直接访问的内容.并意识到双重领先的下划线名称 - 错误提供没有真正的保护.最后,使用单个前导下划线同样可以赢得你,并为你提供更少(潜在的,未来的)痛苦.使用单个前导下划线.
普遍接受的做事方式就是使用简单的属性,就像这样
>>> class MyClass: ... myAttribute = 0 ... >>> c = MyClass() >>> c.myAttribute 0 >>> c.myAttribute = 1 >>> c.myAttribute 1
如果你确实发现自己需要能够编写getter和setter,那么你想要寻找的是"python类属性",而Ryan Tomayko关于Getters/Setters/Fuxors的文章是一个很好的起点(尽管有点长)
编辑:您能详细说明使用单个或双前导下划线命名属性的最佳实践吗?我在大多数模块中看到使用单个下划线.
单下划线并不意味着python有什么特别之处,这只是最好的做法,告诉"嘿,你可能不想访问它,除非你知道你在做什么".然而,双下划线使python mangle成为内部名称,只能从定义它的类中访问它.
双前导和尾随下划线表示一个特殊功能,例如__add__
在使用+运算符时调用.
阅读PEP 8中的更多内容,尤其是"命名约定"部分.