继承inherit是对现有的类的扩展(extension),实现对代码的重用和重写。

被继承的类叫作父类(Super Class 或 Parent Class), 继承的类叫子类(Subclass 或 Child).

  • 父类就像是一个基础模板 base model或者蓝图 blue print,比如动物类
  • 子类继承父类后,它自动拥有了父类的特性,但是可以添加更多的特性,或者修改父类的特性。例如动物类的子类可以猫类狗类,它们都继承了动物类的行为,但是也有自己独特的行为。

对于已经创建好的并开始被使用的类 ClassA 来说,有了新的要求需要做更改,但是对于ClassA来说,已经有对象创建了,并且这些对象并不需要新加的功能,所以需要用到继承来解决问题。

继承动物类

在继承动物类之前,首先要有一个动物类才可以继承:

class Animal:

	def __init__(self, name, sound):
		self.name = name
		self.sound = sound

	def speak(self):
		print(f"{self.name} makes a {self.sound} sound.")

animal = Animal('Tony', 'roar')
animal.speak()

有了动物类之后,就可以实现继承了,下面用小狗类举例

class Dog(Animal):
    pass

dog = Dog('Odie', 'bark')
dog.speak()

子类对父类的扩展

在上面的例子中,Dog类完全继承了父类的属性和方法,在这个基础上, Dog也可以对父类进行扩展,比如新一个功能catch

class Dog(Animal):

	def catch(self, obj):
		print(f"I caught a {obj} that thrown by my master.")

dog = Dog('Odie', 'bark')
dog.speak()

``

方法的重写

有时候,子类需要对父类的行为(方法)进行修改,这叫做重写(Override)

class Cat(Animal):

	def speak(self):
		print(f"{self.sound}, {self.sound}")	

重写之后,Catspeak方法与父类已经完全不一样了

cat = Cat("Garfield", 'Meow~')
cat.speak()

# 打印结果为:Meow~, Meow~

重写可以针对父类的所有资源:

class Cat(Animal):

	def __init__(self, color):
		self.color = color

	def speak(self):
		print(f"{self.sound}, {self.sound}")

对于上面的继承方式,Cat类放弃了父类的两个属性namesound, 这样一来,Cat类里的speak方法就不能被成功调用。

如果在重写时想要保留父类原有的功能或属性,需要用到super关键字。

class Cat(Animal):

	def __init__(self, name, sound, color):
		super().__init__(name, sound)
		self.color = color

	def speak(self):
		super().speak()
		print(f"{self.sound}, {self.sound}")

方法的重写需要特别注意两个点:

  1. 重写时,需要完全继承父类方法,super()的.之后的名字要写正确。
  2. 不要忘记需要带入的参数,以及传值顺序。

完整的例子如下:

class Animal:

    def __init__(self, name, sound):
        self.name = name
        self.sound = sound

    def speak(self):
        print(f"{self.name} makes a {self.sound} sound.")

    def run(self):
        print("I am running.")

    def eat(self, food):
        print(f"I am eating {food}.")


class Cat(Animal):
    
    def __init__(self, name, sound, color):
	super().__init__(name, sound)
	self.color = color

    def speak(self):
	super().speak()
	print(f"{self.sound}, {self.sound}")
    
    def eat(self, food):
        super().run()
        print("Clean my face and hand")
        super().eat(food)
        print("Drink water.")

多继承

多继承指的是一个类可以从多个父类继承属性和方法。

多继承的语法为:

class ChildClass(ParentClass1, ParentClass2, ...):
	pass

下面是一个全新的动物类

class Animal:

	def alive(self):
		print("I am alive")

	def eat(self, food):
		print("I must eat to be alive.")

然后创建一个鸟类:

class Bird:

	def __init__(self):
		self.wings = 2
		self.legs = 2

	def fly(self):
		print("I can fly.")

对于小鸡,它既是动物又是鸟类,所以它可以多继承上面两个父类


class Chicken(Animal, Bird):
	pass

在多继承的过程中,如果需要在重写或者扩展函数时,需要用super关键字来调用父类的方法,与单继承类似,但是需要特别注意,如果两个父类有同名方法,要掌握 MRO 的查找逻辑。

MRO(Method Resolution Order)

当子类调用方法时,Python 会按照MRO中的顺序去查找方法,这样可以保证类的层次结构的正确性。

MRO的规则:

  1. 深度优先: Python 首先会从左到右查找父类
  2. 从子类开始:搜索是从当前类开始的,如果当前类没有该方法或属性,就查找父类

在实例应用中,如果对这个顺序不清楚,可以调用mro()__mro__查看 MRO顺序

print(Chicken.__mro__)

# 打印结果为: (<class '__main__.Chicken'>, <class '__main__.Animal'>, <class '__main__.Bird'>, <class 'object'>)

如果多继承中的多个父类有同名方法的情况,就需要MRO机制来确定最终执行的是哪个类里的方法:

class A:
	def speak(self):
		print('A is speaking.')

class B:
	def speak(self):
		print('B is speaking.')

class C(A, B):
	pass

c = C()
c.speak()

#打印结果: A is speaking.

如果需要手动改变顺序,也就是说需要强制让子类重写父类里的重名方法,并且不按照MRO规则调用,需要特别写明父类的名字的调用,并且要传递参数self, 这种调用叫做显式Explicit调用:

class C(A, B):
	def speak(self):
		B.speak(self)
		A.speak(self)

钻石继承问题(Diamond Problem)

在多继承中,如果一个类继承了两个父类,而这个父类又继承自同一个祖先类,可能会遇到所谓的钻石继承问题:

# 祖先类 A
class A:
    def speak(self):
        print('A is speaking.')

# 父类 B
class B(A):
    def speak(self):
        super().speak()
        print('B is speaking.')

# 父类 C
class C(A):
    def speak(self):
        super().speak()
        print('C is speaking.')

# 子类 D
class D(B, C):
    def speak(self):
        super().speak()
        print('D is speaking.')


print(D.__mro__)

d = D()
d.speak()

"""
执行结果为:
A is speaking.
C is speaking.
B is speaking.
D is speaking.
"""

练习

练习1

class Student:

    def __init__(self, name, scores):
        self.name = name
        self.scores = scores


class Scholar(Student):

    def average_score(self):
        return sum(self.scores)/len(self.scores)


scholar = Scholar('Jack', [100, 98, 92, 97, 99])
print(scholar.average_score())

习题2

class Student:

    def __init__(self, name, scores):
        self.name = name
        self.scores = scores


class AdvancedStudent(Student):

    def average_score(self):
        return sum(self.scores)/len(self.scores)

class Scholar(AdvancedStudent):

    def average_score(self):
        return (sum(self.scores)-min(self.scores))/(len(self.scores)-1)

scholar = Scholar('Jack', [100, 98, 92, 97, 99])
print(scholar.average_score())