1. 概述
super() 返回父类的临时对象,然后可以调用父类的方法。常用于扩展父类的方法,通过使用super() 语句直接调用父类的方法,无需再子类中重新实现, 使用最少的代码来扩展父类的方法。
一个例子:
创建一个矩形类,包含长宽2个属性, 周长和面积2个函数。
class Rectangle:
def __init__(self, length, width):
self.length = length
self.width = width
def area(self):
return self.length * self.width
def perimeter(self):
return 2 * (self.length + self.width)
如果我们需要一个正方形的类,可以选择创建一个类
class Square:
def __init__(self, length):
self.length = length
def area(self):
return self.length * self.length
def perimeter(self):
return 4 * self.length
由于正方形是一个特殊的矩形, 代码中两个类却没什么关系。而通过使用继承来创建类,逻辑上可以反映出正方形和矩形的关系,同时可以减少代码量。
class Square(Rectangle):
def __init__(self, length):
super().__init__(length, length)
super() 返回一个当前对象的父类,所以我们代码中调用的是父类Rectangle的__init__方法,创建一个长和宽相等的矩形。
2. super() 的作用
下面介绍以下super() 做了些什么
super(SubClass, instance) 两个参数:第一个参数是子类,第二个参数是作为该子类实例的对象
在Python3 中为了使用方便,做了一些简化,向我们之前 Square 类中使用的 super() 等效于 super(Square, self) . 第一个参数指的是子类Square,而第二个参数指的是一个Square对象,self。
我们再创建一个正方体的类
class Cube(Square):
def volume(self):
face_area = super(Square, self).area()
return face_area * self.length
Cube 类中,使用super() 函数的第一个参数是 Square 类, 结果是导致从Square的父类开始寻找area() 方法。 第二个参数self 是一个实例,它为父类的方法提供对象的上下文,例如本例中area函数需要使用的实例属性length。
3. 多重继承中的super()
除了单继承之外,Python 还支持多继承,一个子类可以继承自多个父类。延续之前的例子,我们
再创建2个类, 三角形(Triangle)和 金字塔(Pyramid)
class Triangle:
def __init__(self, base, height):
self.base = base
self.height = height
def area(self):
return 0.5 * self.base * self.height
class Pyramid(Triangle, Square):
def __init__(self, base, slant_height):
self.length = base
# 斜高, 平面三角形的底到金字塔顶端的距离
self.slant_height = slant_height
def area(self):
bottom_area = super().area()
plane_area = 0.5 * self.length * self.slant_height * 4
return bottom_area + plane_area
我想要求金字塔的表面积 = 底(正方形面积) + 侧面(三角形面积)* 4。 由于Pyramid 继承自 2个父类(三角形和正方形都定义了area方法), 那么当调用super().area()时, 是调用的哪个父类的方法呢。
3.1 mro (method resolution order)
每个类都有一个__mro__属性,描述其继承关系。我们检查以下金字塔类的__mro__可以看到
In [3]: Pyramid.__mro__
Out[3]: (__main__.Pyramid, __main__.Triangle, __main__.Square, object)
解析顺序为 Pyramid > Triangle > Square > object. 所以当我们调用super().area时, 优先从Triangle中解析,所以调用的三角形的面积公式。
In [4]: pyramid = Pyramid(3,2)
In [5]: pyramid.area()
---------------------------------------------------------------------------
AttributeError: 'Pyramid' object has no attribute 'height'
可以看到,因为金字塔中没有定义 height属性, 导致函数报错。
3.2 解决方法
① 通过修改定义类时父类的顺序, 可以改变__mro__的顺序。将Square 类放到前面
class Pyramid(Square, Triangle):
def __init__(self, base, slant_height):
self.length = base
# 斜高, 平面三角形的底到金字塔顶端的距离
self.slant_height = slant_height
def area(self):
bottom_area = super().area()
plane_area = 0.5 * self.length * self.slant_height * 4
return bottom_area + plane_area
可以看到,输出正常。
In [8]: pyramid = Pyramid(3,2)
In [9]: pyramid.area()
Out[9]: 21.0
②,一个更好的方法是, 多继承时应避免同级的父类存在同名函数,将Triangle中的area函数重命名
class Triangle:
def __init__(self, base, height):
self.base = base
self.height = height
def tri_area(self):
return 0.5 * self.base * self.height
class Pyramid(Triangle, Square):
def __init__(self, base, slant_height):
self.length = base
# 斜高, 平面三角形的底到金字塔顶端的距离
self.slant_height = slant_height
super().__init__(base, slant_height)
def area(self):
bottom_area = super().area()
plane_area = super().tri_area() * 4
return bottom_area + plane_area