三、继承与多态
3.1 继承
3.1.1 Python中继承的概念
在Python中,继承是一种面向对象编程的特性,它允许我们定义一个继承一个或多个其他类的属性和方法的新类。继承有助于代码的复用,可以让我们基于已有的类创建新的类,而不必从头开始编写。
3.1.1.1 基本术语
- 基类(Base class) / 父类(Parent class):被继承的类,提供了属性和方法供子类使用。
- 派生类(Derived class) / 子类(Child class):从基类继承属性和方法的类。
3.1.1.2 单继承
class Parent:
def __init__(self, name):
self.name = name
def show_name(self):
print(f"The name is {self.name}")
class Child(Parent):
pass
c = Child("Alice")
c.show_name() # 输出: The name is Alice
在上面的例子中,Child类继承了Parent类,因此它可以使用Parent类中的show_name方法。
3.1.1.3 多继承
class Mother:
def __init__(self, mother_name):
self.mother_name = mother_name
class Father:
def __init__(self, father_name):
self.father_name = father_name
class Child(Mother, Father):
def __init__(self, mother_name, father_name, child_name):
Mother.__init__(self, mother_name)
Father.__init__(self, father_name)
self.name = child_name
def show_names(self):
print(f"Child Name: {self.name}")
print(f"Mother Name: {self.mother_name}")
print(f"Father Name: {self.father_name}")
c = Child("Martha", "John", "Alice")
c.show_names()
在多继承中,如果多个父类中有相同名称的方法,则按照MRO(方法解析顺序)来决定调用哪个类的方法。
MRO(Method Resolution Order,方法解析顺序)
面向对象编程中的一个概念,特别是在多继承的情况下,它定义了在继承体系中解析方法的顺序。MRO 对于确保正确的方法覆盖(即子类中的方法覆盖父类中的同名方法)以及处理菱形继承(一个类从多个类继承,而这些类又有一个共同的祖先)等问题至关重要。
在Python中,MRO遵循C3线性化算法(C3 superclass linearization)。C3算法可以保证几个重要的特性:
- 子类会先于父类被检查。
- 如果多个父类存在,它们将按照它们在类定义中的顺序被检查。
- 如果对某个类存在多个继承路径,将选择第一个路径。
3.1.1.4 方法重写
class Parent:
def show(self):
print("This is the Parent class show method.")
class Child(Parent):
def show(self):
print("This is the Child class show method.")
c = Child()
c.show() # 输出: This is the Child class show method.
在上面的例子中,Child类中的show方法重写了Parent类中的show方法。
3.1.1.5 调用父类方法
class Parent:
def __init__(self):
print("Parent init")
def say_hello(self):
print("Hello from Parent")
class Child(Parent):
def __init__(self):
super().__init__() # 调用父类的构造函数
print("Child init")
def say_hello(self):
super().say_hello() # 调用父类的方法
print("Hello from Child") # 执行子类自己的代码
# 创建Child类的实例
c = Child()
# 调用子类的方法
c.say_hello()
3.1.2 处理银行账户简易的转账算法(继承版本)
# 定义BankAccount基类
class BankAccount:
# 类属性,用于存储所有账户的共同利率
interest_rate = 0.03 # 假设年利率为3%
# 构造函数,用于初始化银行账户的属性
def __init__(self, account_number, owner, balance=0.0):
self.account_number = account_number # 账户号码
self.owner = owner # 账户所有者
self.balance = balance # 账户余额
# 存款方法,向账户中存入指定金额
def deposit(self, amount):
if amount > 0:
self.balance += amount # 增加账户余额
print(f"{self.owner} 存款 {amount:.2f} 元。账户余额: {self.balance:.2f} 元")
else:
print("存款金额必须为正数。")
# 取款方法,从账户中取出指定金额
def withdraw(self, amount):
if 0 < amount <= self.balance:
self.balance -= amount # 减少账户余额
print(f"{self.owner} 取款 {amount:.2f} 元。账户余额: {self.balance:.2f} 元")
else:
print("无效的取款金额。")
# 显示账户余额的方法
def get_balance(self):
print(f"账户 {self.account_number} ({self.owner}) 的余额是 {self.balance:.2f} 元")
# 转账方法,将指定金额从当前账户转账到另一个账户
def transfer(self, target_account, amount):
if isinstance(target_account, BankAccount):
if 0 < amount <= self.balance:
self.withdraw(amount) # 从当前账户取款
target_account.deposit(amount) # 向目标账户存款
print(f"从 {self.owner} 账户向 {target_account.owner} 账户转账 {amount:.2f} 元。")
else:
print("无效的转账金额。")
else:
print("目标账户必须是一个 BankAccount 实例。")
# 类方法,用于获取当前利率
@classmethod
def get_interest_rate(cls):
return cls.interest_rate
# 类方法,用于设置新的利率
@classmethod
def set_interest_rate(cls, new_rate):
if new_rate >= 0:
cls.interest_rate = new_rate # 更新类属性
print(f"新的利率已设置为:{new_rate}")
else:
print("利率不能为负数。")
# 静态方法,用于计算利息
@staticmethod
def calculate_interest(balance, rate):
return balance * rate # 利息计算公式
# 定义SpecialBankAccount子类,继承自BankAccount
class SpecialBankAccount(BankAccount):
# 构造函数,初始化基类属性并添加特殊属性
def __init__(self, account_number, owner, balance=0.0, special_feature="VIP"):
# 调用基类构造函数初始化基类属性
super().__init__(account_number, owner, balance)
# 添加新的特殊属性
self.special_feature = special_feature
# 显示特殊账户信息的方法
def show_special_feature(self):
# 打印账户的特殊属性
print(f"账户 {self.account_number} ({self.owner}) 的特殊属性是:{self.special_feature}")
# 创建SpecialBankAccount实例
special_account = SpecialBankAccount('123456', 'Charlie', 1500.0)
# 执行一些操作
special_account.deposit(300.0) # Charlie 存款 300 元
special_account.withdraw(150.0) # Charlie 取款 150 元
special_account.get_balance() # Charlie 查看余额
special_account.show_special_feature() # 显示特殊属性
# 使用继承的类方法
print(f"当前利率:{SpecialBankAccount.get_interest_rate()}") # 输出当前利率
SpecialBankAccount.set_interest_rate(0.05) # 设置新的利率为5%
# 使用继承的静态方法
interest = SpecialBankAccount.calculate_interest(special_account.balance, SpecialBankAccount.get_interest_rate())
print(f"Charlie账户的利息:{interest:.2f} 元") # 输出Charlie账户的利息
super():
在Python中,super() 函数是用来调用父类(基类)的一个方法的。它是用来实现继承中的一种特性,即在子类中访问父类的方法
- 调用父类的构造函数:在子类的构造函数中,使用 super() 来调用父类的构造函数,确保父类中的初始化代码得到执行。
- 调用父类的方法:在子类中,如果你想调用父类中的某个方法,可以使用 super() 来实现。
3.2 多态
- 编译时多态(静态多态):主要是通过函数重载或模板实现,这在Python中并不直接支持。
- 运行时多态(动态多态):在运行时根据对象的实际类型来决定调用哪个方法,这是Python中实现多态的主要方式。
3.2.1 Python中的多态
3.2.1.1 Duck Typing
Python有一个著名的原则叫做“鸭子类型”(Duck Typing),即“如果它走起来像鸭子,叫起来也像鸭子,那么它就是鸭子”。这意味着在Python中,一个对象的类型并不重要,只要它拥有某个方法或属性,就可以调用该方法或属性,这就是一种多态的表现。
3.2.1.2 方法重写
在子类中重新定义从父类继承来的方法,这就是方法重写。当子类和父类存在相同的方法时,通过子类实例调用方法时会执行子类的方法,而不是父类的方法。
class Animal:
def speak(self):
pass
class Dog(Animal):
def speak(self):
return "汪汪汪"
class Cat(Animal):
def speak(self):
return "喵喵喵"
def animal_speak(animal):
print(animal.speak())
dog = Dog()
cat = Cat()
animal_speak(dog) # 输出: 汪汪汪
animal_speak(cat) # 输出: 喵喵喵
3.2.1.3 抽象基类(ABC)
Python提供了abc模块,允许创建抽象基类(Abstract Base Classes,ABCs)。抽象类不能被实例化,它只是用来定义子类应该实现的方法。
from abc import ABC, abstractmethod
class Animal(ABC):
@abstractmethod
def speak(self):
pass
class Dog(Animal):
def speak(self):
return "汪汪汪"
class Cat(Animal):
def speak(self):
return "喵喵喵"
3.2.1.3.1 元类
在Python中,元类(metaclass)是创建类的“类”。换句话说,就像类是创建对象的模板一样,元类是创建类的模板。
- 元类是一个高级特性,通常只有在需要时才使用。
- 在Python中,一切皆是对象,类本身也是对象,它是元类的实例。
- 默认的元类:如果你没有显式地指定一个类的元类,Python会使用默认的元类,即type。type既可以作为函数使用,也可以作为元类使用。
class MyClass:
pass
class MyClassWithExplicitMeta(type):
pass
# 这两个类都是使用type作为元类创建的