为什么__mro__没有显示在dir(MyClass)中?

时间:2013-05-21 06:29:07

标签: python method-resolution-order

class MyClass(object):
    pass

print MyClass.__mro__
print dir(MyClass)

输出:

(<class '__main__.MyClass'>, <type 'object'>)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']

为什么__mro__未列在dir()

2 个答案:

答案 0 :(得分:10)

来自Python文档:

  

因为提供dir()主要是为了方便使用   交互式提示,它试图提供一组有趣的名称   不只是它试图提供严格或一致定义的集合   名称及其详细行为可能会在不同版本中发生变化。对于   例如,元类属性不在结果列表中   论证是一个阶级。

__mro__是一个只读属性,用于确定方法解析,以防您的类从多个基类继承。如果您希望自定义此行为,则应使用元类(一种用于创建类实例的特殊对象)来覆盖mro()方法。 __mro__在任何情况下都保持不变。

答案 1 :(得分:9)

我最近想知道同样的事情。我正在寻找一个更符合“Python的实现原因导致mro / __mro__不在dir中列出的答案?我还能信任dir列出什么? ?”而不仅仅是“Python的文档如何证明mro中不包含dir?”当我对编程语言的行为的期望与其实际行为不匹配时,我不喜欢它,因为这意味着我对语言的理解是不正确的 - 除非它只是像urllib2.escape这样的错误。所以我挖了一下,直到找到了答案。

上面评论中的文档中引用的adolfopa行很好地解释了dir的行为。

  

“如果对象是类型或类对象,则列表包含其属性的名称,并且递归地包含其基础的属性。”

这是什么意思? dir递归地从类的__dict__及其每个超类“__dict__收集属性。

set(dir(object)) == set(dict(object.__dict__).keys() #True



class A(object):
    ...

class B(object):
    ...

class C(B):
    ...

class D(C,A):
    ...

set(dir(D)) == set(D.__dict__.keys()) + set(C.__dict__.keys()) \
   + set(B.__dict__.keys()) + set(A.__dict__.keys()) \
   + set(object.__dict__.keys()) #True

dir(object)未列出__mro__ / mro的原因是它们不是对象的属性。它们是type的属性。每个没有定义它自己的__metaclass__的类都是type的实例。大多数元类都是type的子类。这种元类的实例同样是类型的实例。 MyClass.__mro__type.__getattribute__(MyClass,'__mro__')相同。

Python实现类的方式必然会对dir的工作方式产生轻微的异常。

通常dir(MyClass) == dir(MyClass(*requiredparameters)) #True

然而,dir(type) == dir(type(*requiredparameters)) #False,但唯一的方法可能是type.__dict__dir相同。这显然不是dir的目的。

但是等等! dir是由递归求和创建的,为什么我们不能只更改它的最后一部分,以便dir(object)不再只是object.__dict__.keys()而是变为object.__dict__.keys() + type.__dict__.keys()。这样,它会有mro / __mro__以及类对象具有的所有其他属性?啊,但那些是类对象的属性,而不是类。嗯,有什么区别?

考虑

list.__mro__ #(<type 'list'>, <type 'object'>)

尽管

[].__mro__
# Traceback (most recent call last):
#  File "<stdin>", line 1, in <module>
# AttributeError: 'list' object has no attribute '__mro__'

现在,我们处于一个很好的位置,可以回答dir列出的内容,以及dir无法列出的内容。简单的答案是我们已经涵盖的答案。它以递归方式列出了类__dict__中的所有键以及每个超类“__dict__”中的所有键。例如,它还包括实例__dict__中的所有参数。要填写负空格,它不会列出__getattr__中定义的任何内容或__getattribute__中的任何内容,如果它不在__dict__中。它也没有列出类型/元类型的任何属性。


我认为应该指出的另一件事是:Dan的答案,即我写这篇文章时接受的答案,包含的信息不准确或至少具有误导性。

无法设置内置对象的属性,因此从某种意义上说,type.__mro__是“只读”,但仅与list.appendtype.mro的方式相同,对于这个问题。

MyClass.__mro__ = "Hello world!"不会导致错误。它不会影响type中定义的方法解析顺序。因此,如果您尝试修改该行为,它可能没有您期望的效果。 (它所做的是使MyClass(*requiredparameters).__mro__成为"Hello World!",这应该是你所期望的,因为这是定义类在python中工作的方式。)你也可以覆盖{{1}当你是子类化类型来创建元类时。如果你不覆盖它,它就会被继承,就像你没有覆盖的任何其他东西一样。 (如果你创建的元类不是类型的子类,并且不是返回类型实例的函数,大概你已经确切地知道你做得很好,你不必担心这个,但是__mro__不会被继承,因为你没有继承__mro__

根据文档(描述超级行为):

  

该类型的__mro__属性列出了getattr()和super()使用的方法解析搜索顺序。该属性是动态的,只要更新继承层次结构就可以更改。

因此,直接修改type的行为应该与您期望的一样,与修改__mro__的方式大致相同。但是,通过覆盖函数通常更容易获得所需的行为。 (想想你需要做些什么才能正确处理子类化。)