Python 里的继承和多态是面向对象编程(OOP)的核心概念,学好了能让你的代码更灵活、可复用,还能少写不少重复代码。
参考文章:Python 继承和多态 | 简单一点学习 easyeasy.me
1. 什么是继承?
继承的基本概念
继承就是让一个类(子类)“继承”另一个类(父类)的属性和方法。子类可以直接用父类的功能,还能根据需要扩展或修改。打个比方,父类就像是“模板”,子类是基于模板再加工的“定制版”。
为什么需要继承
- 代码复用:不用重复写相同的代码,父类的功能直接拿来用。
- 逻辑清晰:把通用的功能放父类,子类只管自己的“个性”部分。
- 扩展性强:子类可以加新功能,也可以改父类的行为。
Python 中类的基本定义回顾
在 Python 里,类用 class
关键字定义,属性是类的“变量”,方法是类的“函数”。来看个简单例子:
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
return f"{self.name} 发出声音"
# 创建一个实例
dog = Animal("小狗")
print(dog.speak()) # 输出:小狗 发出声音
2. 单继承的实现
定义父类和子类
Python 的继承很简单,子类在定义时括号里写上父类的名字。子类会自动拥有父类的属性和方法。
class Animal: # 父类
def __init__(self, name):
self.name = name
def speak(self):
return f"{self.name} 发出声音"
class Dog(Animal): # 子类,继承 Animal
pass
# 测试
dog = Dog("旺财")
print(dog.speak()) # 输出:旺财 发出声音
Dog
啥也没干,但因为继承了 Animal
,它直接能用 speak
方法。
子类如何调用父类的方法和属性
子类可以用 super()
来调用父类的 __init__
或其他方法,方便在子类里扩展父类的功能。
class Dog(Animal):
def __init__(self, name, breed):
super().__init__(name) # 调用父类的 __init__
self.breed = breed # 子类新增属性
def speak(self): # 重写父类方法
return f"{self.name} 汪汪叫"
# 测试
dog = Dog("旺财", "哈士奇")
print(dog.speak()) # 输出:旺财 汪汪叫
print(dog.breed) # 输出:哈士奇
方法重写(Override)
子类可以“重写”父类的方法,改成自己的逻辑。像上面例子,Dog
重写了 speak
,让它输出“汪汪叫”而不是“发出声音”。
3. 多继承和 MRO
什么是多继承
Python 允许一个类同时继承多个父类,这叫多继承。听起来很酷,但用不好容易把代码搞乱。来看例子:
class Flyer:
def fly(self):
return "我在飞!"
class Swimmer:
def swim(self):
return "我在游!"
class Duck(Animal, Flyer, Swimmer): # 鸭子继承了三个类
def __init__(self, name):
super().__init__(name)
# 测试
duck = Duck("唐老鸭")
print(duck.speak()) # 输出:唐老鸭 发出声音
print(duck.fly()) # 输出:我在飞!
print(duck.swim()) # 输出:我在游!
Duck
继承了 Animal
、Flyer
和 Swimmer
,所以它会叫、会飞、会游。
Python 的方法解析顺序(MRO)
多继承时,如果多个父类有同名方法,Python 得决定用哪个。这靠 MRO(Method Resolution Order) 来搞定。MRO 决定了方法查找的顺序,用的是 C3 线性化算法。
你可以用 类名.__mro__
看 MRO 顺序:
print(Duck.__mro__)
# 输出:
# (<class '__main__.Duck'>, <class '__main__.Animal'>, <class '__main__.Flyer'>,
# <class '__main__.Swimmer'>, <class 'object'>)
Python 会按这个顺序找方法,super()
也会按这个顺序调用父类方法。
多继承的注意事项
- 避免复杂继承:多继承容易让代码逻辑混乱,能用单继承就别用多继承。
- 小心同名方法:如果多个父类有同名方法,确保 MRO 顺序是你想要的。
- 用 super() 谨慎:多继承下,
super()
的行为可能不符合直觉,建议明确调用父类方法。
4. 多态的概念和实现
什么是多态
多态就是“一种接口,多种实现”。简单说,不同的类可以用同样的方法名,但行为各不相同。多态让代码更灵活,调用方不用管具体是哪个类,只要方法名对就行。
Python 中多态的实现方式
Python 是动态语言,天然支持多态,不需要像 Java 那样的接口或抽象类。只需要不同类有同名方法,就能实现多态。
class Cat(Animal):
def speak(self):
return f"{self.name} 喵喵叫"
class Cow(Animal):
def speak(self):
return f"{self.name} 哞哞叫"
# 多态测试
animals = [Dog("旺财", "哈士奇"), Cat("咪咪"), Cow("大黄")]
for animal in animals:
print(animal.speak())
输出:
旺财 汪汪叫
咪咪 喵喵叫
大黄 哞哞叫
这里 Dog
、Cat
、Cow
都继承了 Animal
,但 speak
方法各有不同。调用时只管 speak()
,不用管具体是哪种动物,这就是多态。
多态的实际例子
假设我们要写一个“动物表演”程序,动物种类可能随时加新种类。多态可以让我们轻松扩展:
def animal_show(animal):
print(f"表演开始:{animal.speak()}")
# 测试
animal_show(Dog("旺财", "哈士奇")) # 输出:表演开始:旺财 汪汪叫
animal_show(Cat("咪咪")) # 输出:表演开始:咪咪 喵喵叫
animal_show
函数只关心 animal
有没有 speak
方法,完全不care具体是什么类。
5. 继承和多态的实际应用
什么时候用继承和多态
- 继承:当你有一堆类有共同的属性或方法时,抽个父类出来,把共性放进去。比如,
Dog
、Cat
都有name
和speak
,就适合用继承。 - 多态:当你希望用统一的接口处理不同类型的对象时,用多态。比如,前面例子里的
animal_show
函数,能处理任何有speak
方法的对象。
常见的设计模式场景
- 工厂模式:用一个工厂类根据输入创建不同子类的对象,调用时只用统一接口。
- 策略模式:通过多态实现不同策略,比如不同动物的叫声。
- 模板方法:父类定义流程,子类实现具体步骤。
例子(模板方法):
class Game:
def play(self):
self.start()
self.play_round()
self.end()
def start(self):
print("游戏开始")
def play_round(self):
pass # 子类实现
def end(self):
print("游戏结束")
class Chess(Game):
def play_round(self):
print("下棋中...")
class CardGame(Game):
def play_round(self):
print("打牌中...")
# 测试
chess = Chess()
chess.play()
# 输出:
# 游戏开始
# 下棋中...
# 游戏结束
注意事项和陷阱
- 不要滥用继承:继承层级太深会让代码难维护,优先考虑组合(has-a 关系)而不是继承(is-a 关系)。
- 多态要简单:方法名一致不代表逻辑一致,确保多态的接口语义清晰。
- 小心 super():尤其在多继承时,
super()
可能调用到意外的父类。 - 性能考虑:继承和多态虽然方便,但深层继承链可能影响性能,特别是在大项目里。