Python 如何使用 Python 的 super() 实现多重继承?
多重继承是面向对象编程中常见的一个概念,它允许一个类从多个基类中继承属性和方法。在 Python 中,我们可以通过 super() 去调用父类的方法,从而实现多重继承。
阅读更多:Python 教程
什么是 super()?
super() 是 Python 中的一个内置函数,它可以用来调用父类的方法。当一个类继承自多个父类时,使用 super() 方法可以调用指定父类的特定方法。当我们在子类中使用 super() 方法时,实际上是在调用其父类中对应的方法。
以下是 super() 方法的基本语法:
super([类], [对象/类实例]).方法名(参数)
- 类:我们可以指定当前类或其它类,super() 会根据类的继承顺序自动找到需要调用的下一个父类。如果我们不指定类,那么 super() 默认会使用当前类。
- 对象/类实例:我们可以指定当前类的对象或其它类的实例,super() 方法将从该对象/实例开始继续寻找下一个父类。
- 方法名:需要调用的父类方法名。
- 参数:需要传递给父类方法的参数。
如何使用 super() 实现多重继承?
在 Python 中,我们可以使用以下格式来实现多重继承:
class ClassName(BaseClass1, BaseClass2, ...):
def __init__(self, arg1, arg2, ...):
super().__init__(arg1, arg2, ...)
...
在上面的代码中,我们首先定义了一个继承自多个基类的类 ClassName。在 init 方法中,我们通过 super() 调用了各个基类的初始化方法。
下面是一个简单的示例代码:
class Animal:
def __init__(self, name):
self.name = name
def eat(self):
print('{} is eating'.format(self.name))
class Flyer:
def __init__(self, fly_height):
self.fly_height = fly_height
def fly(self):
print('flying at height of {}m'.format(self.fly_height))
class Bat(Animal, Flyer):
def __init__(self, name, fly_height):
super().__init__(name=name)
Flyer.__init__(self, fly_height=fly_height)
def greet(self):
print('hi, my name is {}. I am a bat!'.format(self.name))
bat = Bat('Bruce', 20)
bat.greet()
bat.eat()
bat.fly()
在上面的代码中,我们定义了两个基类 Animal 和 Flyer,以及一个继承自两个基类的类 Bat。在 Bat 的初始化方法中,我们通过 super() 调用了 Animal 和 Flyer 的初始化方法。在 Bat 的 greet 方法中,我们调用了它自己的特有方法。最后我们新建了 Bat 的一个实例 bat,然后调用了它的实例方法。
在多重继承中,需要注意以下几点:
- Python 的解释器是按照 DFS(深度优先搜索)的顺序去查找需要调用的父类方法的,也就是说,它会先调用第一个父类的方法,再调用第二个,以此类推。
- 如果多个父类之间存在重复方法,Python 会按照 DFS 的顺序去优先调用前面的基类。例如,在上面的示例中,如果 Animal 和 Flyer 中都有一个 eat 方法,那么 Python 会优先调用 Animal 中的 eat 方法。
- 如果某个基类的方法需要传入参数,我们也需要在 super() 中传递对应参数。例如,在上面的示例中,Animal 和 Flyer 各有一个初始化方法 init(self, name) 和 init(self, fly_height),我们需要在 super() 中传递参数 name 和 fly_height,否则就会报错。
super() 的注意事项
虽然 super() 在实现多重继承时非常方便,但在使用时还是需要注意一些事项,以免产生意外的错误。
- 多次调用 super() 会调用同一个方法
在继承树中,多个类继承自同一个父类时,如果多次使用 super() 调用同一个方法,那么该方法只会被调用一次。
例如,在以下的代码中,如果使用 super() 方法调用 eat() 方法两次,那么 Animal 中对应的 eat() 方法只会被调用一次:
class Animal:
def eat(self):
print('Animal is eating')
class Cat(Animal):
def eat(self):
print('Cat is eating')
super().eat()
super().eat()
cat = Cat()
cat.eat() # 输出 Cat is eating,Animal is eating
由于 Cat 的父类 Animal 中有一个 eat() 方法,因此在 Cat 的 eat() 方法中使用 super() 方法调用 Animal 的 eat() 方法时,我们需要注意调用次数,以免出现重复调用的情况。
- 使用 super() 时要注意 MRO 的顺序
在 Python 3 中,类的继承顺序是由 MRO(Method Resolution Order)算法控制的。在多重继承中,MRO 算法会根据类的继承关系和广度优先原则来解决方法调用的顺序。
具体来说,MRO 算法会在一个类的继承列表中,按照从左到右、深度优先的顺序来遍历,找到需要调用的方法。如果在遍历过程中找到了相同的方法,在第一个类中被调用后,就不会再调用同名方法了。
以下是一个示例代码,演示了 MRO 顺序的遍历过程:
class A:
def __init__(self):
print('A.__init__')
super().__init__()
class B:
def __init__(self):
print('B.__init__')
super().__init__()
class C(A, B):
def __init__(self):
print('C.__init__')
super().__init__()
c = C()
print(C.__mro__)
在上面的代码中,我们定义了三个类 A、B 和 C,并指定 C 继承自 A 和 B 两个类。在 C 的初始化方法中使用 super() 方法调用了 A 和 B 的初始化方法。最后我们新建了一个 C 类的实例 c,并打印了 C 类的 MRO 列表。
运行结果为:
C.__init__
A.__init__
B.__init__
(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)
从输出结果可以看出,C 的初始化方法中首先调用了 A 中的初始化方法,然后在 A 的初始化方法中调用了 B 中的初始化方法。此时由于 B 中没有再次调用 super(),因此程序并没有再次调用 A 或 C 的初始化方法。
结论
在 Python 中,使用 super() 方法可以实现多重继承,帮助我们调用多个父类中相同名字的方法。但在使用 super() 时,需要注意调用父类方法的顺序和传递参数的方式,以免引起错误。同时,在多重继承场景中,建议通过 MRO 确保正确的方法被调用。