32. 使用继承与多态扩展类
在第 30 章中,我们学习了如何创建自己的类来建模现实世界的概念。我们构建了像 BankAccount 和 Student 这样的类,把数据和行为打包在一起。但当你需要创建一个与现有类相似、但有一些差异或新增功能的新类时,会发生什么呢?
继承(inheritance) 是 Python 用于基于现有类创建新类的机制。你不需要复制粘贴代码,而是可以创建一个 子类(subclass),它会自动获得 父类(parent class)(也称为 基类(base class) 或 超类(superclass))中的所有属性和方法,然后再添加或修改你需要的部分。
本章将探讨继承如何让你构建相关类的层级体系、如何自定义继承来的行为,以及 多态(polymorphism) 如何允许不同的类在共享公共接口时可以被互换使用。
32.1) 从现有类创建子类
32.1.1) 继承的基本语法
继承允许你基于一个现有类(称为 父类(parent class) 或 基类(base class))创建一个新类(称为 子类(subclass) 或 子类(child class))。子类会自动继承父类的所有方法和属性。
创建子类时,你需要在类名后面的括号中指定父类。
# 父类
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 方法。
32.1.2) 为什么继承很重要
继承解决了一个常见的编程问题:代码重复。假设你正在构建一个系统来管理不同类型的员工:
# 不使用继承——大量重复
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})"注意到 name、employee_id 以及 get_info() 是重复的。通过继承,我们可以消除这类重复:
# 使用继承——共享代码放在父类中
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) 为子类添加新方法
子类可以添加父类没有的自有方法:
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__ 方法中做这件事:
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.00SavingsAccount 具有来自 BankAccount 的 account_number 与 balance,同时还有自己的 interest_rate 属性。
32.1.5) 多层继承
类可以继承自那些本身也继承自其他类的类,从而形成继承层级结构:
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)Dog 继承自 Animal,而 Animal 又继承自 LivingThing。这意味着 Dog 可以访问来自两个父类的所有方法和属性。
32.2) 在子类中重写方法
32.2.1) 方法重写是什么意思
有时子类需要改变某个继承方法的工作方式。方法重写(method overriding) 指的是在子类中定义一个与父类方法同名的方法。当你在子类实例上调用该方法时,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 方法,因为 buddy 是 Dog 的实例。由于 Dog 定义了自己的 speak 方法,Python 就使用该版本。如果 Dog 没有 speak 方法,Python 才会去父类 Animal 中查找并使用父类版本。
这种查找顺序——从实例所属的类开始,然后移动到父类——就是方法重写如何工作的方式,也是子类如何自定义继承行为的方式。
32.2.2) 为什么要重写方法?
方法重写让你能够为通用行为创建更专门的版本。考虑一个形状层级:
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.26544describe() 方法被两个子类继承,并且能够正确工作,因为每个子类都提供了自己的 area() 实现。
当你调用 rect.describe() 时,继承来的 describe() 方法会执行,但 self 指向 Rectangle 实例。因此当 describe() 调用 self.area() 时,Python 会先在 Rectangle 类中查找 area() 并找到被重写的版本。
32.2.3) 重写 __init__ 并调用父类初始化
当你重写 __init__ 时,你通常需要调用父类的 __init__,以确保父类的初始化能够执行:
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() 会返回一个临时对象,使你无需显式写出父类名,就能调用父类的方法。
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__:
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() 来扩展父类的方法,而不是完全替换它:
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: 1CheckingAccount.deposit() 方法调用 super().deposit(amount) 来处理基础的存款逻辑(更新余额与交易次数),然后再加入自己对透支状态的检查。
32.3.4) 何时使用 super() vs 直接调用父类
在大多数情况下使用 super():
class Vehicle:
def __init__(self, brand):
self.brand = brand
class Car(Vehicle):
def __init__(self, brand, model):
super().__init__(brand) # 推荐
self.model = model当你在多重继承场景中需要调用特定父类(后面会讲),或者你想明确指出要调用哪个父类时,可以直接调用父类:
class Car(Vehicle):
def __init__(self, brand, model):
Vehicle.__init__(self, brand) # 显式,但灵活性较差
self.model = model对于单继承(一个父类)来说,super() 几乎总是更好的选择。
32.3.5) super() 与其他方法
你可以对任何方法使用 super(),不仅限于 __init__:
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 ERROR32.4) 多态:与兼容的类协作
32.4.1) 多态是什么意思
多态(polymorphism)(源自希腊语:“多种形式”)是指:如果不同类的对象提供了相同的方法,就可以用同样的方式对待它们。
在 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) 结合继承的多态
多态与继承结合时尤其强大,因为子类可以重写父类方法:
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 在乎的是对象能做什么(它的方法),而不是它是什么(它的类)。如果一个对象具有你需要的方法,无论它的类层级如何,你都可以使用它。
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) 实用示例:插件系统
多态能够实现灵活、可扩展的系统。下面是一个用于数据处理的简单插件系统:
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: DLROWOLLEHDataPipeline 不需要知道它包含哪些具体的处理器——它只是在每个处理器上调用 process()。你可以很容易地添加新的处理器类型,而无需修改流水线代码。
32.5) 检查类型与类关系(isinstance, issubclass)
32.5.1) 使用 isinstance() 检查实例类型
有时你需要检查一个对象是否是某个特定类的实例。isinstance() 函数就是用来做这件事的:
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注意,即使 buddy 是 Dog 的实例,isinstance(buddy, Animal) 仍然会返回 True。这是因为 Dog 继承自 Animal,所以 Dog 实例也被认为是 Animal 实例。
32.5.2) 为什么 isinstance() 会遵循继承关系
isinstance() 函数会检查完整的继承链:
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 的实例。
32.5.3) 一次检查多个类型
你可以通过传入一个元组,检查对象是否为多个类中的任意一个的实例:
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() 函数用于检查一个类是否是另一个类的子类:
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) 类型检查的实际用例
当你需要针对不同类型提供不同行为时,类型检查就很有用:
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不过在很多情况下,多态(让每个类都实现一个共同的方法)比类型检查更好:
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()来扩展父类行为,而不是完全替换它 - 尽可能优先选择多态(共同方法),而不是类型检查
当你构建更大的程序时,这些面向对象技术将帮助你创建更容易理解、维护和扩展的代码。