完整代码如下:
class BankAccount:
def __init__(self, account_holder, initial_balance=0):
"""
初始化银行账户
:param account_holder: 账户持有人姓名
:param initial_balance: 初始余额,默认为0
"""
self.account_holder = account_holder
self.balance = initial_balance
self.transactions = [] # 记录交易历史
def deposit(self, amount):
"""
存款操作
:param amount: 存款金额
"""
if amount <= 0:
print("存款金额必须大于0")
return
self.balance += amount
self.transactions.append(f"存款: +{amount:.2f}")
print(f"成功存款 {amount:.2f}。当前余额: {self.balance:.2f}")
def withdraw(self, amount):
"""
取款操作
:param amount: 取款金额
"""
if amount <= 0:
print("取款金额必须大于0")
return
if amount > self.balance:
print("余额不足,无法完成取款")
return
self.balance -= amount
self.transactions.append(f"取款: -{amount:.2f}")
print(f"成功取款 {amount:.2f}。当前余额: {self.balance:.2f}")
def get_balance(self):
"""
获取当前余额
"""
return self.balance
def get_transaction_history(self):
"""
获取交易历史记录
"""
return self.transactions
def __str__(self):
return f"账户持有人: {self.account_holder}, 当前余额: {self.balance:.2f}"
if __name__ == "__main__":
name = input("请输入账户持有人姓名: ")
while True:
try:
initial_deposit = float(input("请输入初始存款金额: "))
if initial_deposit < 0:
print("初始存款金额不能为负数,请重新输入")
continue
break
except ValueError:
print("请输入有效的数字")
# 创建账户
account = BankAccount(name, initial_deposit)
print(f"\n账户创建成功!")
print(account)
# 循环操作
while True:
print("\n请选择操作:")
print("1. 存款")
print("2. 取款")
print("3. 查询余额")
print("4. 查看交易记录")
print("5. 退出")
choice = input("请输入选项(1-5): ")
if choice == "1":
# 存款
while True:
try:
amount = float(input("请输入存款金额: "))
if amount <= 0:
print("存款金额必须大于0")
continue
account.deposit(amount)
break
except ValueError:
print("请输入有效的数字")
elif choice == "2":
# 取款
while True:
try:
amount = float(input("请输入取款金额: "))
if amount <= 0:
print("取款金额必须大于0")
continue
account.withdraw(amount)
break
except ValueError:
print("请输入有效的数字")
elif choice == "3":
# 查询余额
print(f"\n当前余额: {account.get_balance():.2f}")
elif choice == "4":
# 查看交易记录
print("\n交易记录:")
for transaction in account.get_transaction_history():
print(transaction)
if not account.get_transaction_history():
print("暂无交易记录")
elif choice == "5":
# 退出
print("感谢使用银行账户系统,再见!")
break
else:
print("无效的选项,请重新输入")
一、系统框架
类结构:
- 核心类
BankAccount
封装了银行账户的所有功能 - 使用面向对象编程(OOP)思想,将数据和操作封装在一起
- 核心类
主要组件:
class BankAccount:
# 1. 初始化方法
def __init__(self, account_holder, initial_balance=0)
# 2. 存款功能
def deposit(self, amount)
# 3. 取款功能
def withdraw(self, amount)
# 4. 查询功能
def get_balance(self)
def get_transaction_history(self)
# 5. 字符串表示
def __str__(self)
用户交互:
- 主程序通过
if __name__ == "__main__":
实现交互式命令行界面 - 使用 while 循环保持程序持续运行
- 主程序通过
二、设计思路
数据建模:
- 账户属性:持有人姓名、余额、交易记录
- 交易记录使用列表存储,记录每次操作的详细信息
功能实现原则:
- 单一职责原则:每个方法只做一件事
- 输入验证:对所有用户输入进行有效性检查
- 事务记录:自动记录每笔交易的详细信息
- 防错设计:处理负数金额、余额不足等异常情况
交互流程:
开始 → 输入账户信息 → 主菜单 → 选择操作 → 执行功能 → 返回主菜单 → 退出
异常处理:
- 使用 try-except 处理非数字输入
- 使用条件判断处理业务逻辑错误(如余额不足)
三、关键设计点
状态管理:
balance
变量实时跟踪账户余额transactions
列表完整记录所有交易历史
用户友好性:
- 清晰的提示信息
- 格式化的金额显示(保留两位小数)
- 操作结果的即时反馈
扩展性考虑:
- 通过类的方式实现,便于后续添加新功能
- 交易记录采用标准格式,便于扩展分析功能
四、典型工作流程示例
- 用户输入姓名和初始金额
- 系统创建账户对象
- 用户选择存款:
- 输入存款金额
- 系统验证并执行存款
- 更新余额和交易记录
- 用户选择查询:
- 系统显示当前余额
- 用户退出系统
五、代码分析
整体结构分析
该代码定义了一个BankAccount
类来模拟银行账户的基本操作,包括初始化账户、存款、取款、查询余额、查看交易记录等功能。在if __name__ == "__main__"
模块中,实现了与用户的交互,让用户能够创建账户并进行各种操作。
类定义分析
__init__
方法
- 功能:初始化银行账户对象。
- 接受
account_holder
(账户持有人姓名)和initial_balance
(初始余额,默认为0)两个参数。 - 初始化了三个实例变量:
self.account_holder
(存储账户持有人姓名)、self.balance
(存储账户余额)、self.transactions
(存储交易历史记录的空列表)。
- 接受
- 优点:通过参数初始化实例变量,使每个账户对象在创建时就有明确的初始状态。
deposit
方法
- 功能:实现存款操作。
- 检查存款金额
amount
是否小于等于0,若为真,打印提示信息并返回,不进行存款操作。 - 否则,将存款金额加到账户余额
self.balance
上,并将存款交易记录(格式为"存款: +{amount:.2f}"
)添加到self.transactions
列表中,同时打印存款成功信息和当前余额。
- 检查存款金额
- 优点:有输入校验(金额有效性检查),保证了存款操作的合理性,并且及时更新账户余额和记录交易历史。
withdraw
方法
- 功能:实现取款操作。
- 先检查取款金额
amount
是否小于等于0,若是,打印提示信息并返回。 - 再检查取款金额是否大于当前账户余额
self.balance
,若大于,打印余额不足提示信息并返回。 - 否则,从账户余额中减去取款金额,并将取款交易记录(格式为
"取款: -{amount:.2f}"
)添加到self.transactions
列表中,同时打印取款成功信息和当前余额。
- 先检查取款金额
- 优点:具备完善的输入校验(金额有效性和余额充足性检查),确保取款操作符合实际业务逻辑,保护账户资金安全。
get_balance
方法
- 功能:简单返回当前账户的余额
self.balance
。 - 优点:提供了一种便捷的方式获取账户余额,符合面向对象编程中封装的思想(外部通过方法访问内部数据)。
get_transaction_history
方法
- 功能:返回存储交易历史记录的
self.transactions
列表。 - 优点:方便用户查看账户的交易明细,是对账户操作历史的记录和展示。
__str__
方法
- 功能:定义了对象的字符串表示形式。当使用
print()
函数打印账户对象时,会自动调用该方法,返回一个包含账户持有人姓名和当前余额的字符串(格式为"账户持有人: {self.account_holder}, 当前余额: {self.balance:.2f}"
)。 - 优点:使账户对象在打印时能以友好、易读的方式呈现信息,方便用户直观了解账户基本情况,也有利于调试和日志记录等场景。
if __name__ == "__main__"
模块分析
- 获取用户输入并创建账户:
- 使用
input()
函数获取用户输入的账户持有人姓名name
。 - 通过循环和
try-except
块,确保用户输入的初始存款金额initial_deposit
是有效的数字且不为负数,然后创建BankAccount
对象account
。 - 打印账户创建成功信息和账户对象(调用
__str__
方法)。
- 使用
- 用户操作循环:
- 进入一个无限循环,展示操作菜单(存款、取款、查询余额、查看交易记录、退出)。
- 根据用户输入的选项
choice
,执行相应的操作(调用BankAccount
类的对应方法)。 - 对于每个操作(如存款、取款),同样使用循环和
try-except
块确保用户输入的金额是有效的数字,并进行相应的业务逻辑处理(调用类方法)。 - 若用户选择退出(选项
5
),打印感谢信息并终止循环,结束程序。
代码优点总结
- 面向对象封装:将账户相关的数据(余额、交易记录、持有人姓名等)和操作(存款、取款等)封装在
BankAccount
类中,体现了良好的面向对象设计思想,使代码结构清晰,便于维护和扩展。 - 输入校验完善:在存款、取款等操作中,对用户输入的金额进行了充分的有效性检查(如金额是否为正数、取款时余额是否足够等),增强了程序的健壮性,避免了不合理操作导致的错误。
- 交互友好:通过清晰的菜单提示和操作反馈(如打印存款、取款成功信息和当前余额等),使用户能够方便、直观地与程序进行交互,了解操作结果。
六、__init__
和__str__
是两个特殊的魔法方法
1. __init__
方法
核心作用:
- 构造函数:在创建类实例时自动调用
- 初始化对象:设置对象的初始状态
银行账户示例:
class BankAccount:
def __init__(self, account_holder, initial_balance=0):
self.account_holder = account_holder # 设置账户持有人
self.balance = initial_balance # 设置初始余额
self.transactions = [] # 初始化交易记录
关键特点:
- 第一个参数必须是
self
(指向新创建的对象) - 在实例化时自动执行:
account = BankAccount("张三", 1000) # 自动调用__init__
- 不能有返回值(隐式返回
None
)
2. __str__
方法
核心作用:
- 定义对象的字符串表示:当使用
print()
或str()
时自动调用 - 提供用户友好描述:便于调试和输出
银行账户示例:
def __str__(self):
return f"账户持有人: {self.account_holder}, 当前余额: {self.balance:.2f}"
使用场景:
account = BankAccount("李四", 500)
print(account) # 自动调用__str__,输出:账户持有人: 李四, 当前余额: 500.00
关键特点:
- 必须返回字符串类型
- 如果没有定义
__str__
,Python会使用__repr__
作为备用 - 常用于日志、用户界面等需要可读输出的场景
3. 对比总结
特性 | __init__ |
__str__ |
---|---|---|
调用时机 | 创建实例时 | 调用print() 或str() 时 |
主要用途 | 初始化对象属性 | 定义对象的可读字符串表示 |
返回值 | 不应返回任何值 | 必须返回字符串 |
是否必需 | 非必需(但绝大多数类都会实现) | 非必需(但建议重要类都实现) |
示例调用场景 | obj = ClassName(args) |
print(obj) 或 str(obj) |
4. 实际应用关系
在银行账户系统中,这两个方法协同工作:
# 创建账户(调用__init__)
account = BankAccount("王五", 2000)
# 查看账户信息(调用__str__)
print(account)
# 输出:账户持有人: 王五, 当前余额: 2000.00
5. 为什么需要它们?
__init__
: 确保对象创建后即处于有效状态,避免后续出现属性未定义的错误__str__
: 提供比默认内存地址(如<__main__.BankAccount object at 0x...>
)更有意义的输出
6. 进阶提示
类似方法
__repr__
:用于开发者调试,应返回明确的创建表达式
def __repr__(self):
return f'BankAccount("{self.account_holder}", {self.balance})'
在银行账户系统中,良好的
__str__
实现可以帮助:- 快速查看账户状态
- 生成对账单摘要
- 调试交易记录
七、__str__
方法的语言规范
必须返回字符串 Python明确规定
__str__
方法必须返回一个字符串对象(str
类型)。当调用print(obj)
或str(obj)
时,解释器会自动执行这个方法并显示其返回值。与
print()
的协作机制
print(account) # 内部实际执行:
# 1. 调用account.__str__()
# 2. 打印该方法返回的字符串
3.银行账户示例解析
def __str__(self):
return f"账户持有人: {self.account_holder}, 当前余额: {self.balance:.2f}"
返回值内容: 返回一个格式化的字符串,包含账户持有人和带两位小数的余额 (例如:
"账户持有人: 张三, 当前余额: 1000.00"
)技术细节:
f"..."
:f-string格式化字符串(Python 3.6+):.2f
:将浮点数格式化为2位小数
4.如果不返回字符串会发生什么?
❌ 错误示例:
def __str__(self):
print("账户信息...") # 错误!__str__不应该直接打印
- 调用
print(account)
会导致:TypeError: __str__ returned non-string (type NoneType)
✅ 正确做法: 必须通过return
返回字符串,让调用者决定如何处理这个字符串。
5.为什么不是直接打印?
这种设计体现了关注点分离原则:
__str__
的职责: 只负责生成对象的字符串表示形式 (不关心这个字符串是用来打印、记录日志还是其他用途)调用方的自由: 返回字符串后,调用者可以:
# 选择1:直接打印
print(account)
# 选择2:存入文件
with open("log.txt", "a") as f:
f.write(str(account))
# 选择3:拼接其他字符串
msg = "账户状态:" + str(account)
6.类比其他语言
语言 | 类似机制 |
---|---|
Java | toString() 方法 |
C# | ToString() 方法 |
JavaScript | toString() 原型方法 |
C++ | 重载 << 操作符 |
八、while True
语句、 if elif else
语句和 try except
语句的详细介绍
while True
语句
- 作用:创建一个无限循环,只要条件
True
始终满足(实际上就是一直满足),循环体就会不断执行。通常需要在循环内部通过break
语句来终止循环。 - 语法结构:
while True:
# 循环体代码
if 终止条件:
break
- 示例:
while True:
user_input = input("请输入一个数字(输入 'q' 退出): ")
if user_input == 'q':
break
try:
num = int(user_input)
print(f"你输入的数字是: {num}")
except ValueError:
print("输入无效,请输入一个整数。")
- 应用场景:
- 创建交互式程序,如命令行工具,等待用户持续输入指令,直到用户发出退出信号(如上述示例)。
- 需要持续监听某种状态(如服务器监听客户端连接请求)的场景。
if elif else
语句
- 作用:用于条件判断,根据不同的条件执行相应的代码块。
if
后面可以跟多个elif
(表示“否则如果”),最后可以有一个可选的else
(表示前面条件都不满足时执行)。 - 语法结构:
if 条件1:
# 条件1满足时执行的代码
elif 条件2:
# 条件2满足时执行的代码
else:
# 前面条件都不满足时执行的代码
- 示例:
score = 85
if score >= 90:
print("优秀")
elif score >= 80:
print("良好")
elif score >= 60:
print("及格")
else:
print("不及格")
- 应用场景:
- 根据不同的数值范围、逻辑条件执行不同操作,如成绩等级判断(如上例)。
- 处理不同用户角色的权限逻辑(如判断用户是管理员、普通用户等,执行不同的功能)。
try except
语句
- 作用:用于捕获和处理程序运行时可能出现的异常,防止程序因为异常而崩溃。
try
块中放置可能引发异常的代码,except
块用于处理捕获到的异常。 - 语法结构:
try:
# 可能引发异常的代码
except 异常类型:
# 处理异常的代码
- 示例:
try:
result = 10 / 0 # 会引发 ZeroDivisionError 异常
except ZeroDivisionError:
print("除数不能为零!")
- 场景:
- 处理用户输入可能不符合预期的情况(如尝试将用户输入转换为特定类型时,用户输入了非数字字符)。
- 进行文件操作、网络请求等可能失败的操作时,捕获并处理相应的异常(如文件不存在
FileNotFoundError
、网络连接超时等)。
在实际编程中,这三种语句经常结合使用。例如在银行账户系统代码里:
while True:
try:
initial_deposit = float(input("请输入初始存款金额: "))
if initial_deposit < 0:
print("初始存款金额不能为负数,请重新输入")
continue
break
except ValueError:
print("请输入有效的数字")
这里
while True
创建了一个循环让用户不断输入,直到输入有效(通过break
终止循环)。try except
用于处理用户输入不是数字的情况(捕获ValueError
异常),if
语句用于进一步校验输入数字的合理性(是否为负数)。
再比如在取款操作的代码中:
elif choice == "2":
# 取款
while True:
try:
amount = float(input("请输入取款金额: "))
if amount <= 0:
print("取款金额必须大于0")
continue
account.withdraw(amount)
break
except ValueError:
print("请输入有效的数字")
九、break
和 continue
是用于控制循环流程的关键字
作用和功能
break
- 作用:直接终止当前所在的循环(可以是
for
循环或while
循环),循环体中break
语句之后的代码将不再执行,程序会跳出循环,继续执行循环后面的代码。 - 示例:
- 作用:直接终止当前所在的循环(可以是
for i in range(10):
if i == 5:
break
print(i)
# 输出结果为 0 1 2 3 4,当 i 等于 5 时,循环终止
continue
- 作用:跳过本次循环中
continue
语句之后的代码,直接进入下一次循环的条件判断(对于for
循环,会继续迭代下一个元素;对于while
循环,会继续判断循环条件)。 - 示例:
- 作用:跳过本次循环中
for i in range(10):
if i % 2 == 0:
continue
print(i)
# 输出结果为 1 3 5 7 9,当 i 是偶数时,跳过本次循环的打印操作,直接进入下一次循环
适用场景
break
- 当你在循环中发现某个特定条件满足,并且后续不需要再执行循环中的任何操作时,就可以使用
break
来提前结束循环。例如在一个列表查找元素的循环中,一旦找到目标元素,就可以用break
停止继续查找。 - 示例:
- 当你在循环中发现某个特定条件满足,并且后续不需要再执行循环中的任何操作时,就可以使用
my_list = [10, 20, 30, 40, 50]
target = 30
for num in my_list:
if num == target:
print(f"找到目标元素 {target}")
break
continue
- 当你希望在满足某些条件时,跳过本次循环的一部分操作,但循环还需要继续执行下去时,适合使用
continue
。比如在遍历一个数字列表时,想忽略其中的偶数,只对奇数进行某些计算或处理。 - 示例
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
for num in numbers:
if num % 2 == 0:
continue # 跳过偶数
# 以下是对奇数的处理逻辑
print(f"处理奇数: {num}")
对循环嵌套的影响
break
:在循环嵌套的情况下,break
只会终止它所在的那一层循环。- 示例:
for i in range(5):
for j in range(5):
if j == 2:
break # 只会跳出内层的 j 循环
print(f"i={i}, j={j}")
# 输出结果是 当 j 等于 2 时,内层循环停止,外层循环继续
continue
:同样在循环嵌套中,continue
也只影响它所在的那一层循环,会跳过该层循环中continue
后面的代码,进入该层循环的下一次迭代。- 示例:
for i in range(5):
for j in range(5):
if j == 2:
continue # 跳过内层 j 循环中 j=2 时后续的代码,继续 j 的下一次循环
print(f"i={i}, j={j}")
# 输出结果是 当 j 等于 2 时,跳过本次 j 循环的打印操作,j 继续递增
总的来说,break
用于快速结束循环,continue
用于有条件地跳过循环体中的部分代码,它们为程序员提供了灵活控制循环流程的手段。