Python & AI Tutorials Logo
Python 编程

32. 使用继承与多态扩展类

在第 30 章中,我们学习了如何创建自己的类来建模现实世界的概念。我们构建了像 BankAccountStudent 这样的类,把数据和行为打包在一起。但当你需要创建一个与现有类相似、但有一些差异或新增功能的新类时,会发生什么呢?

继承(inheritance) 是 Python 用于基于现有类创建新类的机制。你不需要复制粘贴代码,而是可以创建一个 子类(subclass),它会自动获得 父类(parent class)(也称为 基类(base class)超类(superclass))中的所有属性和方法,然后再添加或修改你需要的部分。

本章将探讨继承如何让你构建相关类的层级体系、如何自定义继承来的行为,以及 多态(polymorphism) 如何允许不同的类在共享公共接口时可以被互换使用。

32.1) 从现有类创建子类

32.1.1) 继承的基本语法

继承允许你基于一个现有类(称为 父类(parent class)基类(base class))创建一个新类(称为 子类(subclass)子类(child class))。子类会自动继承父类的所有方法和属性。

创建子类时,你需要在类名后面的括号中指定父类。

python
# 父类
class Animal:
    def __init__(self, name):
        self.name = name
    
    def speak(self):
        return f"{self.name} makes a sound"
 
# 从 Animal 继承的子类
class Dog(Animal):
    pass  # 目前还不需要额外代码
 
# 创建一个 Dog 实例
buddy = Dog("Buddy")
print(buddy.speak())  # Output: Buddy makes a sound
print(buddy.name)     # Output: Buddy

即使 Dog 自己没有任何代码(只有 pass),它也会继承 Animal 的所有内容。Dog 类会自动拥有来自父类的 __init__ 方法和 speak 方法。

Animal

+name

+init(name)

+speak()

Dog

32.1.2) 为什么继承很重要

继承解决了一个常见的编程问题:代码重复。假设你正在构建一个系统来管理不同类型的员工:

python
# 不使用继承——大量重复
class FullTimeEmployee:
    def __init__(self, name, employee_id, salary):
        self.name = name
        self.employee_id = employee_id
        self.salary = salary
    
    def get_info(self):
        return f"{self.name} (ID: {self.employee_id})"
 
class PartTimeEmployee:
    def __init__(self, name, employee_id, hourly_rate):
        self.name = name
        self.employee_id = employee_id
        self.hourly_rate = hourly_rate
    
    def get_info(self):
        return f"{self.name} (ID: {self.employee_id})"

注意到 nameemployee_id 以及 get_info() 是重复的。通过继承,我们可以消除这类重复:

python
# 使用继承——共享代码放在父类中
class Employee:
    def __init__(self, name, employee_id):
        self.name = name
        self.employee_id = employee_id
    
    def get_info(self):
        return f"{self.name} (ID: {self.employee_id})"
 
class FullTimeEmployee(Employee):
    def __init__(self, name, employee_id, salary):
        Employee.__init__(self, name, employee_id)  # 调用父类的 __init__
        # Note: We'll learn a better way to do this with super() in Section 32.3
        self.salary = salary
 
class PartTimeEmployee(Employee):
    def __init__(self, name, employee_id, hourly_rate):
        Employee.__init__(self, name, employee_id)  # 调用父类的 __init__
        self.hourly_rate = hourly_rate
 
# 两个子类都继承了 get_info()
alice = FullTimeEmployee("Alice", "E001", 75000)
bob = PartTimeEmployee("Bob", "E002", 25)
 
print(alice.get_info())  # Output: Alice (ID: E001)
print(bob.get_info())    # Output: Bob (ID: E002)

现在,公共属性和方法都放在 Employee 中,每个子类只需要定义让自己独特的部分。

32.1.3) 为子类添加新方法

子类可以添加父类没有的自有方法:

python
class Vehicle:
    def __init__(self, brand, model):
        self.brand = brand
        self.model = model
    
    def get_description(self):
        return f"{self.brand} {self.model}"
 
class Car(Vehicle):
    def __init__(self, brand, model, num_doors):
        Vehicle.__init__(self, brand, model)
        self.num_doors = num_doors
    
    def honk(self):  # Car 特有的新方法
        return "Beep beep!"
 
