# 要调用父类中的方法,可以使用super完成 class A: def spam(self): print('A.spam') class B(A): def spam(self): print('B.spam') super().spam() # super的另一种常用方法是调用父类的__init__方法来确保父类被正确初始化了 class A: def __init__(self): self.x = 0 class B(A): def __init__(self): super().__init__() self.y = 1 # 还有一种情况就是,你覆写了类中的方法,此时你可以调用super函数来使用原方法 class Proxy: def __init__(self, obj): self._obj = obj def __getattr__(self, name): return getattr(self._obj, name) def __setattr__(self, name, value): if name.startwith('_'): super().__setattr__(name, value) else: setattr(self._obj, name, value) # Python中的类继承 # 有些人有坏习惯,在子类中直接调用父类的方法,这在大部分时候能行 class Base: def __init__(self): print('Base.__init__') class A(Base): def __init__(self): Base.__init__(self) print('A.__init__') # 但是如果这涉及到多重继承,那就会导致暴毙现象 class BaseV2: def __init__(self): print('Base.__init__') class A1(BaseV2): def __init__(self): BaseV2.__init__(self) print('A1.__init__') class A2(BaseV2): def __init__(self): BaseV2.__init__(self) print('A2.__init__') class B1(A1, A2): def __init__(self): A1.__init__(self) A2.__init__(self) print('B1.__init__') # 上面的继承关系就是标准的钻石继承,如果运行代码就会出现神奇魔法 b1 = B1() # 哇!Base的__init__函数被调用了两次!这在一些场景下可能会导致麻烦,但如果使用super(),那就不会有这么多事了 class BaseV3: def __init__(self): print('Base.__init__') class B1(BaseV3): def __init__(self): super().__init__() print('B1.__init__') class B2(BaseV3): def __init__(self): super().__init__() print('B2.__init__') class C1(B1, B2): def __init__(self): super().__init__() print('C1.__init__') # 这样的继承是健康的,每个init函数只被调用了一次 c1 = C1() # 这是为啥嘞?每个类Python都会计算得出一个方法解析顺序列表MRO,在这个列表中会简单的对所有基类进行线性排列 print(C1.__mro__) # MRO列表又是怎么确定的呢?Python使用了一种叫C3线性化处理的技术,这是一种归并排序,且满足三个约束 # 1.先检查子类再检查父类; 2.多个父类时按照列表顺序依次检查; 3.如果下一个待选的类出现了两个合法的选择,就从第一个父类中选取 # C3有点麻烦,但是计算的时候可以使用Python2.2更新的新式类MRO方法: # 从左至右的深度优先遍历,但是如果遍历中出现重复的类,只保留最后一个,之后再根据去重前类的出现顺序排序 # 使用super函数时,会从MRO列表中的下一个类中搜索方法,只要每一个重定义过的方法都使用了super(),super就能保证让这个方法只被调用一次 # super(a, b)函数的本质是获取类a在类b的MRO列表中a的下一个类 # 但是这样也可能出现以下意想不到的孝bug class D: def spam(self): print('D.spam') super().spam() class D1: def spam(self): print('D1.spam') class D2(D, D1): pass d = D2() d.spam() # 这是由于MRO列表导致的super函数bug,这三个类的MRO列表应该是[D2, D, D1], super去找了D1中的方法 # 要避免遇到麻烦,最好遵守下面两点 # 1.确保所有的类中都实现了相同签名的调用; 2.确保你要调用的方法在顶层被实现(例子中为D2)