Python——多重继承


Python多重继承简单示例

  • Python多重继承简单示例:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    class Parent1:
    def method1(self):
    print("This is method 1 from Parent1")

    class Parent2:
    def method2(self):
    print("This is method 2 from Parent2")

    class Child(Parent1, Parent2):
    def child_method(self):
    print("This is a method from Child")

    # 创建Child类的实例
    child = Child()
    # 调用从Parent1继承的方法
    child.method1()
    # 调用从Parent2继承的方法
    child.method2()
    # 调用Child类自身的方法
    child.child_method()

super().__init__()的调用规则:

  • super().__init__()前,需要先构造整个MRO,super().__init__()本质实在调用MRO中该类的下一个

  • 所以:在单继承时会调用当前类的父类,但在调用MRO中的第一个方法父类的初始化,且仅调用第一个

  • 多重继承时,父类的MRO相对顺序会得到保障,但是可能会插入其他类(注:此时父类调用super().__init__()时可能不再直接调用其真实父类的初始化函数,而是MRO中的下一个)

    • 比如没有D时,B的直接父类是AB的MRO是B-next->A),但是因为在D的MRO中,B的后一个是C(D的MRO是B-next->C),此时如果B没有初始化super().__init__(),则# Initializing C不会打印
  • 代码示例1:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    class Base1():
    def __init__(self):
    self.name = "Base1"
    print("Initializing Base1")

    def show(self):
    print(f"Name from Base1: {self.name}")

    class Base2():
    def __init__(self):
    self.name = "Base2"
    print("Initializing Base2")

    def show(self):
    print(f"Name from Base2: {self.name}")

    class DerivedUsingInit(Base1, Base2):
    def __init__(self):
    Base1.__init__(self)
    Base2.__init__(self)
    print("Initializing DerivedUsingInit")


    print("\nUsing init for initialization:")
    derived1 = DerivedUsingInit()
    derived1.show()
    print("DerivedUsingInit.__mro__:", DerivedUsingInit.__mro__)

    print("------------------")
    class DerivedUsingSuper(Base1, Base2):
    def __init__(self):
    super().__init__() # 仅调用Base1的__init__函数,不会调用Base2的
    print("Initializing DerivedUsingSuper")

    print("\nUsing super() for initialization:")
    derived2 = DerivedUsingSuper()
    derived2.show()
    print("DerivedUsingSuper.__mro__:", DerivedUsingSuper.__mro__)

    # Initializing Base1
    # Initializing Base2
    # Initializing DerivedUsingInit
    # Name from Base1: Base2
    # DerivedUsingInit.__mro__: (<class '__main__.DerivedUsingInit'>, <class '__main__.Base1'>, <class '__main__.Base2'>, <class 'object'>)
    # ------------------
    #
    # Using super() for initialization:
    # Initializing Base1
    # Initializing DerivedUsingSuper
    # Name from Base1: Base1
    # DerivedUsingSuper.__mro__: (<class '__main__.DerivedUsingSuper'>, <class '__main__.Base1'>, <class '__main__.Base2'>, <class 'object'>)
  • 代码示例2:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    class A:
    def __init__(self):
    print("Initializing A")

    class B(A):
    def __init__(self):
    super().__init__() # 单继承时调用A的init,多继承时可能调用其他的类的init
    print("Initializing B")

    class C(A):
    def __init__(self):
    super().__init__()
    print("Initializing C")

    class D(B, C):
    def __init__(self):
    super().__init__()
    print("Initializing D")

    d = D()
    print("D.__mro__: ", D.__mro__)
    print("B.__mro__: ", B.__mro__)

    # Initializing A
    # Initializing C
    # Initializing B
    # Initializing D
    # D.__mro__: (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
    # B.__mro__: (<class '__main__.B'>, <class '__main__.A'>, <class 'object'>)
  • 代码示例3:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    class A:
    def __init__(self):
    print("Initializing A")

    class B(A):
    def __init__(self):
    # super().__init__()
    print("Initializing B")

    class C(A):
    def __init__(self):
    super().__init__()
    print("Initializing C")

    class D(B, C):
    def __init__(self):
    super().__init__()
    print("Initializing D")

    d = D()
    print("D.__mro__: ", D.__mro__)
    print("B.__mro__: ", B.__mro__)

    # Initializing B
    # Initializing D
    # D.__mro__: (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
    # B.__mro__: (<class '__main__.B'>, <class '__main__.A'>, <class 'object'>)

MRO的简单理解

  • Python 2.3 及以后的版本采用 C3 线性化算法来确定 MRO,C3 线性化算法的核心目标是保证 MRO 满足三个重要特性:
    • 单调性:子类必须在父类之前被检查
    • 局部优先性:类定义中父类的顺序会被保留
    • 一致性:如果一个类继承自多个父类,那么 MRO 必须保证所有父类的 MRO 顺序一致
    • 注意:对于相同名称的同一个函数,MRO顺序靠前的生效,靠后的会被靠前的覆盖
  • 示例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    class A:
    pass

    class B(A):
    pass

    class C(A):
    pass

    class D(B, C):
    pass

    # 打印 D 类的 MRO
    print(D.mro())

