我偶然发现了我不理解的双下划线名称的这种行为:
class A:
pass
class B:
pass
class C(A,B):
__id__ = 'c'
c = C()
print(C.__mro__) # print the method resolution order of class C
#print(c.__mro__) # AttributeError: 'C' object has no attribute '__mro__'
print(C.__id__) # print 'c'
print(c.__id__) # print 'c'
我知道 __name
的名称重整,它不适用于 __name__
(更多关于重载运算符方法)。 __id__
的行为就像一个普通的类变量,可以通过类名和实例访问。
但是,__mro__
只能通过类名访问,事实上我什至可以在 C: 中显式引入 __mro__
class C(A,B):
__mro__ = 'bla'
print(C.__mro__) # print the method resolution order of class C
print(c.__mro__) # print 'bla'
我想知道这种行为是 python 内部的魔法还是可以在常规 python 代码中实现。
[python 版本 3.4.3]
最佳答案
这与查找顺序有关。
出租 descriptors除此之外,python 首先检查对象 __dict__
以查找属性。如果找不到,它将查看对象的类和类的基来查找属性。如果在那里也找不到,则会引发 AttributeError。
这可能难以理解,所以让我们用一个简短的例子来说明这一点:
#!/usr/bin/python3
class Foo(type):
X = 10
class Bar(metaclass=Foo):
Y = 20
baz = Bar()
print("X on Foo", hasattr(Foo, "X"))
print("X on Bar", hasattr(Bar, "X"))
print("X on baz", hasattr(baz, "X"))
print("Y on Foo", hasattr(Foo, "Y"))
print("Y on Bar", hasattr(Bar, "Y"))
print("Y on baz", hasattr(baz, "Y"))
输出是:
X on Foo True
X on Bar True
X on baz False
Y on Foo False
Y on Bar True
Y on baz True
如您所见,X
已在元类 Foo
上声明。它可以通过 元类 的实例,类 Bar
访问,但不能在 Bar
的实例 baz
上访问,因为它只在Foo
的__dict__
中,而不在Bar
或baz的
。 Python 只检查“元”层次结构中的一个。__dict__
中
有关元类魔术的更多信息,请参阅问题 What is a metaclass in python? 的优秀答案.
然而,这不足以描述行为,因为 __mro__
对于 Foo
的每个 instance 都是不同的(也就是说,对于每个类(class))。
这可以使用描述符来实现。 在查找对象__dict__
属性名称之前,python 检查类的__dict__
及其基类以查看是否存在描述符分配给名称的对象。描述符是任何具有 __get__
method 的对象.如果是这种情况,则调用描述符对象的 __get__
方法并从属性查找中返回结果。通过将描述符分配给元类的属性,可以实现所看到的行为:描述符可以根据 instance 参数返回不同的值,但是属性只能通过类访问,并且元类,而不是类的实例。
描述符的主要示例是 property
.下面是一个简单的示例,其中的描述符具有与 __mro__
相同的行为:
class Descriptor:
def __get__(self, instance, owner):
return "some value based on {}".format(instance)
class OtherFoo(type):
Z = Descriptor()
class OtherBar(metaclass=OtherFoo):
pass
other_baz = OtherBar()
print("Z on OtherFoo", hasattr(OtherFoo, "Z"))
print("Z on OtherBar", hasattr(OtherBar, "Z"))
print("Z on other_baz", hasattr(other_baz, "Z"))
print("value of Z on OtherFoo", OtherFoo.Z)
print("value of Z on OtherBar", OtherBar.Z)
输出是:
Z on OtherFoo True
Z on OtherBar True
Z on other_baz False
value of Z on OtherFoo some value based on None
value of Z on OtherBar some value based on <class '__main__.OtherBar'>
如您所见,OtherBar
和OtherFoo
都具有可访问的Z
属性,但是other_baz
没有.不过,对于每个 OtherFoo
实例,Z
可以有不同的值,也就是说,每个使用 OtherFoo
元类的类。
元类起初令人困惑,在使用描述符时更是如此。我建议阅读元类 linked question ,以及一般 python 中的描述符。
关于python - __mro__ 与其他双下划线名称有何不同?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38655863/