目录

Python 继承与多态

学习目标

  • 理解继承的概念和作用
  • 掌握单继承和多继承的用法
  • 理解 super() 的作用和使用方法
  • 掌握方法重写和多态的实现

一、继承基础

继承是面向对象编程的核心特性之一,允许一个类(子类)继承另一个类(父类)的属性和方法。

# 父类(基类)
class Animal:
    def __init__(self, name):
        self.name = name

    def speak(self):
        return f"{self.name} 发出声音"

    def move(self):
        return f"{self.name} 在移动"


# 子类(派生类)
class Dog(Animal):
    def speak(self):
        return f"{self.name} 说: 汪汪!"


class Cat(Animal):
    def speak(self):
        return f"{self.name} 说: 喵喵!"


# 使用
dog = Dog("Buddy")
cat = Cat("Luna")

print(dog.speak())    # Buddy 说: 汪汪!
print(cat.speak())    # Luna 说: 喵喵!
print(dog.move())     # Buddy 在移动(继承自 Animal)

二、super() 函数

super() 用于调用父类的方法,常用于在子类中扩展父类的功能。

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def introduce(self):
        return f"我是 {self.name}{self.age} 岁"


class Student(Person):
    def __init__(self, name, age, grade):
        # 调用父类的 __init__
        super().__init__(name, age)
        self.grade = grade

    def introduce(self):
        # 调用父类的 introduce 并扩展
        base = super().introduce()
        return f"{base},我在 {self.grade} 年级"


s = Student("Alice", 15, "初三")
print(s.introduce())
# 我是 Alice,15 岁,我在 初三年级

三、方法重写

子类可以重写(覆盖)父类的方法,以提供特定的实现。

class Shape:
    def __init__(self, name):
        self.name = name

    def area(self):
        raise NotImplementedError("子类必须实现 area 方法")

    def perimeter(self):
        raise NotImplementedError("子类必须实现 perimeter 方法")

    def info(self):
        return f"{self.name}: 面积={self.area():.2f}, 周长={self.perimeter():.2f}"


class Rectangle(Shape):
    def __init__(self, width, height):
        super().__init__("矩形")
        self.width = width
        self.height = height

    def area(self):
        return self.width * self.height

    def perimeter(self):
        return 2 * (self.width + self.height)


class Circle(Shape):
    PI = 3.14159

    def __init__(self, radius):
        super().__init__("圆形")
        self.radius = radius

    def area(self):
        return self.PI * self.radius ** 2

    def perimeter(self):
        return 2 * self.PI * self.radius


# 使用
rect = Rectangle(5, 3)
circle = Circle(4)

print(rect.info())     # 矩形: 面积=15.00, 周长=16.00
print(circle.info())   # 圆形: 面积=50.27, 周长=25.13

四、多态

多态是指同一个方法调用,作用于不同的对象,产生不同的行为。

class Animal:
    def speak(self):
        return "动物发出声音"


class Dog(Animal):
    def speak(self):
        return "汪汪!"


class Cat(Animal):
    def speak(self):
        return "喵喵!"


class Duck(Animal):
    def speak(self):
        return "嘎嘎!"


# 多态:统一的接口,不同的行为
def animal_concert(animals):
    for animal in animals:
        print(f"{type(animal).__name__}: {animal.speak()}")


# 创建不同的动物
animals = [Dog(), Cat(), Duck()]
animal_concert(animals)
# Dog: 汪汪!
# Cat: 喵喵!
# Duck: 嘎嘎!

# Python 的鸭子类型
def make_it_speak(obj):
    """只要有 speak 方法就行,不需要继承关系"""
    print(obj.speak())


class Robot:
    def speak(self):
        return "哔哔啵啵"


make_it_speak(Dog())     # 汪汪!
make_it_speak(Robot())   # 哔哔啵啵

五、多继承

Python 支持多继承,一个子类可以继承多个父类。

class Flyable:
    def fly(self):
        return "我能飞"


class Swimmable:
    def swim(self):
        return "我能游泳"


class Duck(Flyable, Swimmable):
    def speak(self):
        return "嘎嘎"


duck = Duck()
print(duck.fly())        # 我能飞
print(duck.swim())       # 我能游泳
print(duck.speak())      # 嘎嘎

MRO(方法解析顺序)

当多继承出现同名方法时,Python 使用 C3 线性化算法确定调用顺序。

class A:
    def method(self):
        return "A"


class B(A):
    def method(self):
        return "B"


class C(A):
    def method(self):
        return "C"


class D(B, C):
    pass


d = D()
print(d.method())        # B(按 MRO 顺序)

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

# 使用 super() 按 MRO 调用
class E(B, C):
    def method(self):
        return f"E -> {super().method()}"


e = E()
print(e.method())        # E -> B

菱形继承问题

class A:
    def __init__(self):
        print("A.__init__")
        self.a = "a"