继承顺序要满足MRO规则

  • 错误示例,下面的GrandChild1不满足MRO规则,会报错:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    class Parent:
    def __init__(self):
    self.value = "I'm from Parent"

    class Child1(Parent):
    def __init__(self):
    self.value = "I'm from Child1"

    class Child2(Parent):
    def __init__(self):
    self.value = "I'm from Child2"

    class GrandChild(Child1, Child2, Parent): # OK
    def __init__(self):
    self.value = "I'm from GrandChild"

    class GrandChild1(Parent, Child1, Child2): # 报错
    def __init__(self):
    self.value = "I'm from GrandChild1"
    # TypeError: Cannot create a consistent method resolution order (MRO) for bases Parent, Child1, Child2

补充:super()函数详细说明

  • super()函数本质是返回了当前类的 MRO 的一个下一个对象,对于单继承模式而言,就是当前类的父类

super()函数的高阶用法

  • super()函数还可以传入参数,示例如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    class A:
    def method(self):
    print("Method in A")

    class B(A):
    def method(self):
    print("Method in B")

    class C(A):
    def method(self):
    print("Method in C")

    class D(B, C):
    def method(self):
    super(B, self).method() # 调用C类的method方法
    # super(B, self).method1() # 报错,因为C类没有method1方法
    print("Method in D")

    d = D()
    d.method()
    print(D.__mro__)
    print(B.__mro__)

    # Method in C
    # Method in D
    # (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
    # (<class '__main__.B'>, <class '__main__.A'>, <class 'object'>)
    • super(B, self) 是按照方法解析顺序(MRO)来查找 B 类在当前实例 self 的继承关系中的下一个类
    • Python会根据类的MRO来确定方法的调用顺序,MRO是一个列表,它定义了类及其父类的搜索顺序。当使用 super(B, self) 时,它会在 self 所属类的MRO中,从 B 类的下一个位置开始查找方法。例如在多重继承中,通过这种方式可以明确指定从某个类之后的MRO顺序中查找方法,以实现特定的方法调用逻辑
    • 在这个例子中, D 类继承自 BC 类,而 BC 又都继承自 A 类。在 D 类的 method 方法中,使用 super(B, self).method() 明确指定跳过 B 类,调用 C 类中继承自 A 类的 method 方法
  • 注意:在Python的 super() 函数中,默认是调用自身类和对象作为参数的 super 函数,例如:

    1
    2
    3
    4
    class A(B):
    def __init__(self):
    # 等价于调用super().__init__(),
    super(A, self).__init__()
    • 注:Python3中调用super(A, self).__init__() 等价于调用super().__init__(),但在部分Python2旧版本中必须明确指明类和对象
  • 自定义类层次结构中的方法调用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    class Base:
    def operation(self):
    print("Base operation")

    class Derived1(Base):
    def operation(self):
    print("Derived1 operation")

    class Derived2(Base):
    def operation(self):
    print("Derived2 operation")

    class Composite:
    def __init__(self):
    self.derived1 = Derived1()
    self.derived2 = Derived2()
    def operation(self):
    super(Derived1, self.derived1).operation() # 调用Base的operation方法
    super(Derived2, self.derived2).operation() # 调用Base的operation方法
    composite = Composite()
    composite.operation()
    print(Composite.__mro__)
    print(Derived1.__mro__)
    print(Derived2.__mro__)

    # Base operation
    # Base operation
    # (<class '__main__.Composite'>, <class 'object'>)
    # (<class '__main__.Derived1'>, <class '__main__.Base'>, <class 'object'>)
    # (<class '__main__.Derived2'>, <class '__main__.Base'>, <class 'object'>)
    • Composite 类中,通过 super(cls, instance) 的方式,分别调用了 Derived1Derived2 类的父类 Base 中的 operation 方法。这种方式可以在自定义的类层次结构中,灵活地控制方法的调用路径
    • 不建议使用过于复杂的操作,非必要也不做多重继承,通常情况下,使用默认的 super() 函数调用方式就能满足大多数的编程需求,但是很多官方的库中,会明确显式知名其自身类和对象,比如PyTorch的 nn.Linear 类的实现:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      class Linear(Module):
      """...comments..."""
      # ...
      def __init__(self, in_features: int, out_features: int, bias: bool = True,
      device=None, dtype=None) -> None:
      factory_kwargs = {'device': device, 'dtype': dtype}
      super(Linear, self).__init__() # 这里是显示调用其父类的初始化函数,等价于 super().__init__()
      self.in_features = in_features
      self.out_features = out_features
      self.weight = Parameter(torch.empty((out_features, in_features), **factory_kwargs))
      if bias:
      self.bias = Parameter(torch.empty(out_features, **factory_kwargs))
      else:
      self.register_parameter('bias', None)
      self.reset_parameters()