上一篇【第 008 篇】面向对象编程(一):类与对象
下一篇【第 010 篇】Python模块与包:代码组织最佳实践


系列说明:本系列共30篇,涵盖Python开发完整学习路径。本文为第009篇,深入讲解面向对象三大特性:继承、封装与多态的实现与应用。


摘要

本文是面向对象编程系列的第二篇,将深入探讨Python中面向对象的三大核心特性:继承、封装与多态。继承允许我们基于现有类创建新类,实现代码复用和层次化设计;封装通过访问控制保护数据完整性,隐藏实现细节;多态则让不同类的对象能够以统一接口进行操作,增强代码的灵活性和可扩展性。文章将从基础概念出发,通过丰富的代码示例,详细讲解单继承与多继承的实现、方法重写、super()函数的使用、私有属性与@property装饰器、鸭子类型与抽象基类等核心知识点。


一、继承基础

1.1 什么是继承

继承(Inheritance)是面向对象编程的核心特性之一,它允许一个类(子类/派生类)基于另一个类(父类/基类)来构建,自动获得父类的属性和方法。

继承的核心价值:

  • 代码复用:避免重复编写相同的代码
  • 层次化组织:建立清晰的类层次结构
  • 扩展性:在保留原有功能的基础上添加新功能

1.2 单继承的实现

Python中使用以下语法实现单继承:

class Animal:
    """动物基类"""
    
    def __init__(self, name: str, age: int) -> None:
        self.name = name
        self.age = age
    
    def speak(self) -> str:
        return f"{self.name} 发出声音"
    
    def introduce(self) -> str:
        return f"我是{self.name},今年{self.age}岁"


class Dog(Animal):
    """狗类,继承自Animal"""
    
    def __init__(self, name: str, age: int, breed: str) -> None:
        super().__init__(name, age)
        self.breed = breed
    
    def speak(self) -> str:
        return f"{self.name} 说:汪汪汪!"
    
    def fetch(self) -> str:
        return f"{self.name} 正在捡球"


class Cat(Animal):
    """猫类,继承自Animal"""
    
    def speak(self) -> str:
        return f"{self.name} 说:喵喵喵~"
    
    def climb(self) -> str:
        return f"{self.name} 正在爬树"


# 测试代码
dog = Dog("旺财", 3, "金毛")
cat = Cat("咪咪", 2)

print(dog.introduce())  # 继承自Animal的方法
print(dog.speak())      # Dog重写的方法
print(dog.fetch())      # Dog特有的方法
print()
print(cat.introduce())  # 继承自Animal的方法
print(cat.speak())      # Cat重写的方法
print(cat.climb())       # Cat特有的方法

输出结果:

我是旺财,今年3岁
旺财 说:汪汪汪!
旺财 正在捡球

我是咪咪,今年2岁
咪咪 说:喵喵喵~
咪咪 正在爬树

1.3 多继承的语法

Python支持多继承,即一个类可以同时继承多个父类:

class Flyable:
    """可飞行接口"""
    
    def fly(self) -> str:
        return f"{self.name} 在天空中飞翔"


class Swimmable:
    """可游泳接口"""
    
    def swim(self) -> str:
        return f"{self.name} 在水中游泳"


class Duck(Animal, Flyable, Swimmable):
    """鸭子类,继承自动物、可飞行、可游泳"""
    
    def speak(self) -> str:
        return f"{self.name} 说:嘎嘎嘎!"


duck = Duck("唐老鸭", 5)
print(duck.speak())   # Duck重写的方法
print(duck.fly())     # 继承自Flyable
print(duck.swim())    # 继承自Swimmable

1.4 方法解析顺序(MRO)

当使用多继承时,Python需要确定方法调用的顺序,这就是方法解析顺序(Method Resolution Order, MRO)

class A:
    def method(self):
        print("A.method")


class B(A):
    def method(self):
        print("B.method")


class C(A):
    def method(self):
        print("C.method")


class D(B, C):
    pass


# 查看MRO
print(D.__mro__)
# 输出:(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)

d = D()
d.method()  # 输出:B.method(按MRO顺序找到的第一个method)

二、子类方法重写

