【问题标题】:Python object attributes - methodology for accessPython 对象属性 - 访问方法
【发布时间】:2008-10-03 06:18:37
【问题描述】:

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

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


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

【问题讨论】:

  • 你说的是属于类(和所有对象)的类级属性还是类中定义的实例级属性?我认为您的问题混淆了类和对象。
  • 我的意思是对象属性(实例级),而不是类属性

标签: python oop object attributes


【解决方案1】:

关于单下划线和双下划线:两者都表示“隐私”的相同概念。也就是说,人们会知道属性(无论是方法还是“普通”数据属性或其他任何东西)不是对象的公共 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 属性名称,当在一个类中访问时,被名称修改以包含它正在被访问的类的名称​​in。当ClassA 尝试访问self.__double 时,它实际上在编译时变成了self._ClassA__double 的访问,对于ClassB 也是如此。 (如果ClassB 中的一个方法要赋值给__double,为简洁起见不包含在代码中,因此它不会触及ClassA__double,而是创建一个新属性。)没有其他保护这个属性,所以如果你知道正确的名字,你仍然可以直接访问它:

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

那么为什么会出现这个问题?

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

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

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

【讨论】:

  • 感谢您的出色回答。我希望我能两次投票给你 :-)
  • 这是一个很好的解释。感谢您的提示!
  • 很好的答案,除了 python 中的“getter 和 setter 是邪恶的”。
  • 我不确定你在说什么。 getter 和 setter 有它们的用途;这就是为什么我们有property()。访问控制不是其中之一。
【解决方案2】:

普遍接受的做事方式就是使用简单的属性,就像这样

>>> 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 是一个很好的起点(虽然有点长)

【讨论】:

  • 感谢您的链接 - 这是一篇与我的问题非常相关的优秀文章。
【解决方案3】:

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

单下划线对 python 没有任何特殊意义,它只是最佳实践,告诉“嘿,除非你知道自己在做什么,否则你可能不想访问它”。然而,双下划线使 python 在内部修改名称,使其只能从定义它的类访问。

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

PEP 8 中了解更多信息,尤其是“命名约定”部分。

【讨论】:

    【解决方案4】:

    我认为大多数人只是直接访问它们,不需要 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/

    【讨论】:

      【解决方案5】:

      Python 不需要从一开始就定义访问器,因为将属性转换为属性既快速又轻松。下面是生动的演示:

      Recovery from Addiction

      【讨论】:

        【解决方案6】:

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

        【讨论】:

          【解决方案7】:

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

          > __getattr__
          

          > __setattr__
          

          方法。

          编辑:

          虽然我的回答仍然正确,但我意识到这是不对的。在python中有更好的方法来制作访问器,而且不是很尴尬。

          【讨论】:

          • getattrsetattr 实际上是最不方便的方法。属性和 slots 通常更可取。
          • 我想用property会更简单
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2013-06-11
          • 2011-07-05
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多