# 类继承机制与属性查找

# 单继承

由类继承关系如下:

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')

image

其字节码如下:

  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 字段,子类与父类被串在一起,形成一条继承链:

image

# 多继承

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')

image

 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__​属性访问:

image

# 属性查找顺序

有多继承场景如下:

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

image

>>> 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