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

tur*_*1ng 9 python method-resolution-order

class MyClass(object):
    pass

print MyClass.__mro__
print dir(MyClass)
Run Code Online (Sandbox Code Playgroud)

输出:

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

为什么__mro__没有列出dir()

Dan*_*sky 11

从Python文档:

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

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

  • 在研究 Python 的工作原理时,更完整的 *dir()* 会很好。然而,当只使用 Python 类和实例时,我们通常想看看提供了哪些数据和服务,而不是它们是如何实现的。因此,尽管很奇怪,但日常实用性胜过 dir-must-show-all-details 的纯度。 (2认同)

use*_*868 11

我最近想知道同样的事情.我正在寻找一个更符合"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
Run Code Online (Sandbox Code Playgroud)

原因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'>)
Run Code Online (Sandbox Code Playgroud)

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

现在,我们处在一个很好的地方,可以回答我们可以指望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!"它应该是什么,你会预期,因为这是如何蟒蛇定义类属性的作品.)您也可以覆盖__mro__,当你子类化类型来创建一个元类.如果你不覆盖它,它就会被继承,就像你没有覆盖的任何其他东西一样.(如果你创建的元类不是类型的子类,并且不是返回类型实例的函数,大概你已经确切地知道你做得很好,你不必担心这个,但是__mro__因为你没有继承,所以不会继承type)

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

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

所以直接修改__mro__应该像你期望的那样运行,就像修改mro那样.但是,通过覆盖函数通常更容易获得所需的行为.(想想你需要做些什么才能正确处理子类化.)