本文目录导读:

这是一个非常经典的面向对象编程(OOP)教学场景,用一个银行账户系统来解释封装、继承和多态,既直观又贴近现实。
下面我们一步步构建这个系统,并解释每个概念是如何体现的。
封装 —— 保护你的“小金库”
核心思想:把数据(属性)和操作数据的方法(行为)捆绑在一起,并隐藏内部细节,对外只暴露必要的接口,就像你不需要知道银行后台如何计算利息,只需要知道“查询余额”和“取款”按钮怎么按。
案例分析:
假设我们有一个 BankAccount 类。
class BankAccount:
def __init__(self, owner, initial_balance):
self.__owner = owner # 私有属性: 两个下划线开头
self.__balance = initial_balance # 私有属性: 余额不能直接修改
# 公共方法: 获取余额 (对外暴露的接口)
def get_balance(self):
return self.__balance
# 公共方法: 存款
def deposit(self, amount):
if amount > 0:
self.__balance += amount
print(f"存款成功,当前余额: {self.__balance}")
else:
print("存款金额必须大于0")
# 公共方法: 取款 (内部校验逻辑隐藏)
def withdraw(self, amount):
if 0 < amount <= self.__balance:
self.__balance -= amount
print(f"取款成功,当前余额: {self.__balance}")
else:
print("余额不足或金额无效")
这个例子中的封装体现在:
__balance是私有属性,外部代码不能直接account.__balance = 1000。- 必须通过
get_balance()、deposit()、withdraw()这些公开接口来操作。 - 存款和取款时的校验逻辑(如金额必须大于0、不能透支)被封装在方法内部,使用者无需关心。
继承 —— 复用“银行模板”
核心思想:子类可以继承父类的属性和方法,并在此基础上添加自己特有的行为,就像“储蓄卡”和“信用卡”都是“银行卡”,有共同的基础功能(存、取、查余额),但也有各自的特有功能(信用卡可以透支)。
案例分析:
我们从 BankAccount 派生出两个子类:SavingsAccount(储蓄账户)和 CheckingAccount(支票账户/活期账户)。
# 继承自 BankAccount
class SavingsAccount(BankAccount):
def __init__(self, owner, initial_balance, interest_rate):
# 调用父类的构造方法
super().__init__(owner, initial_balance)
self.__interest_rate = interest_rate # 新增属性: 利率
# 新增方法: 计算利息
def apply_interest(self):
interest = self._BankAccount__balance * self.__interest_rate / 100 # 注意访问私有属性的方式
# 更推荐的方式: 通过父类的存款方法
self.deposit(interest)
print(f"应用利息 {interest:.2f} 元")
class CheckingAccount(BankAccount):
def __init__(self, owner, initial_balance, overdraft_limit):
super().__init__(owner, initial_balance)
self.__overdraft_limit = overdraft_limit # 新增属性: 透支额度
# 重写父类方法: 允许透支
def withdraw(self, amount):
# 特有逻辑: 余额 + 透支额度 >= 取款金额
if 0 < amount <= (self._BankAccount__balance + self.__overdraft_limit):
# 调用父类的取款方法? 不行, 因为父类有余额限制。
# 需要直接操作余额(为了演示, 这里直接操作私有属性, 实际应使用 protected)
self._BankAccount__balance -= amount
print(f"取款成功 (可使用透支),当前余额: {self._BankAccount__balance}")
else:
print("超过透支额度")
这个例子中的继承体现在:
SavingsAccount和CheckingAccount都继承了BankAccount的get_balance()、deposit()方法,不需要重写。- 它们各自增加了新属性(利率、透支额度)和新方法(
apply_interest)。 - 它们可以选择重用或重写父类的方法(
CheckingAccount重写了withdraw)。
多态 —— 同一个接口,不同表现
核心思想:不同类的对象,对同一个方法调用,会做出不同的响应,前提是它们共享一个共同的基类或接口,所有类型的账户都有“取款”这个功能,但储蓄卡不能透支,信用卡可以。
案例分析:
我们编写一个通用的函数,来处理不同类型的账户。
def process_account(account: BankAccount):
"""模拟月末处理: 对不同类型的账户进行不同操作"""
print(f"处理账户: {account.__class__.__name__}")
print(f"当前余额: {account.get_balance()}")
# 重点: 相同的调用, 不同的行为!
account.deposit(100) # 所有账户都能存款, 行为一致
# 取款: 不同类型的账户, 取款逻辑可能不同
account.withdraw(200) # 储蓄卡可能失败(余额不足), 支票卡可能成功(允许透支)
print("--- 处理完成 ---\n")
# 创建实例
savings = SavingsAccount("张三", 500, 2.5)
checking = CheckingAccount("李四", 100, 500)
# 调用同一个函数, 传入不同类型的账户
process_account(savings) # 输出: 存款100, 取款200失败(余额600, 但取200够, 实际是没重写withdraw)
# 更正: SavingsAccount 没有重写 withdraw, 所以用父类的, 余额600>200, 成功。
# 我们修改一下: 让 SavingsAccount 的余额初始为50, 这样取200会失败。
# 为了演示更清晰, 我们重新初始化:
savings2 = SavingsAccount("王五", 50, 3.0)
checking2 = CheckingAccount("赵六", 100, 500)
def process_account_polymorphic(account):
print(f"处理 {account.__class__.__name__}")
print(f"余额: {account.get_balance()}")
account.withdraw(200) # 这里就是多态的核心!
print(f"取款后余额: {account.get_balance()}")
process_account_polymorphic(savings2) # 输出: 余额不足或金额无效 (因为savings2余额只有50)
process_account_polymorphic(checking2) # 输出: 取款成功 (因为checking2能透支, 余额-100)
这个例子中的多态体现在:
process_account_polymorphic函数接受一个BankAccount类型的参数。- 在函数内部,调用了
account.withdraw(200)。 - 当传入
SavingsAccount对象时,withdraw行为是“余额不足则拒绝”。 - 当传入
CheckingAccount对象时,withdraw行为是“允许透支”。 - 相同的代码(
account.withdraw(200)),因对象类型不同,产生不同的结果。
对应关系
| 概念 | 银行业的现实类比 | 代码中的体现 |
|---|---|---|
| 封装 | 银行的数据库(余额、密码)只有内部系统能访问,你只能通过ATM或柜台(接口)操作。 | 私有属性 __balance + 公共方法 get_balance()、deposit()、withdraw()。 |
| 继承 | 储蓄卡、信用卡都是“银行卡”的扩展,它们共享卡号、密码、基本存取功能,但各有特点(利率、透支)。 | SavingsAccount 和 CheckingAccount 继承 BankAccount,重用并扩展了功能。 |
| 多态 | 你喊一声“取200块”,储蓄卡会检查余额;信用卡会检查信用额度,同一个指令“取款”,不同卡的处理方式不同。 | 调用 account.withdraw(200),实际执行的是对象所属子类的 withdraw 方法。 |
通过这个银行账户案例,你应该能清晰地看到 封装保护了数据和实现细节,继承实现了代码的复用和扩展,多态让代码更灵活、可扩展性更强。 这三者共同构成了面向对象编程的基石。