Python|单继承中的 super()
先决条件:继承,函数覆盖
在相当抽象的级别上, super()
提供了对超类(父类)的那些方法的访问,这些方法已在从它继承的子类(子类)中被覆盖。考虑下面给出的代码示例,这里我们有一个名为Square
的类和另一个名为Cube
的类,它继承了类Square
。
class Square:
def __init__(self, side):
self.side = side
def area(self):
return self.side * self.side
class Cube(Square):
def area(self):
face_area = self.side * self.side
return face_area * 6
def volume(self):
face_area = self.side * self.side
return face_area * self.side
注意:由于Cube class
没有__init__()
方法,所以Square class
的__init__()
将用于初始化Cube
实例(继承的基本属性)。
考虑到这个例子,我们知道立方体的每个面都是正方形,因此立方体的face_area
表示正方形的area
。现在,使用类Square
的area()
方法评估face_area
是有意义的,而不是手动计算它,这不仅可以避免我们重写代码,还可以从一个地方更改area()
逻辑。但是由于我们已经覆盖了Cube
中的area()
方法,我们不能使用self.area()
调用Square
的area()
方法。
现在,这是super()
来救援的情况。 super()
返回父类的代理对象,然后您在该代理对象上调用您选择的方法,因此,我们可以使用super()
调用Square class
的area()
方法,如super().area()
.下面是class Cube
的修改定义。
class Cube(Square):
def area(self):
return super().area() * 6
def volume(self):
return super().area() * self.side()
请注意,我们可以将Square
方法的area()
称为Square.area()
而不是super().area()
但后者更容易换出超类或在需要时重命名它,从而使代码更容易维护。在super()
中传递参数 –
在上一节中,我们讨论了如何在不带任何参数的情况下使用super()
,但这只能让我们访问作为子类的直接父级的超类的方法。
要访问不是子类的直接父类的超类的方法,我们使用带有两个参数的super()
。让我们考虑一个名为Square
、 SquarePrism
和Cube
的三个类的示例,以了解如何将super()
与参数一起使用。
现在,立方体只是一种特殊类型的方形棱镜,它的高度等于它的底边,因此立方体与 SquarePrism 的相似性远大于它与 Square 的相似性。因此,在本例中, class Cube
将继承class SquarePrism
和class
将继承
SquarePrismclass Square
。对于Square
类,我们将使用上一节中使用的相同定义。下面给出了我们新定义的类SquarePrism
的定义。
class SquarePrism(Square):
def __init__(self, side, height):
self.side = side
self.height = height
def face_area(self):
base_area = super().area()
lateral_area = self.side * self.height
return base_area, lateral_area
def area(self):
base_area = super().area()
lateral_area = self.side * self.height
return 2 * base_area + 4 * lateral_area
SquarePrism
实例有两个属性,它的方形底边和方形棱镜的高度。实例方法face_area()
返回一个由两个数字组成的元组,分别表示方形棱镜的底面积和方形棱镜的侧面面积。由于底面是正方形,对于正方形棱镜的底面积,我们将方法Square.area()
称为super().area()
。 area()
方法返回方形棱镜的总表面积。
到目前为止,我们使用了不带任何参数的super()
,现在遵循新类Cube
的定义,它将演示带参数的super()
的使用。
class Cube(SquarePrism):
def __init__(self, side)
super().__init__(side = side, height = side)
def face_area(self):
return super(SquarePrism, self).area()
def area(self):
return super().area()
与__init__()
和area()
) 方法不同, Cube
的face_area()
方法与其对应的SquarePrism.face_area()
() 方法有些不同。对于一个立方体,侧面积等于底面积,因此, face_area()
方法返回元组是没有意义的,因此class Cube
的face_area()
将返回立方体面之一的面积.
现在,由于立方体的每个面都是正方形,因此使用class Square
的area()
方法再次有意义。现在,由于class Square
不是class Cube
的直接父类,我们不能将class Square
的area()
方法作为super().area()
访问,因为它将调用方法SquarePrism.area()
。
这里我们使用super(SquarePrism, self).face_area()
来调用class Square
的area()
方法。在第一个参数中, SquarePrism
表示super()
在类SquarePrism,
的直接父级中搜索area()
方法,即class Square
中。使用self
作为第二个参数将当前Cube
对象的上下文提供给super()
以便请求的area()
方法对其进行操作。
请记住,要以双参数形式使用super()
,作为第二个参数传递的对象必须是作为第一个参数传递的type
的实例。
注意:由于类Cube
是class SquarePrism
的子类,因此Cube
实例也是class SquarePrism
和class Square
的实例。
尝试以下代码片段并观察输出以澄清上述观点。
class Square:
def __init__(self):
pass
class SquarePrism(Square):
def __init__(self):
pass
class Cube(SquarePrism):
def __init__(self):
pass
square = Square()
squareprism = SquarePrism()
cube = Cube()
print(isinstance(squareprism, Square))
print(isinstance(cube, Square))
值得注意的是, super()
的零参数形式只能在类定义中使用,因为它是由编译器使用适当的参数自动填充的,即如果我们在类中使用super()
,比如X
, super()
将被编译器转换为super(X, self)
。
虽然super()
的零参数形式在开发人员中很流行,但两个参数形式目前似乎没有多大用处。但是在多重继承中,两个参数的形式起着非常重要的作用。