class Motorcycle(Vehicle):
    def __init__(self, brand, model, has_sidecar):
        Vehicle.__init__(self, brand, model)
        self.has_sidecar = has_sidecar
    
    def rev_engine(self):  # Motorcycle 特有的新方法
        return "Vroom vroom!"
 
# 每个子类既有父类的方法,也有自己的方法
my_car = Car("Toyota", "Camry", 4)
print(my_car.get_description())  # Output: Toyota Camry
print(my_car.honk())             # Output: Beep beep!
 
my_bike = Motorcycle("Harley", "Sportster", False)
print(my_bike.get_description())  # Output: Harley Sportster
print(my_bike.rev_engine())       # Output: Vroom vroom!

Car 类同时拥有 get_description()(继承而来)和 honk()(自身的方法)。Motorcycle 类拥有 get_description()(继承而来)和 rev_engine()(自身的方法)。

32.1.4) 为子类添加新属性

子类也可以添加自己的实例属性。你通常会在子类的 __init__ 方法中做这件事:

python
class BankAccount:
    def __init__(self, account_number, balance):
        self.account_number = account_number
        self.balance = balance
    
    def deposit(self, amount):
        self.balance += amount
        return self.balance
 
class SavingsAccount(BankAccount):
    def __init__(self, account_number, balance, interest_rate):
        BankAccount.__init__(self, account_number, balance)
        self.interest_rate = interest_rate  # 新属性
    
    def apply_interest(self):  # 使用新属性的新方法
        interest = self.balance * self.interest_rate
        self.balance += interest
        return interest
 
# SavingsAccount 拥有 BankAccount 的所有属性,再加上 interest_rate
savings = SavingsAccount("SA001", 1000, 0.03)
savings.deposit(500)  # 继承的方法
print(f"Balance: ${savings.balance}")  # Output: Balance: $1500
 
interest_earned = savings.apply_interest()
print(f"Interest earned: ${interest_earned:.2f}")  # Output: Interest earned: $45.00
print(f"New balance: ${savings.balance:.2f}")      # Output: New balance: $1545.00

SavingsAccount 具有来自 BankAccountaccount_numberbalance,同时还有自己的 interest_rate 属性。

32.1.5) 多层继承

类可以继承自那些本身也继承自其他类的类,从而形成继承层级结构:

python
class LivingThing:
    def __init__(self, name):
        self.name = name
    
    def is_alive(self):
        return True
 
class Animal(LivingThing):
    def __init__(self, name, species):
        LivingThing.__init__(self, name)
        self.species = species
    
    def move(self):
        return f"{self.name} is moving"
 
class Dog(Animal):
    def __init__(self, name, breed):
        Animal.__init__(self, name, "Dog")
        self.breed = breed
    
    def bark(self):
        return f"{self.name} says: Woof!"
 
# Dog 继承自 Animal,而 Animal 又继承自 LivingThing
max_dog = Dog("Max", "Golden Retriever")
 
# 三个层级中的方法都可用
print(max_dog.is_alive())  # Output: True (from LivingThing)
print(max_dog.move())      # Output: Max is moving (from Animal)
print(max_dog.bark())      # Output: Max says: Woof! (from Dog)
 
# 三个层级中的属性都存在
print(max_dog.name)     # Output: Max (from LivingThing)
print(max_dog.species)  # Output: Dog (from Animal)
print(max_dog.breed)    # Output: Golden Retriever (from Dog)

LivingThing

+name

+is_alive()

Animal

+species

+move()

Dog

+breed

+bark()

Dog 继承自 Animal,而 Animal 又继承自 LivingThing。这意味着 Dog 可以访问来自两个父类的所有方法和属性。

32.2) 在子类中重写方法

32.2.1) 方法重写是什么意思

有时子类需要改变某个继承方法的工作方式。方法重写(method overriding) 指的是在子类中定义一个与父类方法同名的方法。当你在子类实例上调用该方法时,Python 会使用子类版本,而不是父类版本。

python
class Animal:
    def __init__(self, name):
        self.name = name
    
    def speak(self):
        return f"{self.name} makes a sound"
 
class Dog(Animal):
    def speak(self):  # 重写父类的 speak 方法
        return f"{self.name} says: Woof!"
 
