14
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()

4

2 回答 2

21

我最近也在想同样的事情。我正在寻找一个更符合“Python 的实现原因mro/__mro__不被列出的原因dir?我可以信任dir列出什么?”的答案。而不仅仅是“Python 的文档如何证明不包含mroin是合理的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.append现在或type.mro现在相同的方式设置。

MyClass.__mro__ = "Hello world!"不会导致错误。它根本不会影响type. 因此,如果您尝试修改该行为,它可能不会产生您期望的效果。(它所做的是导致MyClass(*requiredparameters).__mro__应该"Hello World!"是您所期望的,因为这是在 python 中定义类的属性的方式。)您也可以__mro__在子类化类型时覆盖以创建元类。如果你不覆盖它,它就会被继承,就像你不覆盖的任何其他东西一样。(如果您要创建的元类不是类型的子类,也不是返回类型实例的函数,那么您可能已经清楚地知道自己做得很好,因此您不必担心这一点,但__mro__不会被继承,因为你没有子类化type

根据文档(描述 super 的行为):

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

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

于 2013-08-03T18:27:49.297 回答
8

来自 Python 文档:

因为提供 dir() 主要是为了方便在交互式提示下使用,所以它尝试提供一组有趣的名称,而不是尝试提供一组严格或一致定义的名称,并且它的详细行为可能会随版本而变化。例如,当参数是类时,元类属性不在结果列表中。

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

于 2013-05-21T06:38:25.937 回答