# 类继承机制与属性查找
# 单继承
由类继承关系如下:
class Animal:
def run(self):
print('running')
class Dog(Animal):
def yelp(self):
print('woof')
def play(self):
print('playing')
class Sleuth(Dog):
def yelp(self):
print('WOOF!')
def hunt(self):
print('hunting')
其字节码如下:
1 0 LOAD_BUILD_CLASS
2 LOAD_CONST 0 (<code object Animal at 0x109b90810, file "", line 1>)
4 LOAD_CONST 1 ('Animal')
6 MAKE_FUNCTION 0
8 LOAD_CONST 1 ('Animal')
10 CALL_FUNCTION 2
12 STORE_NAME 0 (Animal)
5 14 LOAD_BUILD_CLASS
16 LOAD_CONST 2 (<code object Dog at 0x109bd1c90, file "", line 5>)
18 LOAD_CONST 3 ('Dog')
20 MAKE_FUNCTION 0
22 LOAD_CONST 3 ('Dog')
24 LOAD_NAME 0 (Animal)
26 CALL_FUNCTION 3
28 STORE_NAME 1 (Dog)
11 30 LOAD_BUILD_CLASS
32 LOAD_CONST 4 (<code object Sleuth at 0x109bd1a50, file "", line 11>)
34 LOAD_CONST 5 ('Sleuth')
36 MAKE_FUNCTION 0
38 LOAD_CONST 5 ('Sleuth')
40 LOAD_NAME 1 (Dog)
42 CALL_FUNCTION 3
44 STORE_NAME 2 (Sleuth)
46 LOAD_CONST 6 (None)
48 RETURN_VALUE
__build_class__函数将基类保存于 PyTypeObject 类型对象的 tp_base 字段中,可以通过__base__属性访问。通过 tp_base 字段,子类与父类被串在一起,形成一条继承链:
# 多继承
class Animal:
def run(self):
print('running')
class Dog(Animal):
def yelp(self):
print('woof')
def play(self):
print('playing')
class Sleuth(Dog):
def yelp(self):
print('WOOF!')
def hunt(self):
print('hunting')
class SnifferDog(Dog):
def search(self):
print('searching')
class PoliceDog(Sleuth, SnifferDog):
def patrol(self):
print('patroling')
21 62 LOAD_BUILD_CLASS
64 LOAD_CONST 8 (<code object PoliceDog at 0x106b81c00, file "", line 21>)
66 LOAD_CONST 9 ('PoliceDog')
68 MAKE_FUNCTION 0
70 LOAD_CONST 9 ('PoliceDog')
72 LOAD_NAME 2 (Sleuth)
74 LOAD_NAME 3 (SnifferDog)
76 CALL_FUNCTION 4
78 STORE_NAME 4 (PoliceDog)
80 LOAD_CONST 10 (None)
82 RETURN_VALUE
注意到,字节码在调用 __build_class__函数前,将两个父类 Sleuth 以及 SnifferDog 作为参数按顺序压入栈中。因此,这段字节码等价于:
__build_class__(func, 'PoliceDog', Sleuth, SnifferDog)
这样一来, __build_class__函数的 bases 参数将拿到一个由直接父类组成的元组!
在多继承场景,光一个 tp_base 字段不足以保存多个基类, __build_class__函数应该将 bases 元组保存在另一个字段中。再次回到 PyTypeObject 源码,不难找出字段 tp_bases 字段,它便保存着基类列表,可以通过__bases__属性访问:
# 属性查找顺序
有多继承场景如下:
class A:
pass
class B(A):
pass
class C(B):
pass
class D(A):
pass
class E(D):
pass
class F(A):
pass
class G(C, E, F):
pass
>>> G.__mro__
(<class '__main__.G'>, <class '__main__.C'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.D'>, <class '__main__.F'>, <class '__main__.A'>, <class 'object'>)
属性查找的顺序就是对类继承关系图的 拓扑排序 !拓扑排序恰好可以保证有向图中有连接关系节点间的先后顺序。
- 拓扑排序是深度优先的 -
G C B E ... - 分支遍历顺序由基类列表定义顺序决定 -
G C .. E .. F .. A
← 类机制概述 Python程序执行过程 →