class Cat(Animal):
    def speak(self):  # 用不同的行为进行重写
        return f"{self.name} says: Meow!"
 
# 每个类都有自己版本的 speak()
generic_animal = Animal("Generic")
print(generic_animal.speak())  # Output: Generic makes a sound
 
buddy = Dog("Buddy")
print(buddy.speak())  # Output: Buddy says: Woof!
 
whiskers = Cat("Whiskers")
print(whiskers.speak())  # Output: Whiskers says: Meow!

当你调用 buddy.speak() 时,Python 会先在 Dog 类中查找 speak 方法,因为 buddyDog 的实例。由于 Dog 定义了自己的 speak 方法,Python 就使用该版本。如果 Dog 没有 speak 方法,Python 才会去父类 Animal 中查找并使用父类版本。

这种查找顺序——从实例所属的类开始,然后移动到父类——就是方法重写如何工作的方式,也是子类如何自定义继承行为的方式。

32.2.2) 为什么要重写方法?

方法重写让你能够为通用行为创建更专门的版本。考虑一个形状层级:

python
class Shape:
    def __init__(self, name):
        self.name = name
    
    def area(self):
        return 0  # 默认实现
    
    def describe(self):
        return f"{self.name} with area {self.area()}"
 
class Rectangle(Shape):
    def __init__(self, width, height):
        Shape.__init__(self, "Rectangle")
        self.width = width
        self.height = height
    
    def area(self):  # 用矩形特有的计算方式重写
        return self.width * self.height
 
class Circle(Shape):
    def __init__(self, radius):
        Shape.__init__(self, "Circle")
        self.radius = radius
    
    def area(self):  # 用圆形特有的计算方式重写
        return 3.14159 * self.radius ** 2
 
# 每种形状用不同方式计算面积
rect = Rectangle(5, 3)
print(rect.describe())  # Output: Rectangle with area 15
 
circle = Circle(4)
print(circle.describe())  # Output: Circle with area 50.26544

describe() 方法被两个子类继承,并且能够正确工作,因为每个子类都提供了自己的 area() 实现。

当你调用 rect.describe() 时,继承来的 describe() 方法会执行,但 self 指向 Rectangle 实例。因此当 describe() 调用 self.area() 时,Python 会先在 Rectangle 类中查找 area() 并找到被重写的版本。

32.2.3) 重写 __init__ 并调用父类初始化

当你重写 __init__ 时,你通常需要调用父类的 __init__,以确保父类的初始化能够执行:

python
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    def introduce(self):
        return f"I'm {self.name}, {self.age} years old"
 
class Student(Person):
    def __init__(self, name, age, student_id, major):
        # 调用父类的 __init__ 来设置 name 和 age
        Person.__init__(self, name, age)
        # 然后设置学生特有的属性
        self.student_id = student_id
        self.major = major
    
alice = Student("Alice", 20, "S12345", "Computer Science")
print(alice.name)        # Output: Alice
print(alice.student_id)  # Output: S12345

注意 Student.__init__ 会先调用 Person.__init__(self, name, age) 来初始化父类的属性,然后再添加自己的属性。

32.3) 使用 super() 访问父类行为

32.3.1) super() 的作用是什么

在前一节中,我们显式调用父类方法:ParentClass.method(self, ...)。Python 提供了一个更简洁的方法:super() 函数。super() 会返回一个临时对象,使你无需显式写出父类名,就能调用父类的方法。

python
class Animal:
    def __init__(self, name):
        self.name = name
    
    def speak(self):
        return f"{self.name} makes a sound"
 
class Dog(Animal):
    def __init__(self, name, breed):
        super().__init__(name)  # 比 Animal.__init__(self, name) 更简洁
        self.breed = breed
    
    def speak(self):
        parent_sound = super().speak()  # 调用父类的 speak()
        return f"{parent_sound} - specifically, Woof!"
 
buddy = Dog("Buddy", "Labrador")
print(buddy.speak())
# Output: Buddy makes a sound - specifically, Woof!

使用 super() 有几个优点:

  • 你不需要显式写出父类名称
  • 它能在多重继承中正确工作(后面会讲)
  • 如果你修改了父类,代码更容易维护