2.1 方法重写基础

方法重写(Override)是指子类重新定义父类中已有的方法。

class Vehicle:
    """交通工具基类"""
    
    def __init__(self, brand: str) -> None:
        self.brand = brand
    
    def start(self) -> str:
        return f"{self.brand} 车辆启动"


class ElectricCar(Vehicle):
    """电动汽车"""
    
    def __init__(self, brand: str, battery_capacity: float) -> None:
        super().__init__(brand)
        self.battery_capacity = battery_capacity
    
    def start(self) -> str:
        return f"{self.brand} 电动汽车静音启动"
    
    def charge(self) -> str:
        return f"{self.brand} 充电完成"


tesla = ElectricCar("特斯拉", 75.0)
print(tesla.start())   # 调用重写后的方法
print(tesla.stop())    # 调用继承自父类的方法

2.2 super() 函数调用父类方法

super()函数用于调用父类(超类)的方法:

class Rectangle:
    """矩形类"""
    
    def __init__(self, width: float, height: float) -> None:
        self.width = width
        self.height = height
    
    def area(self) -> float:
        return self.width * self.height
    
    def describe(self) -> str:
        return f"矩形:宽{self.width},高{self.height},面积{self.area()}"


class Square(Rectangle):
    """正方形类"""
    
    def __init__(self, side: float) -> None:
        super().__init__(side, side)
    
    def describe(self) -> str:
        base_desc = super().describe()
        return f"{base_desc}\n这是一个正方形,边长为{self.width}"


square = Square(5)
print(square.describe())

三、封装实现

3.1 私有属性的保护

封装(Encapsulation)是隐藏对象内部实现细节的编程原则。Python通过命名约定来实现访问控制:

class BankAccount:
    """银行账户类"""
    
    def __init__(self, owner: str, initial_balance: float = 0.0) -> None:
        self.owner = owner
        self._transaction_history = []  # 受保护属性
        self.__balance = initial_balance  # 私有属性
    
    def deposit(self, amount: float) -> str:
        if amount <= 0:
            return "存款金额必须大于0"
        self.__balance += amount
        self._transaction_history.append(f"存款: +{amount}")
        return f"成功存入 {amount} 元"
    
    def get_balance(self) -> float:
        return self.__balance


account = BankAccount("张三", 1000.0)
print(account.deposit(500.0))

# 尝试直接访问私有属性
# print(account.__balance)  # 会报错:AttributeError
print(f"余额通过方法访问:{account.get_balance()}")

3.2 使用@property实现优雅封装

class Circle:
    """圆形类"""
    
    def __init__(self, radius: float) -> None:
        self._radius = radius
    
    @property
    def radius(self) -> float:
        """半径的Getter"""
        return self._radius
    
    @radius.setter
    def radius(self, value: float) -> None:
        if value < 0:
            raise ValueError("半径不能为负数")
        self._radius = value
    
    @property
    def area(self) -> float:
        """面积(只读属性)"""
        import math
        return math.pi * self._radius ** 2


circle = Circle(5.0)
print(f"半径:{circle.radius}")
print(f"面积:{circle.area:.2f}")

circle.radius = 10.0
print(f"修改后面积:{circle.area:.2f}")

四、多态应用

4.1 什么是多态

多态(Polymorphism)是指不同类型的对象对同一消息做出不同响应的能力。

class Duck:
    def quack(self) -> str:
        return "嘎嘎嘎!"


class Person:
    def quack(self) -> str:
        return "人在模仿鸭子叫:嘎嘎嘎~"


def make_it_quack(obj) -> None:
    """鸭子类型示例"""
    print(f"叫声:{obj.quack()}")


duck = Duck()
person = Person()

make_it_quack(duck)
make_it_quack(person)

4.2 抽象基类(ABC)

Python的abc模块提供了定义抽象基类的机制:

from abc import ABC, abstractmethod
from typing import List


class Shape(ABC):
    """形状抽象基类"""
    
    @abstractmethod
    def area(self) -> float:
        """计算面积"""
        pass
    
    @abstractmethod
    def perimeter(self) -> float:
        """计算周长"""
        pass


