python多继承的问题

__new__

考虑以下代码的输出结果:

class A:
    print("AAAAA")

    def __init__(self):
        print("a init")
        ...

    def __new__(cls, *args, **kwargs):
        print("a new")
        return super(A, cls).__new__(cls)

a = A()

结果为:

__new__ 是object的一个静态方法,用于创建对象实例,__init__ 用于初始化对象。而上面的 “AAAAA” 则是在加载类的过程中就执行了

多继承下的实例化过程

考虑以下代码输出:

class A:
    print("AAAAA")

    a_attr = "a"

    def __init__(self):
        print("a init")
        super(A, self).__init__()    # 这里的super指的是谁?
        ...

    def __new__(cls, *args, **kwargs):
        print("a new")
        return super(A, cls).__new__(cls)

class B:
    print("BBBBB")

    def __init__(self):
        print("b init")
        super(B, self).__init__()    # 这里的super指的是谁?
        ...

    def __new__(cls, *args, **kwargs):
        print("b new")
        return super(B, cls).__new__(cls)

    def bm(self):
        print("bbb")


class C(A, B):
    print('CCCCC')
    def __init__(self):
        print('c init')
        super(C, self).__init__()    # 这里的super指的是谁?

    def __new__(cls, *args, **kwargs):
        print("c new")
        return super(C, cls).__new__(cls)
    ...


c = C()
print(C.mro())

上述代码中,A和B相互独立,C继承A、B,实例化C(没有调用super的实例化),可以看到,在对C进行实例化后,并没有立即进行初始化,而是先分别实例化了A和B。这说明,对象初始化之前需要先对该类及其父类实例化,这是符合直觉的,因为你可能在c的初始化过程中调用a或b的属性(赋零值的属性,暂未被显式初始化)或方法。

问题就在于,如果在C的 __init__ 中调用super对父类初始化,那么这个super到底指谁呢?

这就涉及到python的 Method Resolution Order(MRO)机制 了,这里的super指的其实是a

MRO是一个列表,它反映了多继承下,方法的查找顺序,可以通过 class.mro() 方法进行查看

print(C.mro())

# [<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]

该列表中,下一个元素就是上一个元素的super,所以,对C来说,其super指的就是A,当初始化代码执行到A时,对A来说,其super指的就是B,对B来说,其super自然就是object了。这一点就是不符合直觉的,并且会造成很多很多的问题,例如钻石型继承关系、重载等问题,在java中,只允许单继承就不会有这种问题了。

当然,上述C、A、B的 __init__ 不执行super的初始化也是没关系的,因为类在实例化时父类也被实例化过了,其属性(不是定义在init函数中的,例如上述代码中的 a_attr)也被赋予了零值或初始值,也就可以正常调用父类的属性和方法的

Leave a Comment