32.3.2) 在 __init__ 中使用 super()

super() 最常见的用途是调用父类的 __init__

python
class Employee:
    def __init__(self, name, employee_id):
        self.name = name
        self.employee_id = employee_id
        self.is_active = True
    
    def deactivate(self):
        self.is_active = False
 
class Manager(Employee):
    def __init__(self, name, employee_id, department):
        super().__init__(name, employee_id)  # 初始化父类属性
        self.department = department
        self.team = []  # Manager 特有的属性
    
    def add_team_member(self, employee):
        self.team.append(employee)
 
# Manager 拥有 Employee 的所有属性,并加上自己的属性
sarah = Manager("Sarah", "M001", "Engineering")
print(sarah.name)        # Output: Sarah
print(sarah.is_active)   # Output: True
print(sarah.department)  # Output: Engineering

通过调用 super().__init__(name, employee_id)Manager 类确保 Employee 中的所有初始化逻辑都会运行,包括将 is_active 设置为 True

32.3.3) 用 super() 扩展父类方法

你可以使用 super() 来扩展父类的方法,而不是完全替换它:

python
class BankAccount:
    def __init__(self, account_number, balance):
        self.account_number = account_number
        self.balance = balance
        self.transaction_count = 0
    
    def deposit(self, amount):
        self.balance += amount
        self.transaction_count += 1
        return self.balance
 
class CheckingAccount(BankAccount):
    def __init__(self, account_number, balance, overdraft_limit):
        super().__init__(account_number, balance)
        self.overdraft_limit = overdraft_limit
    
    def deposit(self, amount):
        was_overdrawn = self.balance < 0
        
        # 调用父类的 deposit 来处理基本逻辑
        new_balance = super().deposit(amount)
        
        # 添加支票账户特有的行为
        if was_overdrawn and new_balance >= 0:
            print("Account is no longer overdrawn")
        return new_balance
 
checking = CheckingAccount("C001", -50, 100)
checking.deposit(75)
# Output: Account is no longer overdrawn
print(f"Balance: ${checking.balance}")  # Output: Balance: $25
print(f"Transactions: {checking.transaction_count}")  # Output: Transactions: 1

CheckingAccount.deposit() 方法调用 super().deposit(amount) 来处理基础的存款逻辑(更新余额与交易次数),然后再加入自己对透支状态的检查。

32.3.4) 何时使用 super() vs 直接调用父类

在大多数情况下使用 super()

python
class Vehicle:
    def __init__(self, brand):
        self.brand = brand
 
class Car(Vehicle):
    def __init__(self, brand, model):
        super().__init__(brand)  # 推荐
        self.model = model

当你在多重继承场景中需要调用特定父类(后面会讲),或者你想明确指出要调用哪个父类时,可以直接调用父类:

python
class Car(Vehicle):
    def __init__(self, brand, model):
        Vehicle.__init__(self, brand)  # 显式,但灵活性较差
        self.model = model

对于单继承(一个父类)来说,super() 几乎总是更好的选择。

32.3.5) super() 与其他方法

你可以对任何方法使用 super(),不仅限于 __init__

python
class TextProcessor:
    def process(self, text):
        # 基础处理:去掉首尾空白
        return text.strip()
 
class UppercaseProcessor(TextProcessor):
    def process(self, text):
        # 先进行父类的处理
        processed = super().process(text)
        # 然后转换为大写
        return processed.upper()
 
class PrefixProcessor(UppercaseProcessor):
    def __init__(self, prefix):
        self.prefix = prefix
    
    def process(self, text):
        # 先进行父类的处理(它也会调用自己的父类)
        processed = super().process(text)
        # 然后添加前缀
        return f"{self.prefix}: {processed}"
 
processor = PrefixProcessor("ALERT")
result = processor.process("  system error  ")
print(result)  # Output: ALERT: SYSTEM ERROR

32.4) 多态:与兼容的类协作

32.4.1) 多态是什么意思

多态(polymorphism)(源自希腊语:“多种形式”)是指:如果不同类的对象提供了相同的方法,就可以用同样的方式对待它们。

在 Python 中,如果多个类拥有同名方法,你就可以在不知道对象确切类名的情况下调用这些方法。

