Python教程32:继承
“站在巨人的肩膀上。”
继承是面向对象编程的三大特性之一(封装、继承、多态),它让我们可以基于已有类创建新类,实现代码复用。今天我们深入学习Python的继承机制。
1. 什么是继承
问题场景
假设要创建多个类:
1# 没有继承:代码重复
2class Dog:
3 def __init__(self, name, age):
4 self.name = name
5 self.age = age
6
7 def eat(self):
8 return f"{self.name} is eating"
9
10class Cat:
11 def __init__(self, name, age):
12 self.name = name
13 self.age = age
14
15 def eat(self):
16 return f"{self.name} is eating"
17
18 def meow(self):
19 return "Meow!"
20
21# 重复的代码...
继承(Inheritance):
- 子类继承父类的属性和方法
- 子类可以添加新的属性和方法
- 子类可以重写父类的方法
- 实现代码复用,减少重复
为什么需要继承:
- 代码复用:共享功能写一次
- 逻辑清晰:体现is-a关系(狗是动物)
- 易于维护:修改父类,所有子类受益
- 扩展性强:基于现有代码扩展新功能
继承的基本语法
1class ParentClass:
2 """父类(基类、超类)"""
3 pass
4
5class ChildClass(ParentClass):
6 """子类(派生类)"""
7 pass
2. 基本继承
简单示例
1class Animal:
2 """动物类(父类)"""
3
4 def __init__(self, name, age):
5 """初始化动物"""
6 self.name = name
7 self.age = age
8
9 def eat(self):
10 """吃东西(所有动物都会吃)"""
11 return f"{self.name} is eating"
12
13 def sleep(self):
14 """睡觉"""
15 return f"{self.name} is sleeping"
16
17class Dog(Animal):
18 """狗类(子类)- 继承Animal"""
19
20 def bark(self):
21 """狗叫(狗特有的方法)"""
22 return f"{self.name} says: Woof!"
23
24class Cat(Animal):
25 """猫类(子类)- 继承Animal"""
26
27 def meow(self):
28 """猫叫(猫特有的方法)"""
29 return f"{self.name} says: Meow!"
30
31# 使用
32dog = Dog("Buddy", 3)
33print(dog.eat()) # 继承自Animal:Buddy is eating
34print(dog.bark()) # Dog自己的方法:Buddy says: Woof!
35
36cat = Cat("Whiskers", 2)
37print(cat.eat()) # 继承自Animal:Whiskers is eating
38print(cat.meow()) # Cat自己的方法:Whiskers says: Meow!
继承的特点:
- 子类自动拥有父类的所有属性和方法
- 子类可以添加新的属性和方法
- 子类的实例既是子类的实例,也是父类的实例
1print(isinstance(dog, Dog)) # True
2print(isinstance(dog, Animal)) # True(Dog是Animal的子类)
3print(issubclass(Dog, Animal)) # True
3. 方法重写(Override)
子类可以重写父类的方法:
1class Animal:
2 """动物类"""
3 def __init__(self, name):
4 self.name = name
5
6 def speak(self):
7 """说话(通用方法)"""
8 return f"{self.name} makes a sound"
9
10class Dog(Animal):
11 """狗类"""
12 def speak(self):
13 """重写speak方法"""
14 return f"{self.name} says: Woof!"
15
16class Cat(Animal):
17 """猫类"""
18 def speak(self):
19 """重写speak方法"""
20 return f"{self.name} says: Meow!"
21
22# 使用
23animals = [
24 Animal("Generic"),
25 Dog("Buddy"),
26 Cat("Whiskers")
27]
28
29for animal in animals:
30 print(animal.speak())
31# 输出:
32# Generic makes a sound
33# Buddy says: Woof!
34# Whiskers says: Meow!
方法重写的规则:
- 子类方法名与父类相同
- 会覆盖父类的实现
- 调用时优先使用子类的版本
4. super()函数
super()用于调用父类的方法:
1class Animal:
2 """动物类"""
3 def __init__(self, name, age):
4 self.name = name
5 self.age = age
6 print(f"Animal.__init__ called for {name}")
7
8 def get_info(self):
9 return f"Name: {self.name}, Age: {self.age}"
10
11class Dog(Animal):
12 """狗类"""
13 def __init__(self, name, age, breed):
14 """
15 初始化狗
16 - super()调用父类的__init__
17 - 避免代码重复
18 """
19 super().__init__(name, age) # 调用父类初始化
20 self.breed = breed # 添加新属性
21 print(f"Dog.__init__ called for {name}")
22
23 def get_info(self):
24 """重写get_info"""
25 # 调用父类方法,然后添加额外信息
26 parent_info = super().get_info()
27 return f"{parent_info}, Breed: {self.breed}"
28
29# 使用
30dog = Dog("Buddy", 3, "Golden Retriever")
31# 输出:
32# Animal.__init__ called for Buddy
33# Dog.__init__ called for Buddy
34
35print(dog.get_info())
36# 输出:Name: Buddy, Age: 3, Breed: Golden Retriever
super()的作用:
- 调用父类的方法
- 避免硬编码父类名(更灵活)
- 支持多重继承(后续会讲)
- 遵循方法解析顺序(MRO)
不使用super()的问题:
1class Dog(Animal):
2 def __init__(self, name, age, breed):
3 # 不推荐:硬编码父类名
4 Animal.__init__(self, name, age)
5 self.breed = breed
5. 扩展父类功能
添加新属性和方法
1class Vehicle:
2 """交通工具类"""
3 def __init__(self, brand, model):
4 self.brand = brand
5 self.model = model
6
7 def start(self):
8 return f"{self.brand} {self.model} is starting"
9
10class ElectricCar(Vehicle):
11 """电动汽车类"""
12 def __init__(self, brand, model, battery_capacity):
13 super().__init__(brand, model)
14 self.battery_capacity = battery_capacity # 新属性
15
16 def charge(self):
17 """充电(新方法)"""
18 return f"Charging {self.brand} {self.model}"
19
20 def get_range(self):
21 """计算续航(新方法)"""
22 return self.battery_capacity * 5 # 假设每kWh行驶5km
23
24# 使用
25tesla = ElectricCar("Tesla", "Model 3", 75)
26print(tesla.start()) # 继承的方法
27print(tesla.charge()) # 新方法
28print(f"Range: {tesla.get_range()}km") # 新方法
6. 实际应用示例
员工管理系统
1class Employee:
2 """员工基类"""
3
4 # 类属性:员工ID计数器
5 _id_counter = 1000
6
7 def __init__(self, name, department):
8 """初始化员工"""
9 self.name = name
10 self.department = department
11 self.employee_id = Employee._id_counter
12 Employee._id_counter += 1
13
14 def get_info(self):
15 """获取员工信息"""
16 return f"ID: {self.employee_id}, Name: {self.name}, Dept: {self.department}"
17
18 def calculate_salary(self):
19 """计算工资(子类需要实现)"""
20 raise NotImplementedError("子类必须实现calculate_salary方法")
21
22class FullTimeEmployee(Employee):
23 """全职员工"""
24
25 def __init__(self, name, department, monthly_salary):
26 super().__init__(name, department)
27 self.monthly_salary = monthly_salary
28
29 def calculate_salary(self):
30 """计算月薪"""
31 return self.monthly_salary
32
33 def get_info(self):
34 """重写get_info"""
35 base_info = super().get_info()
36 return f"{base_info}, Type: Full-time, Salary: ¥{self.monthly_salary}"
37
38class PartTimeEmployee(Employee):
39 """兼职员工"""
40
41 def __init__(self, name, department, hourly_rate):
42 super().__init__(name, department)
43 self.hourly_rate = hourly_rate
44 self.hours_worked = 0
45
46 def log_hours(self, hours):
47 """记录工作小时"""
48 self.hours_worked += hours
49
50 def calculate_salary(self):
51 """计算兼职工资"""
52 return self.hourly_rate * self.hours_worked
53
54 def get_info(self):
55 """重写get_info"""
56 base_info = super().get_info()
57 salary = self.calculate_salary()
58 return f"{base_info}, Type: Part-time, Earned: ¥{salary}"
59
60# 使用
61full_time = FullTimeEmployee("张三", "技术部", 10000)
62part_time = PartTimeEmployee("李四", "市场部", 100)
63part_time.log_hours(40)
64
65employees = [full_time, part_time]
66for emp in employees:
67 print(emp.get_info())
68 print(f" 本月工资:¥{emp.calculate_salary()}\n")
7. 继承层次
类可以形成继承层次结构:
1class Animal:
2 """动物(最顶层)"""
3 def breathe(self):
4 return "Breathing"
5
6class Mammal(Animal):
7 """哺乳动物(继承Animal)"""
8 def feed_young(self):
9 return "Feeding young with milk"
10
11class Dog(Mammal):
12 """狗(继承Mammal)"""
13 def bark(self):
14 return "Woof!"
15
16class Cat(Mammal):
17 """猫(继承Mammal)"""
18 def meow(self):
19 return "Meow!"
20
21# Dog继承了整个链条的所有方法
22dog = Dog()
23print(dog.breathe()) # 来自Animal
24print(dog.feed_young()) # 来自Mammal
25print(dog.bark()) # 来自Dog自己
继承树:
Animal
└── Mammal
├── Dog
└── Cat
8. 检查继承关系
isinstance()
检查实例类型:
1dog = Dog()
2
3print(isinstance(dog, Dog)) # True
4print(isinstance(dog, Mammal)) # True
5print(isinstance(dog, Animal)) # True
6print(isinstance(dog, Cat)) # False
issubclass()
检查类的继承关系:
1print(issubclass(Dog, Mammal)) # True
2print(issubclass(Dog, Animal)) # True
3print(issubclass(Dog, Dog)) # True(类是自己的子类)
4print(issubclass(Dog, Cat)) # False
访问父类
1print(Dog.__bases__) # (<class '__main__.Mammal'>,)
2print(Dog.__mro__) # Method Resolution Order(方法解析顺序)
3# (<class '__main__.Dog'>, <class '__main__.Mammal'>,
4# <class '__main__.Animal'>, <class 'object'>)
9. object:所有类的基类
Python中所有类都隐式继承自object:
1class MyClass:
2 pass
3
4# 等价于
5class MyClass(object):
6 pass
7
8# object提供了基本方法
9obj = MyClass()
10print(dir(obj)) # 列出所有继承自object的方法
11# __str__, __repr__, __eq__, __hash__等
10. 继承的最佳实践
1. 遵循里氏替换原则
子类应该可以替换父类而不破坏程序:
1def process_animal(animal: Animal):
2 """处理动物"""
3 print(animal.eat())
4
5# 可以传入任何Animal的子类
6process_animal(Dog("Buddy", 3))
7process_animal(Cat("Whiskers", 2))
2. 不要过度使用继承
1# 不好:过深的继承层次
2class A: pass
3class B(A): pass
4class C(B): pass
5class D(C): pass # 太深了
6
7# 好:优先使用组合
8class Engine:
9 def start(self): pass
10
11class Car:
12 def __init__(self):
13 self.engine = Engine() # 组合
3. 使用抽象基类定义接口
1from abc import ABC, abstractmethod
2
3class Shape(ABC):
4 """形状抽象类"""
5
6 @abstractmethod
7 def area(self):
8 """子类必须实现"""
9 pass
10
11class Circle(Shape):
12 def __init__(self, radius):
13 self.radius = radius
14
15 def area(self):
16 return 3.14 * self.radius ** 2
17
18# shape = Shape() # TypeError: 不能实例化抽象类
19circle = Circle(5)
20print(circle.area())
11. 与Golang对比
Golang使用组合而非继承:
1// Go:使用嵌入(embedding)实现类似继承
2type Animal struct {
3 Name string
4}
5
6func (a *Animal) Eat() string {
7 return a.Name + " is eating"
8}
9
10type Dog struct {
11 Animal // 嵌入Animal
12 Breed string
13}
14
15func (d *Dog) Bark() string {
16 return d.Name + " says: Woof!"
17}
18
19// 使用
20dog := Dog{
21 Animal: Animal{Name: "Buddy"},
22 Breed: "Golden Retriever",
23}
24dog.Eat() // 可以调用Animal的方法
25dog.Bark() // Dog自己的方法
对比:
- Python:显式继承,is-a关系
- Go:组合/嵌入,has-a关系
- Python:支持多重继承
- Go:只有组合,没有继承
12. 小结
今天我们学习了继承:
- 定义:class Child(Parent)
- 继承内容:属性和方法
- 方法重写:覆盖父类实现
- super():调用父类方法
- 扩展:添加新功能
- 层次结构:多级继承
- 类型检查:isinstance、issubclass
- object:所有类的基类
- 最佳实践:里氏替换、避免过深层次
继承是代码复用的强大工具,但要合理使用,下一课我们将学习多态。
练习题:
- 创建一个Shape基类和Circle、Rectangle子类,实现面积计算
- 设计一个账户系统,包含Account基类和SavingsAccount、CheckingAccount子类
- 理解并实践方法解析顺序(MRO)
本文代码示例:
关注公众号:极客老墨
更多 AI 应用开发、工程实践和效率工具分享,欢迎扫码关注。
