python - __mro__ 与其他双下划线名称有何不同?

标签 python syntax python-3.4

我偶然发现了我不理解的双下划线名称的这种行为:

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__中,而不在Barbaz的__dict__。 Python 只检查“元”层次结构中的一个

有关元类魔术的更多信息,请参阅问题 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'>

如您所见,OtherBarOtherFoo 都具有可访问的Z 属性,但是other_baz 没有.不过,对于每个 OtherFoo 实例,Z 可以有不同的值,也就是说,每个使用 OtherFoo 元类的类。

元类起初令人困惑,在使用描述符时更是如此。我建议阅读元类 linked question ,以及一般 python 中的描述符。

关于python - __mro__ 与其他双下划线名称有何不同?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38655863/

相关文章:

python - 根据字符串属性索引拆分 Pandas 数据框

Python 3.7 Authlib UnsupportedAlgorithmError

powershell - 如何在 Powershell 中执行相当于 $PROGPATH/program 的 bash 操作?

ios - 了解 Swift 平面 map 语法

python - Ctypes:分配 double** ,将其传递给 C,然后在 Python 中使用

python - 为 Holoviews 图中的每个数据点添加文本注释

python - 将 python 列表传递给 django 模板

sql - VB : Syntax error when trying to insert to database with SQL

python - 使用 Wordnik 将 python 3.4 应用程序部署到 bluemix 的问题

python - Python Pandas 中的 Zip 错误消息 - Anaconda