class B(A):
    def __init__(self):
        print("B.__init__")
        super().__init__()   # 使用 super 避免重复调用
        self.b = "b"


class C(A):
    def __init__(self):
        print("C.__init__")
        super().__init__()
        self.c = "c"


class D(B, C):
    def __init__(self):
        print("D.__init__")
        super().__init__()
        self.d = "d"


d = D()
# D.__init__
# B.__init__
# C.__init__
# A.__init__
# A 只被调用一次!
print(d.a, d.b, d.c, d.d)   # a b c d

六、isinstanceissubclass

class Animal:
    pass


class Dog(Animal):
    pass


dog = Dog()

# isinstance:检查对象是否是指定类的实例
print(isinstance(dog, Dog))       # True
print(isinstance(dog, Animal))    # True(继承关系)
print(isinstance(dog, object))    # True(所有类都继承 object)

# issubclass:检查类是否是另一个类的子类
print(issubclass(Dog, Animal))    # True
print(issubclass(Dog, object))    # True
print(issubclass(Animal, Dog))    # False

七、抽象基类

抽象基类(Abstract Base Class, ABC)定义了接口规范,子类必须实现指定的方法。

from abc import ABC, abstractmethod


class Shape(ABC):
    @abstractmethod
    def area(self):
        """计算面积"""
        pass

    @abstractmethod
    def perimeter(self):
        """计算周长"""
        pass

    def info(self):
        return f"面积={self.area()}, 周长={self.perimeter()}"


class Rectangle(Shape):
    def __init__(self, width, height):
        self.width = width
        self.height = height

    def area(self):
        return self.width * self.height

    def perimeter(self):
        return 2 * (self.width + self.height)


# shape = Shape()       # TypeError: 不能实例化抽象类
rect = Rectangle(5, 3)
print(rect.info())      # 面积=15, 周长=16

八、综合示例

"""
员工管理系统 - 继承与多态应用
"""

from abc import ABC, abstractmethod


class Employee(ABC):
    """员工基类"""

    def __init__(self, name, employee_id):
        self.name = name
        self.employee_id = employee_id

    @abstractmethod
    def calculate_salary(self):
        """计算薪资(子类必须实现)"""
        pass

    def info(self):
        return f"{self.name} ({self.employee_id}) - 月薪: {self.calculate_salary():.2f}"


class SalariedEmployee(Employee):
    """固定薪资员工"""

    def __init__(self, name, employee_id, monthly_salary):
        super().__init__(name, employee_id)
        self.monthly_salary = monthly_salary

    def calculate_salary(self):
        return self.monthly_salary


class HourlyEmployee(Employee):
    """时薪员工"""

    def __init__(self, name, employee_id, hourly_rate, hours_worked):
        super().__init__(name, employee_id)
        self.hourly_rate = hourly_rate
        self.hours_worked = hours_worked

    def calculate_salary(self):
        return self.hourly_rate * self.hours_worked


class CommissionEmployee(Employee):
    """佣金员工"""

    def __init__(self, name, employee_id, base_salary, sales, commission_rate):
        super().__init__(name, employee_id)
        self.base_salary = base_salary
        self.sales = sales
        self.commission_rate = commission_rate

    def calculate_salary(self):
        return self.base_salary + self.sales * self.commission_rate


# 创建员工列表(多态)
employees = [
    SalariedEmployee("Alice", "E001", 15000),
    HourlyEmployee("Bob", "E002", 100, 160),
    CommissionEmployee("Charlie", "E003", 8000, 50000, 0.1),
]

# 统一处理(多态)
print("=" * 50)
print("      员 工 薪 资 报 表")
print("=" * 50)
for emp in employees:
    print(emp.info())

# 计算总薪资
total = sum(emp.calculate_salary() for emp in employees)
print("=" * 50)
print(f"总薪资支出: {total:.2f}")

小结

  • 继承使用 class Child(Parent) 语法,子类继承父类的属性和方法
  • super() 调用父类方法,常用于扩展构造方法
  • 方法重写让子类提供特定实现,实现多态
  • Python 支持多继承,使用 MRO 确定方法调用顺序
  • 抽象基类(ABC)定义接口规范,子类必须实现抽象方法
  • isinstance() 检查对象类型,issubclass() 检查继承关系

练习

  1. 定义一个 Vehicle 基类,再定义 CarBicycle 子类,重写 move() 方法。
  2. 使用 super() 在子类中扩展父类的 __init__ 方法。
  3. 创建一个抽象基类 Payment,定义 pay(amount) 抽象方法,然后实现 AlipayWechatPay 子类。
  4. 分析以下多继承的 MRO 输出:
    class A: pass
    class B(A): pass
    class C(A): pass
    class D(B, C): pass
    print(D.__mro__)
  5. 解释 Python 的"鸭子类型",并举例说明。