假设我有一个具有某些属性的类。(在 Pythonic-OOP 中)如何访问这些属性是最好的?就像 obj.attr ?或者也许编写 get 访问器?此类事物可接受的命名风格是什么?

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


如果这个问题已经被问过(我有预感,尽管搜索没有带来结果),请指出它 - 我将关闭这个问题。

有帮助吗?

解决方案

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

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

如果你确实发现自己需要能够编写getter和setter,那么你想要寻找的是“python类属性”。和 Ryan Tomayko的文章 Getters / Setters / Fuxors 是一个很好的起点(虽然有点长)

其他提示

关于单下划线和双前导下划线:两者都表明了相同的“隐私”概念。也就是说,人们会知道该属性(无论是方法还是“普通”数据属性或其他任何内容)不是对象的公共 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._singleb._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 为他们进行名称修改:请注意,损坏的属性名称中不包含模块名称。最后,双下划线属性在所有属性列表和所有形式的内省中仍然可见,这些内省不注意跳过以 ( 开头的属性单身的) 下划线。

所以, 如果 您使用双下划线名称,并且非常谨慎地使用它们,因为它们可能会变得非常不方便,并且永远不要将它们用于方法 或子类可能想要重新实现、覆盖或直接访问的任何其他内容. 。并意识到双前导下划线名称修改提供了 没有真正的保护. 。最后,使用一个前导下划线会给你带来同样多的好处,并给你带来更少的(潜在的、未来的)痛苦。使用单个前导下划线。

  

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

单下划线并不意味着python有什么特别之处,这只是最好的做法,告诉“嘿,你可能不想访问它,除非你知道你在做什么”。然而,双下划线使python mangle成为内部名称,只能从定义它的类中访问它。

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

PEP 8 中阅读更多内容,尤其是“命名约定” ;部分。

我认为大多数人只是直接访问它们,不需要get / set方法。

>>> class myclass:
...     x = 'hello'
...
>>>
>>> class_inst = myclass()
>>> class_inst.x
'hello'
>>> class_inst.x = 'world'
>>> class_inst.x
'world'

顺便说一句,你可以使用dir()函数来查看你的实例附加了哪些属性/方法:

>>> dir(class_inst)
['__doc__', '__module__', 'x']

两个主要的下划线,“__”用于使属性或函数私有。 对于其他公约,请参阅PEP 08: http://www.python.org/dev/peps/pep-0008/

Python不需要从一开始就定义访问器,因为将属性转换为属性是快速而轻松的。请参阅以下内容以进行生动的演示:

从成瘾中恢复

在python中没有真正意义上的getter / setter,你无论如何都无法保护东西,如果在获取/设置属性时需要执行一些额外的代码,请查看属性()内置(python -c'帮助(属性)')

有些人使用getter和setter。根据您使用的编码样式,您可以将它们命名为getSpam和seteggs。但您也可以只读取属性或仅指定属性。这有点尴尬。一种方法是覆盖

> __getattr__

> __setattr__

方法

编辑:

虽然我的回答仍然是正确的,但是当我意识到这一点时,它是不对的。有更好的方法在python中创建访问器并且不是很尴尬。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top