Python多重继承简单示例
- Python多重继承简单示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20class 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的直接父类是A(B的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
51class 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
29class 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
27class 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
14class 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
20class 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
27class 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类继承自B和C类,而B和C又都继承自A类。在D类的method方法中,使用super(B, self).method()明确指定跳过B类,调用C类中继承自A类的method方法
注意:在Python的
super()函数中,默认是调用自身类和对象作为参数的super函数,例如:1
2
3
4class A(B):
def __init__(self):
# 等价于调用super().__init__(),
super(A, self).__init__()- 注:Python3中调用
super(A, self).__init__()等价于调用super().__init__(),但在部分Python2旧版本中必须明确指明类和对象
- 注:Python3中调用
自定义类层次结构中的方法调用
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
30class 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)的方式,分别调用了Derived1和Derived2类的父类Base中的operation方法。这种方式可以在自定义的类层次结构中,灵活地控制方法的调用路径 - 不建议使用过于复杂的操作,非必要也不做多重继承,通常情况下,使用默认的
super()函数调用方式就能满足大多数的编程需求,但是很多官方的库中,会明确显式知名其自身类和对象,比如PyTorch的nn.Linear类的实现:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15class 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()
- 在