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__在任何情况下都保持不变.
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那样.但是,通过覆盖函数通常更容易获得所需的行为.(想想你需要做些什么才能正确处理子类化.)