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()
?
答案 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.append
或type.mro
的方式相同,对于这个问题。
MyClass.__mro__ = "Hello world!"
不会导致错误。它不会影响type
中定义的方法解析顺序。因此,如果您尝试修改该行为,它可能没有您期望的效果。 (它所做的是使MyClass(*requiredparameters).__mro__
成为"Hello World!"
,这应该是你所期望的,因为这是定义类在python中工作的方式。)你也可以覆盖{{1}当你是子类化类型来创建元类时。如果你不覆盖它,它就会被继承,就像你没有覆盖的任何其他东西一样。 (如果你创建的元类不是类型的子类,并且不是返回类型实例的函数,大概你已经确切地知道你做得很好,你不必担心这个,但是__mro__
不会被继承,因为你没有继承__mro__
)
根据文档(描述超级行为):
该类型的__mro__属性列出了getattr()和super()使用的方法解析搜索顺序。该属性是动态的,只要更新继承层次结构就可以更改。
因此,直接修改type
的行为应该与您期望的一样,与修改__mro__
的方式大致相同。但是,通过覆盖函数通常更容易获得所需的行为。 (想想你需要做些什么才能正确处理子类化。)