Python对象属性-访问方法

时间:2020-03-06 15:03:50  来源:igfitidea点击:

假设我有一个带有某些属性的类。如何最好地(在Pythonic-OOP中)访问这些属性?就像obj.attr一样吗?还是写get访问器?
这些东西可接受的命名方式是什么?

编辑:
我们能否详细说明使用单引号或者双引号下划线命名属性的最佳做法?我在大多数模块中看到使用单个下划线。

如果已经问过这个问题(虽然搜索没有带来结果,但我有一个直觉,请指出),然后我将结束该问题。

解决方案

我认为大多数只需要直接访问它们,不需要获取/设置方法。

>>> 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不需要从一开始就定义访问器,因为将属性转换为属性既快捷又轻松。请参见以下内容以进行生动的演示:

从成瘾中恢复

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

> __getattr__

> __setattr__

方法。

编辑:

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

通常公认的做事方式只是使用简单的属性,就像这样

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

如果我们确实需要编写getter和setters,那么我们要查找的是" python类属性"和Ryan Tomayko在上的文章。
Getters / Setters / Fuxors是一个很好的起点(尽管时间很长)

在python中进行getter / setters并没有真正的意义,无论如何我们都无法保护内容,如果在获取/设置属性时需要执行一些额外的代码,请查看内置的property()(python -c'help(property )')

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在内部改变了名称,使其只能从定义它的类中访问。

双引号AND尾部下划线表示特殊功能,例如__add__,在使用+运算符时会调用。

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

关于单引号和双引号下划线:两者都表示相同的"私密性"概念。也就是说,人们会知道该属性(无论是方法还是"常规"数据属性或者其他任何属性)都不是对象的公共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并获取由ClassA创建的_single属性:

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

但是尝试直接访问a或者b实例上的__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时,它实际上变为-at compiletime-进入对self._ClassA__double的访问,同样对ClassB的访问。 (如果将ClassB中的方法分配给__double,为了简洁起见,该方法不包含在代码中,那么它就不会碰到ClassA的__double,而是创建了一个新属性。)此属性,因此,如果我们知道正确的名称,仍可以直接访问它:

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

那么为什么这是一个问题呢?

嗯,任何时候只要我们想继承并更改处理此属性的任何代码的行为,这都是一个问题。我们或者必须重新实现直接接触此double-underscore属性的所有内容,或者必须猜测类名称并手动更改名称。当这个double-underscore属性实际上是一个方法时,问题变得更糟:覆盖该方法或者在子类中调用该方法意味着手动进行名称处理,或者重新实现所有调用该方法的代码以不使用double-underscore名称。更不用说使用getattr()动态访问属性了:我们也必须在那里手动处理。

另一方面,由于该属性仅被简单地重写,因此它仅提供表面的"保护"。任何代码片段仍可以通过手动处理来获取属性,尽管这将使其代码取决于类的名称,并可以根据意愿来重构代码或者重命名类(同时仍保持相同的用户可见性)名称,这是Python中的常见做法)会不必要地破坏其代码。他们还可以通过将类命名为与类相同的方式来"欺骗" Python进行名称处理:请注意,在经过损坏的属性名称中不包含模块名称。最后,double-underscore属性在所有属性列表和所有内省形式中仍然可见,而不必考虑跳过以(单个)下划线开头的属性。

因此,如果我们使用双下划线名称,请尽量少使用它们,因为它们可能会带来很大的不便,并且切勿将它们用于方法或者子类可能希望重新实现,覆盖或者直接访问的任何其他内容。并意识到,双引号的下划线名称改编并不能提供真正的保护。最后,使用单个下划线就可以为我们赢得同等的收益,并减轻我们(潜在的,将来的)痛苦。使用单个前划线。