python
class Dog:
    def __init__(self, name):
        self.name = name
    
    def speak(self):
        return f"{self.name} says: Woof!"
 
class Cat:
    def __init__(self, name):
        self.name = name
    
    def speak(self):
        return f"{self.name} says: Meow!"
 
class Bird:
    def __init__(self, name):
        self.name = name
    
    def speak(self):
        return f"{self.name} says: Tweet!"
 
# 适用于任何拥有 speak() 方法的对象的函数
def make_animal_speak(animal):
    print(animal.speak())
 
# 可用于不同的类
buddy = Dog("Buddy")
whiskers = Cat("Whiskers")
tweety = Bird("Tweety")
 
make_animal_speak(buddy)     # Output: Buddy says: Woof!
make_animal_speak(whiskers)  # Output: Whiskers says: Meow!
make_animal_speak(tweety)    # Output: Tweety says: Tweet!

make_animal_speak() 函数并不关心参数 animal 到底是什么类——它只需要该对象有一个 speak() 方法。这就是多态在发挥作用。

32.4.2) 结合继承的多态

多态与继承结合时尤其强大,因为子类可以重写父类方法:

python
class PaymentMethod:
    def process_payment(self, amount):
        return f"Processing ${amount:.2f}"
 
class CreditCard(PaymentMethod):
    def __init__(self, card_number):
        self.card_number = card_number
    
    def process_payment(self, amount):
        return f"Charging ${amount:.2f} to credit card ending in {self.card_number[-4:]}"
 
class PayPal(PaymentMethod):
    def __init__(self, email):
        self.email = email
    
    def process_payment(self, amount):
        return f"Sending ${amount:.2f} via PayPal to {self.email}"
 
class BankTransfer(PaymentMethod):
    def __init__(self, account_number):
        self.account_number = account_number
    
    def process_payment(self, amount):
        return f"Transferring ${amount:.2f} to account {self.account_number}"
 
# 适用于任何 PaymentMethod 的函数
def complete_purchase(payment_method, amount):
    print(payment_method.process_payment(amount))
    print("Purchase complete!")
 
# 所有支付方式都可以用同一个函数
credit = CreditCard("1234567890123456")
paypal = PayPal("user@example.com")
bank = BankTransfer("9876543210")
 
complete_purchase(credit, 99.99)
# Output: Charging $99.99 to credit card ending in 3456
# Output: Purchase complete!
 
complete_purchase(paypal, 49.50)
# Output: Sending $49.50 via PayPal to user@example.com
# Output: Purchase complete!
 
complete_purchase(bank, 199.00)
# Output: Transferring $199.00 to account 9876543210
# Output: Purchase complete!

complete_purchase() 函数适用于任何 PaymentMethod 子类。每个子类都提供了自己的 process_payment() 实现,但函数不需要知道它正在使用的是哪个具体类。

32.4.3) 鸭子类型:"如果它走起来像鸭子……"

Python 的多态并不要求类必须通过继承建立关联。这称为 鸭子类型(duck typing):“如果它走起来像鸭子,叫起来也像鸭子,那它就是鸭子。”换句话说,Python 在乎的是对象能做什么(它的方法),而不是它是什么(它的类)。如果一个对象具有你需要的方法,无论它的类层级如何,你都可以使用它。

python
class FileWriter:
    def __init__(self, filename):
        self.filename = filename
    
    def write(self, data):
        print(f"Writing to {self.filename}: {data}")
 
class DatabaseWriter:
    def __init__(self, table_name):
        self.table_name = table_name
    
    def write(self, data):
        print(f"Inserting into {self.table_name}: {data}")
 
class ConsoleWriter:
    def write(self, data):
        print(f"Console output: {data}")
 
# 适用于任何拥有 write() 方法的对象的函数
def save_data(writer, data):
    writer.write(data)
 
# 三个类都能工作,尽管它们彼此无关
file_writer = FileWriter("data.txt")
db_writer = DatabaseWriter("users")
console_writer = ConsoleWriter()
 
save_data(file_writer, "User data")
# Output: Writing to data.txt: User data
 
save_data(db_writer, "User data")
# Output: Inserting into users: User data
 
save_data(console_writer, "User data")
# Output: Console output: User data