class Rectangle(Shape):
    def __init__(self, width: float, height: float) -> None:
        self.width = width
        self.height = height
    
    def area(self) -> float:
        return self.width * self.height
    
    def perimeter(self) -> float:
        return 2 * (self.width + self.height)


class Circle(Shape):
    def __init__(self, radius: float) -> None:
        self.radius = radius
    
    def area(self) -> float:
        import math
        return math.pi * self.radius ** 2
    
    def perimeter(self) -> float:
        import math
        return 2 * math.pi * self.radius


def calculate_total_area(shapes: List[Shape]) -> float:
    return sum(shape.area() for shape in shapes)


shapes = [Rectangle(5, 3), Circle(4)]
print(f"总面积:{calculate_total_area(shapes):.2f}")

五、组合与继承的选择

5.1 继承的适用场景

继承适用于明确的"是一个"关系

class Employee:
    def __init__(self, name: str, salary: float) -> None:
        self.name = name
        self.salary = salary


class Manager(Employee):
    """经理是一个员工(is-a关系)"""
    def __init__(self, name: str, salary: float, department: str) -> None:
        super().__init__(name, salary)
        self.department = department

5.2 组合的适用场景

组合适用于**"有一个"关系**:

class Engine:
    def __init__(self, horsepower: int) -> None:
        self.horsepower = horsepower


class Car:
    """汽车由多个部件组合而成"""
    def __init__(self, brand: str, engine: Engine) -> None:
        self.brand = brand
        self.engine = engine


v8_engine = Engine(400)
sports_car = Car("跑车", v8_engine)

六、综合实战:设计一个动物园系统

from abc import ABC, abstractmethod
from typing import List


class Animal(ABC):
    """动物抽象基类"""
    
    def __init__(self, name: str, age: int, food_per_day: float) -> None:
        self.name = name
        self.age = age
        self.food_per_day = food_per_day
    
    @property
    @abstractmethod
    def species(self) -> str:
        pass
    
    @abstractmethod
    def make_sound(self) -> str:
        pass
    
    def eat(self, food_amount: float) -> str:
        return f"{self.name} 吃了 {food_amount}kg 食物"


class Lion(Animal):
    @property
    def species(self) -> str:
        return "狮子"
    
    def make_sound(self) -> str:
        return f"{self.name} 发出吼叫:ROAR!!!"


class Monkey(Animal):
    @property
    def species(self) -> str:
        return "猴子"
    
    def make_sound(self) -> str:
        return f"{self.name} 吱吱叫"


class Zoo:
    """动物园"""
    
    def __init__(self, name: str) -> None:
        self.name = name
        self.animals: List[Animal] = []
    
    def add_animal(self, animal: Animal) -> None:
        self.animals.append(animal)
        print(f"欢迎 {animal.species} {animal.name} 加入{self.name}!")
    
    def show_animals(self) -> None:
        for animal in self.animals:
            print(f"- {animal.species} {animal.name}{animal.age}岁")


# 测试
zoo = Zoo("快乐动物园")
zoo.add_animal(Lion("辛巴", 5, 8.0))
zoo.add_animal(Monkey("阿布", 3, 2.5))
zoo.show_animals()

七、常见问题与注意事项

7.1 继承相关注意事项

  1. 避免过深的继承层次:继承层次太深会增加代码复杂度
  2. 菱形继承问题:使用MRO机制妥善处理
  3. 遵循里氏替换原则:子类应该能够替换父类

7.2 封装相关注意事项

  1. 不要过度使用私有属性
  2. 合理使用@property

7.3 多态相关注意事项

  1. 鸭子类型 vs 抽象基类的选择
  2. 谨慎使用isinstance()

八、总结

本文深入讲解了Python面向对象编程的三大核心特性:

  1. 继承:通过继承实现代码复用,支持单继承和多继承
  2. 封装:通过访问控制保护数据,@property实现优雅封装
  3. 多态:通过鸭子类型和抽象基类实现灵活的多态

遵循"组合优于继承"的设计原则,构建可维护的代码结构。


上一篇【第 008 篇】面向对象编程(一):类与对象
下一篇【第 010 篇】Python模块与包:代码组织最佳实践


参考资料

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