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:所有类的基类
  • 最佳实践:里氏替换、避免过深层次

继承是代码复用的强大工具,但要合理使用,下一课我们将学习多态。


练习题

  1. 创建一个Shape基类和Circle、Rectangle子类,实现面积计算
  2. 设计一个账户系统,包含Account基类和SavingsAccount、CheckingAccount子类
  3. 理解并实践方法解析顺序(MRO)

本文代码示例

关注公众号:极客老墨

更多 AI 应用开发、工程实践和效率工具分享,欢迎扫码关注。

极客老墨微信公众号二维码

相关阅读