🐍 继承与多态 — 代码复用的终极武器
🕐 预计用时:2-3 小时 | 🎯 目标:掌握继承、super()、方法重写、多继承、isinstance/issubclass
📖 今日目录
1. 什么是继承?
继承就是"子承父业"——子类自动拥有父类的所有属性和方法,还能添加自己的。
# 没有继承:大量重复代码
class Dog:
def __init__(self, name):
self.name = name
def speak(self):
print(f"{self.name}:汪汪!")
class Cat:
def __init__(self, name):
self.name = name
def speak(self):
print(f"{self.name}:喵喵!")
# 有继承:提取公共代码到父类
class Animal: # 父类(基类)
def __init__(self, name):
self.name = name
def speak(self):
pass
class Dog(Animal): # 子类(派生类)
def speak(self):
print(f"{self.name}:汪汪!")
class Cat(Animal):
def speak(self):
print(f"{self.name}:喵喵!")
2. 单继承基础
class Animal:
"""动物基类"""
def __init__(self, name, age):
self.name = name
self.age = age
def eat(self):
print(f"{self.name} 在吃东西")
def sleep(self):
print(f"{self.name} 在睡觉")
def __str__(self):
return f"{self.name}, {self.age}岁"
class Dog(Animal):
"""狗类,继承 Animal"""
def __init__(self, name, age, breed):
super().__init__(name, age) # 调用父类的 __init__
self.breed = breed # 子类新增属性
def bark(self):
print(f"{self.name}:汪汪汪!")
def fetch(self, item):
print(f"{self.name} 捡回了 {item}")
class Cat(Animal):
"""猫类,继承 Animal"""
def __init__(self, name, age, color):
super().__init__(name, age)
self.color = color
def meow(self):
print(f"{self.name}:喵喵喵~")
def scratch(self):
print(f"{self.name} 在磨爪子")
# 子类拥有父类的所有方法
dog = Dog("旺财", 3, "金毛")
dog.eat() # 继承自 Animal
dog.sleep() # 继承自 Animal
dog.bark() # Dog 自己的方法
dog.fetch("飞盘") # Dog 自己的方法
cat = Cat("小花", 2, "橘色")
cat.eat() # 继承自 Animal
cat.meow() # Cat 自己的方法
cat.scratch() # Cat 自己的方法
print(dog) # 旺财, 3岁(继承了 __str__)
print(cat) # 小花, 2岁
💡 继承的好处:
1. 代码复用:公共代码写在父类,不用重复
2. 扩展方便:子类只需写自己特有的部分
3. 维护简单:改父类一处,所有子类受益
3. super() 调用父类
super() 让子类调用父类的方法——最常用于调用父类的 __init__。
class Shape:
def __init__(self, color="红色"):
self.color = color
print(f"Shape.__init__: 颜色={color}")
class Circle(Shape):
def __init__(self, radius, color="蓝色"):
super().__init__(color) # 调用父类的 __init__
self.radius = radius
print(f"Circle.__init__: 半径={radius}")
c = Circle(5)
# 输出:
# Shape.__init__: 颜色=蓝色
# Circle.__init__: 半径=5
📋 super() 的常见用法
class Base:
def __init__(self):
self.base_attr = "来自父类"
def show(self):
print(f"Base.show: {self.base_attr}")
class Child(Base):
def __init__(self):
super().__init__() # 调用父类构造
self.child_attr = "来自子类"
def show(self):
super().show() # 先执行父类的 show
print(f"Child.show: {self.child_attr}")
c = Child()
c.show()
# Base.show: 来自父类
# Child.show: 来自子类
💡 super() 的本质:
super().__init__(color) 等价于 Shape.__init__(self, color)
但 super() 更好——它在多继承时自动按 MRO 顺序调用。
4. 方法重写(Override)
子类可以重新定义父类的方法——"我用我自己的方式做"。
class Employee:
"""员工基类"""
def __init__(self, name, salary):
self.name = name
self.salary = salary
def work(self):
print(f"{self.name} 在工作")
def bonus(self):
return self.salary * 0.1 # 默认 10% 奖金
def __str__(self):
return f"{self.name}: ¥{self.salary:.0f}/月"
class Manager(Employee):
"""经理:重写 work 和 bonus"""
def __init__(self, name, salary, department):
super().__init__(name, salary)
self.department = department
def work(self): # 重写
print(f"{self.name} 管理 {self.department} 部门")
def bonus(self): # 重写
return self.salary * 0.3 # 经理 30% 奖金
class Developer(Employee):
"""开发者:重写 work"""
def __init__(self, name, salary, language):
super().__init__(name, salary)
self.language = language
def work(self): # 重写
print(f"{self.name} 写 {self.language} 代码")
def bonus(self):
return self.salary * 0.2 # 开发者 20% 奖金
class Designer(Employee):
"""设计师:不重写,用默认实现"""
pass
# 不同子类,同名方法,不同行为
employees = [
Manager("张三", 20000, "技术"),
Developer("李四", 15000, "Python"),
Developer("王五", 15000, "Java"),
Designer("赵六", 12000),
]
for emp in employees:
emp.work()
print(f" 奖金: ¥{emp.bonus():.0f}")
# 张三 管理 技术 部门
# 奖金: ¥6000
# 李四 写 Python 代码
# 奖金: ¥3000
# 王五 写 Java 代码
# 奖金: ¥3000
# 赵六 在工作
# 奖金: ¥1200
5. 多态
多态 = 同一个方法,不同对象有不同的行为。
class Shape:
def area(self):
raise NotImplementedError("子类必须实现 area 方法")
def __str__(self):
return f"{self.__class__.__name__}: 面积={self.area():.2f}"
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
return 3.14159 * self.radius ** 2
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
class Triangle(Shape):
def __init__(self, base, height):
self.base = base
self.height = height
def area(self):
return 0.5 * self.base * self.height
# 多态:同一个接口,不同的实现
shapes = [Circle(5), Rectangle(4, 6), Triangle(3, 8)]
for s in shapes:
print(s) # 每个对象的 area() 行为不同
# Circle: 面积=78.54
# Rectangle: 面积=24.00
# Triangle: 面积=12.00
# 多态的好处:不需要知道具体类型,统一接口
def total_area(shapes):
return sum(s.area() for s in shapes)
print(f"总面积: {total_area(shapes):.2f}") # 114.54
💡 Python 的多态是"鸭子类型":
不看你是什么类型,只看你有没有这个方法。
"如果它走路像鸭子、叫声像鸭子,那它就是鸭子。"
不需要继承也能实现多态——只要有同名方法就行。
6. isinstance 与 issubclass
class Animal:
pass
class Dog(Animal):
pass
class Puppy(Dog):
pass
# isinstance — 判断对象是否是某个类(或其子类)的实例
puppy = Puppy()
print(isinstance(puppy, Puppy)) # True
print(isinstance(puppy, Dog)) # True(子类实例也是父类的实例)
print(isinstance(puppy, Animal)) # True
print(isinstance(puppy, str)) # False
# issubclass — 判断类的继承关系
print(issubclass(Puppy, Dog)) # True
print(issubclass(Puppy, Animal)) # True
print(issubclass(Dog, Animal)) # True
print(issubclass(Animal, Dog)) # False(反向不行)
# 实际用途:类型检查
def feed(animal):
if isinstance(animal, Dog):
print("给狗粮")
elif isinstance(animal, Cat):
print("给猫粮")
else:
print("给通用食物")
7. 多继承
Python 支持一个类继承多个父类——"同时继承多个家族的遗产"。
class Flyable:
def __init__(self):
self.can_fly = True
def fly(self):
print(f"{self.name} 在飞翔")
class Swimmable:
def __init__(self):
self.can_swim = True
def swim(self):
print(f"{self.name} 在游泳")
class Duck(Flyable, Swimmable):
"""鸭子:既能飞又能游"""
def __init__(self, name):
self.name = name
super().__init__()
def quack(self):
print(f"{self.name}:嘎嘎嘎!")
duck = Duck("唐老鸭")
duck.fly() # 继承自 Flyable
duck.swim() # 继承自 Swimmable
duck.quack() # 自己的方法
print(f"能飞: {duck.can_fly}")
print(f"能游: {duck.can_swim}")
# 多继承的菱形问题
class A:
def __init__(self):
print("A.__init__")
def hello(self):
print("Hello from A")
class B(A):
def __init__(self):
super().__init__()
print("B.__init__")
class C(A):
def __init__(self):
super().__init__()
print("C.__init__")
def hello(self):
print("Hello from C")
class D(B, C):
"""D 继承 B 和 C,B 和 C 都继承 A"""
def __init__(self):
super().__init__()
print("D.__init__")
d = D()
# C.__init__ → B.__init__ → D.__init__(按 MRO 顺序)
d.hello() # Hello from C(C 在 B 前面)
8. MRO 方法解析顺序
# MRO(Method Resolution Order)决定了多继承时方法的查找顺序
class A:
def show(self): print("A")
class B(A):
def show(self): print("B")
class C(A):
def show(self): print("C")
class D(B, C):
pass
# 查看 MRO
print(D.__mro__)
# (<class 'D'>, <class 'B'>, <class 'C'>, <class 'A'>, <class 'object'>)
# 查找顺序:D → B → C → A → object
d = D()
d.show() # "B"(按 MRO 找到 B 的 show)
# MRO 规则:C3 线性化
# 1. 子类优先于父类
# 2. 多个父类按声明顺序(从左到右)
# 3. 每个类只出现一次
💡 多继承的建议:
1. 尽量用单继承,多继承会让代码复杂
2. 如果用多继承,用 mixin 模式(只添加方法,不修改状态)
3. 用 类名.__mro__ 检查方法查找顺序
4. 避免菱形继承导致的复杂性
9. 实战练习
🎯 练习 1:支付系统
from datetime import datetime
class Payment:
"""支付基类"""
def __init__(self, amount, currency="CNY"):
self.amount = amount
self.currency = currency
self.status = "pending"
self.timestamp = None
def pay(self):
if self._validate():
self._process()
self.status = "success"
self.timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
print(f"✅ 支付成功: {self.currency} {self.amount:.2f}")
return True
return False
def _validate(self):
if self.amount <= 0:
print("❌ 金额必须大于0")
return False
return True
def _process(self):
"""子类重写此方法实现具体支付逻辑"""
pass
def receipt(self):
print(f"\n📄 收据")
print(f" 金额: {self.currency} {self.amount:.2f}")
print(f" 方式: {self.__class__.__name__}")
print(f" 状态: {self.status}")
if self.timestamp:
print(f" 时间: {self.timestamp}")
class CreditCard(Payment):
"""信用卡支付"""
def __init__(self, amount, card_number, holder, currency="CNY"):
super().__init__(amount, currency)
self.card_number = card_number
self.holder = holder
def _validate(self):
if not super()._validate():
return False
if len(self.card_number) != 16:
print("❌ 信用卡号必须16位")
return False
return True
def _process(self):
masked = "****" + self.card_number[-4:]
print(f" 💳 刷卡: {masked} ({self.holder})")
class WechatPay(Payment):
"""微信支付"""
def __init__(self, amount, openid, currency="CNY"):
super().__init__(amount, currency)
self.openid = openid
def _process(self):
print(f" 📱 微信扫码: {self.openid[:8]}...")
class Alipay(Payment):
"""支付宝"""
def __init__(self, amount, account, currency="CNY"):
super().__init__(amount, currency)
self.account = account
def _process(self):
print(f" 🅰️ 支付宝: {self.account}")
# 多态:统一接口,不同实现
payments = [
CreditCard(299.00, "1234567812345678", "张三"),
WechatPay(59.90, "wx_user_12345"),
Alipay(199.00, "alipay@test.com"),
]
for p in payments:
p.pay()
p.receipt()
🎯 练习 2:游戏角色继承体系
import random
class Character:
"""角色基类"""
def __init__(self, name, hp, attack, defense):
self.name = name
self.hp = hp
self.max_hp = hp
self.attack = attack
self.defense = defense
self.alive = True
def take_damage(self, damage):
actual = max(1, damage - self.defense)
self.hp = max(0, self.hp - actual)
if self.hp == 0:
self.alive = False
return actual
def attack_enemy(self, enemy):
if not self.alive:
return
damage = self.attack + random.randint(-3, 3)
actual = enemy.take_damage(damage)
print(f"⚔️ {self.name} → {enemy.name}: {actual}点伤害")
if not enemy.alive:
print(f"💀 {enemy.name} 被击败!")
def show_status(self):
bar = "█" * int(self.hp / self.max_hp * 15) + "░" * (15 - int(self.hp / self.max_hp * 15))
print(f" {self.name}: {bar} {self.hp}/{self.max_hp}")
class Warrior(Character):
"""战士:高血量高防御,有盾击"""
def __init__(self, name):
super().__init__(name, hp=150, attack=20, defense=15)
self.shield_active = False
def shield_bash(self, enemy):
"""盾击:伤害+50%概率眩晕"""
if not self.alive:
return
damage = int(self.attack * 1.5)
actual = enemy.take_damage(damage)
stunned = random.random() < 0.5
print(f"🛡️ {self.name} 盾击 {enemy.name}: {actual}点伤害" +
(" [眩晕!]" if stunned else ""))
if stunned:
enemy.stunned = True
class Mage(Character):
"""法师:高攻击低血量,有火球术"""
def __init__(self, name):
super().__init__(name, hp=80, attack=35, defense=5)
self.mana = 100
def fireball(self, enemy):
"""火球术:高伤害但消耗法力"""
if not self.alive:
return
if self.mana < 30:
print(f"❌ {self.name} 法力不足!")
return
damage = self.attack * 2 + random.randint(-5, 10)
actual = enemy.take_damage(damage)
self.mana -= 30
print(f"🔥 {self.name} 火球术 → {enemy.name}: {actual}点伤害 (法力:{self.mana})")
class Assassin(Character):
"""刺客:低血量高攻击,有暴击"""
def __init__(self, name):
super().__init__(name, hp=90, attack=30, defense=8)
def attack_enemy(self, enemy):
"""重写:30% 概率暴击"""
if not self.alive:
return
crit = random.random() < 0.3
damage = self.attack * (2 if crit else 1) + random.randint(-3, 5)
actual = enemy.take_damage(damage)
tag = " [暴击!]" if crit else ""
print(f"🗡️ {self.name} → {enemy.name}: {actual}点伤害{tag}")
if not enemy.alive:
print(f"💀 {enemy.name} 被击败!")
# 对战
hero = Warrior("勇者")
mage = Mage("法师")
assassin = Assassin("刺客")
boss = Character("Boss", hp=200, attack=25, defense=12)
print("⚔️ 对战开始!")
for round_num in range(1, 11):
print(f"\n--- 第{round_num}回合 ---")
hero.attack_enemy(boss)
if boss.alive: mage.fireball(boss)
if boss.alive: assassin.attack_enemy(boss)
if boss.alive: boss.attack_enemy(hero)
if not boss.alive:
print("\n🎉 胜利!")
break
hero.show_status()
boss.show_status()
10. 今日小结
| |
|---|
| class Child(Parent) |
| 调用父类方法,常用 super().__init__() |
| |
| |
| |
| |
| class D(B, C) |
| 方法解析顺序:D → B → C → A → object |
🧠 记忆口诀:
class 子类(父类) 继承写,super 调用父类的。
重写方法换行为,多态同名不一样。
isinstance 查类型,issubclass 查关系。
多继承要小心,MRO 定顺序。
🔮 预告: Day 24 魔术方法 — `__str__`/`__repr__`/`__len__`/`__eq__`/`__lt__`,运算符重载。让对象像内置类型一样优雅!