大家好,我是大华!
经过前面 6 的天学习,咱们已经知道了什么是类、对象、属性和方法。基础的语法是知道了,可具体该怎么使用呢?
学习还是需要多实践,光学语法和理论还是不够的。不然课程计划学完了之后,时间一长,前面的也就忘得差不多了。
所以第 7 天,我就基于前面学习的语法和面向对象做了一个日常记账的功能脚本。
下面我们一起看看具体是怎么设计和实现的。
一、功能设计
我要实现的功能很简单,就只有两个对象。
首先是账单类 Record,它是具体的每一条账单信息,包含的字段是金额、分类和日期。
第二个类是账本 AccountBook,它负责保存所有账单、新增、统计和展示。
二、代码实现
先来看下基础部分的整体代码。
from datetime import dateclass Record: """表示一条账单""" def __init__(self, amount, category, record_date=None): self.amount = amount self.category = category self.record_date = record_date or date.today() def __str__(self): """返回账单的可读字符串""" return f"{self.record_date} | {self.category} | ¥{self.amount}"class AccountBook: """账本,用来管理多条账单""" def __init__(self): self.records = [] """新增账单""" def add_record(self, record): self.records.append(record) # 总计 def total_amount(self): return sum(record.amount for record in self.records) # 查看 def show_records(self): if not self.records: print("暂无记账记录") return for record in self.records: print(record)book = AccountBook()r1 = Record(30, "早餐")r2 = Record(120, "买书")r3 = Record(50, "打车")book.add_record(r1)book.add_record(r2)book.add_record(r3)book.show_records()print("总支出:", book.total_amount())
执行后输出
2026-02-04 | 早餐 | ¥302026-02-04 | 买书 | ¥1202026-02-04 | 打车 | ¥50总支出: 200
代码不多,下面逐段的拆解。
三、代码拆解
1、代码注释和字符串
昨天我去看了别人写的几套代码,有很多这种 """ ... """ 字符串,看着像是对代码的注释,但似乎又是一个字符串。它和平时用的注释有什么区别?
这是之前没讲过的,也是我刚了解到的。
1、# ...
代码注释(comment),例如:
# 账本class AccountBook:
Python 解释器会直接忽略,不会被程序读取,纯粹是写给人看的说明。这是一种行为解释和备注。
2、""" ... """
字符串(docstring / 文档字符串)。
class Record: """表示一条账单"""
这不是注释,是代码 docstring。什么是 docstring?
来看一个例子:

在VScode里当我要用这个类的时候,就会提示这个说明。
使用的方式也要注意,不能像注释那样写哪里都可以,需要使用在类的里面或者函数的里面。
来看下对比:

这里 add_record 方法里面有说明,所以在我要使用这个方法的时候,就会出现。
而下面这种情况是不会出现这个字符串说明的:

因为写到了 add_record 方法的外面,所以无法识别。
2、账单类(Record)
如果你不用类,可能会写成这样:
record = { "amount": 50, "category": "吃饭", "date": "2026-02-04"}
问题是:
而 Record 把数据 + 行为封装在了一起。
class Record: def __init__(self, amount, category, record_date=None): self.amount = amount self.category = category self.record_date = record_date or date.today()
这里有2个设计点:
- •
amount / category / date 都是账单自身的属性 - •
record_date 允许不传,默认用今天,更贴近日常使用
这就是封装。
为什么要写 __str__?
def __str__(self): return f"{self.record_date} | {self.category} | ¥{self.amount}"
__str__ 的作用,是定义对象在被当成字符串时应该怎么展示。这样一来,在任何地方直接 print(record),都能得到清晰、统一的输出,而不用每次手动去拼接字符串。
__str__ 是类中的一种特殊方法,它通常不需要我们主动调用,而是在对象被打印、或被转换成字符串时,由 Python 自动触发。
和 __str__ 类似的,还有一些同样由语言机制自动调用的特殊方法,比如:
这些方法的共同点是:我们定义规则,Python 在合适的场景下帮我们调用。
3、账本类(AccountBook)
AccountBook 的职责只有一个:那就是管理多个 Record,可以说是一对多的关系。
class AccountBook: def __init__(self): self.records = []
3.1、为什么 add_record 不直接传参数?
def add_record(self, record): self.records.append(record)
而不是:
def add_record(self, amount, category): ...
因为:
拆解完成,很简单是不是,那再加点东西。
现在是通过固定调用的方式实现的,接下来增加一个动态输入和输出的方式进行实现。
四、功能增强
原来两个类的代码不变,把手动调用的地方删除,然后只需要增加输入和主函数就可以了。通过输入创建 Record,再交给 AccountBook。
4.1、代码实现
删除的部分
book = AccountBook()r1 = Record(30, "早餐")r2 = Record(120, "买书")r3 = Record(50, "打车")book.add_record(r1)book.add_record(r2)book.add_record(r3)book.show_records()print("总支出:", book.total_amount())
新增的部分
def create_record_by_input(): while True: try: amount = float(input("请输入金额:")) break except ValueError: print("金额必须是数字,请重新输入") category = input("请输入消费分类:").strip() return Record(amount, category)def main(): book = AccountBook() while True: print("\n===== 记账系统 =====") print("1. 新增一笔账") print("2. 查看所有账单") print("3. 查看总支出") print("4. 退出") choice = input("请选择操作:").strip() if choice == "1": record = create_record_by_input() book.add_record(record) print("✅ 记账成功") elif choice == "2": book.show_records() elif choice == "3": print("总支出:¥", book.total_amount()) elif choice == "4": print("已退出记账系统") break else: print("无效选择,请重新输入")# 执行主函数main()
然后来运行测试一下,下面是按序号输入和输出的结果。
===== 记账系统 =====1. 新增一笔账2. 查看所有账单3. 查看总支出4. 退出请选择操作:1请输入金额:25请输入消费分类:香烟✅ 记账成功===== 记账系统 =====1. 新增一笔账2. 查看所有账单3. 查看总支出4. 退出请选择操作:22026-02-04 | 香烟 | ¥25.0===== 记账系统 =====1. 新增一笔账2. 查看所有账单3. 查看总支出4. 退出请选择操作:3总支出:¥ 25.0===== 记账系统 =====1. 新增一笔账2. 查看所有账单3. 查看总支出4. 退出请选择操作:4已退出记账系统
OK,完成。
先把这个小记账脚本吃透,为后面学文件存储、数据库、Web 接口,打下一点点基础。
今天的学习内容先到这里。感谢你的观看,如果有不对或者需要补充的地方,欢迎指正。