1. 概述
Python中的super()
函数可以在子类中调用父类的方法,而不必显式地指定父类的名称,这可以大大提高代码的灵活性和可读性,特别是在多重继承的情况下。在本文中,我们将介绍super的基本用法,以及一些高级使用技巧。
2. 基本用法
2.1 在单继承中的使用
在单继承中,如果子类要调用父类的方法,一般使用父类的名称来显式地指定要调用的方法。例如:
class Animal:
def sound(self):
print("")
class Cat(Animal):
def sound(self):
print("Meow")
cat = Cat()
cat.sound() # 输出Meow
使用super()函数的优势在于可以允许实例化的子类共享同一个父类,并且保留对父类的方法调用。这在代码重构时非常有用,因为子类可以继承父类的基本行为,同时可以更方便地提供额外的行为或更改父类的行为。
使用super()函数的语法如下:
class Animal:
def sound(self):
print("")
class Cat(Animal):
def sound(self):
super().sound()
print("Meow")
cat = Cat()
cat.sound() # 输出空行和Meow
上面的代码中,我们使用了super().sound()
调用了父类的sound()方法。这样做的好处是,无论Animal类的名称如何更改,Cat都可以通过super()函数访问到正确的父类。
2.2 在多继承中的使用
在多继承中,使用super()函数更加有用。假设我们有一个类Bird,它继承了Animal类,而Animal类又继承了LivingThing类:
class LivingThing:
def breathe(self):
print("")
class Animal(LivingThing):
def sound(self):
print("")
class Bird(Animal):
def fly(self):
print("")
bird = Bird()
bird.fly()
bird.sound()
bird.breathe()
在这个例子中,我们可以使用super()函数来调用父类的方法,而无需明确指定父类的名称:
class LivingThing:
def breathe(self):
print("")
class Animal(LivingThing):
def sound(self):
print("")
class Bird(Animal):
def sound(self):
super().sound()
print("Tweet")
def fly(self):
print("")
bird = Bird()
bird.fly()
bird.sound()
bird.breathe()
需要注意的是,在多继承的情况下,super()
函数并不总是按照我们期望的方式工作。例如:
class A:
def method(self):
print("A")
class B(A):
def method(self):
print("B")
super().method()
class C(A):
def method(self):
print("C")
super().method()
class D(B, C):
def method(self):
print("D")
super().method()
d = D()
d.method() # 输出D、B、C、A
在这个例子中,我们定义了一个多继承关系,其中类B和类C继承自类A,并覆盖了它的方法。在类D中,我们再次覆盖了A的方法,并使用了super()
函数来调用其他类中的方法。然而,输出的顺序并不是我们期望的B、C、A。相反,输出顺序是D、B、C、A。
这是因为Python中的super()
函数是基于方法解析顺序(Method Resolution Order,简称MRO)的,MRO是一个确定多个继承关系中属性和方法调用顺序的算法。Python使用C3算法来计算MRO。要查看类的MRO,可以使用__mro__
属性:
print(D.__mro__) # 输出(, , , , )
3. 高级用法
3.1 动态绑定方法
有时候我们会在运行时动态地绑定方法,这时候需要使用到types.MethodType()
和super()
函数。
假如我们有一个类Human,它有一个方法walk:
class Human:
def __init__(self, name):
self.name = name
def walk(self):
print("{} is walking".format(self.name))
human = Human("John")
human.walk() # 输出John is walking
现在我们希望动态地把walk方法绑定到另一个实例的对象上,而不是在类定义中指定。这可以通过以下代码实现:
class MyClass:
def __init__(self, name):
self.name = name
def run(self):
print("{} is running".format(self.name))
object1 = MyClass("Anna")
object2 = MyClass("Ben")
method = human.walk
method = types.MethodType(method, object1)
method() # 输出John is walking
method = human.walk
method = types.MethodType(method, object2)
method() # 输出John is walking
上面的代码中,我们将一个方法对象human.walk
绑定到不同的MyClass
实例上,使用types.MethodType()
函数将方法绑定到实例。调用method()
方法时,self.name
将引用不同的对象,输出结果也会不同。
3.2 经典类和新式类的区别
Python中的类分为新式类和经典类。在早期版本的Python中,新式类需要显式地继承自object类,而经典类不需要这样做。因此,在不同的继承体系中使用super()
函数会产生不同的结果。
新式类的super()
函数根据MRO调用父类方法,而经典类的super()
函数则根据深度遍历调用父类方法。
例如,假设我们有一个名为MyClass的经典类:
class MyClass:
def my_method(self):
print("MyClass.my_method()")
class AnotherClass(MyClass):
def my_method(self):
print("AnotherClass.my_method()")
MyClass.my_method(self)
在这个例子中,我们定义了类MyClass和AnotherClass,AnotherClass继承自MyClass并覆盖了其方法。在AnotherClass的my_method()方法中,我们使用MyClass.my_method(self)
调用父类的方法。
现在我们来看看使用新式类的情况:
class MyNewClass(object):
def my_method(self):
print("MyNewClass.my_method()")
class AnotherNewClass(MyNewClass):
def my_method(self):
print("AnotherNewClass.my_method()")
super().my_method()
在这个例子中,我们定义了类MyNewClass和AnotherNewClass。AnotherNewClass继承自MyNewClass,并覆盖了其方法。在AnotherNewClass的my_method()方法中,我们使用super().my_method()
调用父类的方法。
请注意,使用经典类时,我们需要显式地传递self参数,而使用新式类时,则不需要传递self参数。
3.3 super()和重载运算符
在面向对象编程中,我们经常会使用运算符重载来实现自定义操作。在Python中,运算符重载基于函数调用来实现,因此可以使用super()
函数来调用父类的运算符方法。
例如,假设我们有一个类Vector,表示一个多维向量。我们希望使用运算符重载实现向量相加,代码如下:
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
return Vector(self.x + other.x, self.y + other.y)
v1 = Vector(1, 2)
v2 = Vector(3, 4)
v3 = v1 + v2
print(v3.x, v3.y) # 输出4 6
现在,我们希望创建一个名为LoggedVector的子类,它可以记录向量操作的次数。为此,我们需要覆盖__add__()
方法,并在类中添加一个访问计数器的属性:
class LoggedVector(Vector):
cnt = 0
def __add__(self, other):
LoggedVector.cnt += 1
return super().__add__(other)
v1 = LoggedVector(1, 2)
v2 = LoggedVector(3, 4)
v3 = v1 + v2
v4 = v1 + v2
print(v3.x, v3.y) # 输出4 6
print(LoggedVector.cnt) # 输出2
在这个例子中,我们创建了LoggedVector类,它继承自Vector,并覆盖了__add__()
方法。在__add__()
方法中,我们调用了父类的方法,并在计数器上增加了1。通过使用super()
函数,我们确保了正确的父类方法被调用。
4. 总结
在本文中,我们介绍了Python中的super()
函数。通过使用super()
函数,我们可以在子类中调用父类的方法,而无需明确指定父类的名称。这可以使代码更灵活,可读性更高,特别是在多重继承的情况下。我们还介绍了一些高级用法,例如动态绑定方法,经典类和新式类之间的区别,以及如何使用super()
函数和运算符重载。掌握了这些知识,你可以更好地利用Python的继承特性,写出更优雅的代码。