这些类都没有继承同一个父类,但它们都能配合 save_data() 使用,因为它们都有一个 write() 方法。这就是鸭子类型——函数不关心类,只关心接口(可用的方法)。

32.4.4) 实用示例:插件系统

多态能够实现灵活、可扩展的系统。下面是一个用于数据处理的简单插件系统:

python
class DataProcessor:
    def process(self, data):
        return data  # 基础实现什么也不做
 
class UppercaseProcessor(DataProcessor):
    def process(self, data):
        return data.upper()
 
class ReverseProcessor(DataProcessor):
    def process(self, data):
        return data[::-1]
 
class RemoveSpacesProcessor(DataProcessor):
    def process(self, data):
        return data.replace(" ", "")
 
class DataPipeline:
    def __init__(self):
        self.processors = []
    
    def add_processor(self, processor):
        self.processors.append(processor)
    
    def run(self, data):
        result = data
        for processor in self.processors:
            result = processor.process(result)  # 多态调用
        return result
 
# 构建一个处理流水线
pipeline = DataPipeline()
pipeline.add_processor(UppercaseProcessor())
pipeline.add_processor(RemoveSpacesProcessor())
pipeline.add_processor(ReverseProcessor())
 
# 通过流水线处理数据
input_data = "Hello World"
output = pipeline.run(input_data)
print(f"Input:  {input_data}")   # Output: Input:  Hello World
print(f"Output: {output}")        # Output: Output: DLROWOLLEH

DataPipeline 不需要知道它包含哪些具体的处理器——它只是在每个处理器上调用 process()。你可以很容易地添加新的处理器类型,而无需修改流水线代码。

32.5) 检查类型与类关系(isinstance, issubclass)

32.5.1) 使用 isinstance() 检查实例类型

有时你需要检查一个对象是否是某个特定类的实例。isinstance() 函数就是用来做这件事的:

python
class Animal:
    pass
 
class Dog(Animal):
    pass
 
class Cat(Animal):
    pass
 
buddy = Dog()
whiskers = Cat()
 
# 检查对象是否是某个类的实例
print(isinstance(buddy, Dog))     # Output: True
print(isinstance(buddy, Animal))  # Output: True (Dog inherits from Animal)
print(isinstance(buddy, Cat))     # Output: False
 
print(isinstance(whiskers, Cat))    # Output: True
print(isinstance(whiskers, Animal)) # Output: True
print(isinstance(whiskers, Dog))    # Output: False

注意,即使 buddyDog 的实例,isinstance(buddy, Animal) 仍然会返回 True。这是因为 Dog 继承自 Animal,所以 Dog 实例也被认为是 Animal 实例。

32.5.2) 为什么 isinstance() 会遵循继承关系

isinstance() 函数会检查完整的继承链:

python
class Vehicle:
    pass
 
class Car(Vehicle):
    pass
 
class ElectricCar(Car):
    pass
 
tesla = ElectricCar()
 
# 检查所有继承层级
print(isinstance(tesla, ElectricCar))  # Output: True
print(isinstance(tesla, Car))          # Output: True
print(isinstance(tesla, Vehicle))      # Output: True
print(isinstance(tesla, str))          # Output: False

tesla 实例

ElectricCar

Car

Vehicle

由于继承关系,tesla 对象是 ElectricCar 的实例,但它也同时是 CarVehicle 的实例。

32.5.3) 一次检查多个类型

你可以通过传入一个元组,检查对象是否为多个类中的任意一个的实例:

python
class Dog:
    pass
 
class Cat:
    pass
 
class Bird:
    pass
 
def is_pet(animal):
    return isinstance(animal, (Dog, Cat, Bird))
 
buddy = Dog()
whiskers = Cat()
tweety = Bird()
rock = "just a rock"
 
print(is_pet(buddy))     # Output: True
print(is_pet(whiskers))  # Output: True
print(is_pet(tweety))    # Output: True
print(is_pet(rock))      # Output: False

这比写 isinstance(animal, Dog) or isinstance(animal, Cat) or isinstance(animal, Bird) 更简洁。

32.5.4) 使用 issubclass() 检查类之间的关系

issubclass() 函数用于检查一个类是否是另一个类的子类:

python
class Animal:
    pass
 
class Dog(Animal):
    pass
 
class Cat(Animal):
    pass
 
class Poodle(Dog):
    pass
 
# 检查类关系
print(issubclass(Dog, Animal))    # Output: True
print(issubclass(Cat, Animal))    # Output: True
print(issubclass(Poodle, Dog))    # Output: True
print(issubclass(Poodle, Animal)) # Output: True (indirect inheritance)
print(issubclass(Dog, Cat))       # Output: False
 
# 一个类也被视为自身的子类
print(issubclass(Dog, Dog))       # Output: True

注意,issubclass() 作用于类而非实例。对实例使用 isinstance(),对类使用 issubclass()

32.5.5) 类型检查的实际用例

当你需要针对不同类型提供不同行为时,类型检查就很有用:

python
class Employee:
    def __init__(self, name, salary):
        self.name = name
        self.salary = salary
 
class Manager(Employee):
    def __init__(self, name, salary, bonus):
        super().__init__(name, salary)
        self.bonus = bonus
 
class Contractor:
    def __init__(self, name, hourly_rate, hours):
        self.name = name
        self.hourly_rate = hourly_rate
        self.hours = hours
 
def calculate_payment(worker):
    # 当结合继承使用 isinstance() 时,先检查子类,再检查父类
    if isinstance(worker, Manager):  # 先检查 Manager
        return worker.salary + worker.bonus
    elif isinstance(worker, Employee):  # 再检查 Employee(父类)
        return worker.salary
    elif isinstance(worker, Contractor):
        return worker.hourly_rate * worker.hours
    else:
        return 0
 
alice = Employee("Alice", 50000)
bob = Manager("Bob", 70000, 10000)
charlie = Contractor("Charlie", 50, 160)
 
print(f"Alice's payment: ${calculate_payment(alice)}")    # Output: Alice's payment: $50000
print(f"Bob's payment: ${calculate_payment(bob)}")        # Output: Bob's payment: $80000
print(f"Charlie's payment: ${calculate_payment(charlie)}")# Output: Charlie's payment: $8000

不过在很多情况下,多态(让每个类都实现一个共同的方法)比类型检查更好:

python
class Employee:
    def __init__(self, name, salary):
        self.name = name
        self.salary = salary
    
    def calculate_payment(self):
        return self.salary
 
class Manager(Employee):
    def __init__(self, name, salary, bonus):
        super().__init__(name, salary)
        self.bonus = bonus
    
    def calculate_payment(self):
        return self.salary + self.bonus
 
class Contractor:
    def __init__(self, name, hourly_rate, hours):
        self.name = name
        self.hourly_rate = hourly_rate
        self.hours = hours
    
    def calculate_payment(self):
        return self.hourly_rate * self.hours
 
# 不需要类型检查——多态会处理它
workers = [
    Employee("Alice", 50000),
    Manager("Bob", 70000, 10000),
    Contractor("Charlie", 50, 160)
]
 
for worker in workers:
    payment = worker.calculate_payment()  # 多态调用
    print(f"{worker.name}'s payment: ${payment}")

Output:

Alice's payment: $50000
Bob's payment: $80000
Charlie's payment: $8000

这种多态方式更灵活,也更容易扩展到新的 worker 类型。当你添加新的 worker 类时,不需要修改调用方代码——只要确保它有一个 calculate_payment() 方法即可。


继承与多态是组织代码、构建灵活且可扩展系统的强大工具。通过创建子类,你可以在复用现有代码的同时添加或修改行为。通过重写方法,你可以自定义子类的工作方式。而通过使用多态,你可以编写能够通过公共接口与许多不同类协作的代码。

关键是要有意识地使用这些特性:

  • 当确实存在“是一个(is-a)”关系时再使用继承(Dog 是一个 Animal
  • 通过重写方法来让行为更专门化,而不是彻底改变一个类的职能
  • 使用 super() 来扩展父类行为,而不是完全替换它
  • 尽可能优先选择多态(共同方法),而不是类型检查

当你构建更大的程序时,这些面向对象技术将帮助你创建更容易理解、维护和扩展的代码。

© 2025. Primesoft Co., Ltd.
support@primesoft.ai