记账本 v1
4 道练习题·预计 60 分钟·做对一题解锁下一段
前言
前面三章,我们学了列表、元组、字典、集合,还学了条件判断和循环。
学到这里,各位童鞋是不是有种感觉:脑子里装了一堆零件,每个零件都认识,可真要拼成一个能用的程序,又不知道从哪儿下手?
其实,这时候缺的往往不是语法,而是拆问题的习惯。
什么叫拆问题呢?就是写程序之前,先用大白话把要做的事一条一条写下来,再看看每一条该用学过的哪个零件。拆清楚了,写代码基本上就是照着翻译。
光说不练可不行。这一章,咱们还是拿实战 1 的记账本来练手。它现在只能记一笔,而且程序一结束,记过的账也就忘了。我们要让它一口气记下一整天的账,最后还能告诉你:钱都花到哪儿去了。
代码会比实战 1 长不少,第一遍写不对很正常。别急着看答案,先对照流程,把程序一步一步走一遍,看看是哪一步出了岔子。自己找出来的问题,比直接看答案记得牢多了。
学这一章之前,需要先学完 实战 1 的记账本 v0,以及第 3~5 章的列表、字典、条件判断和循环。哪里卡住了,就回到对应的章节看一看:第 3 章 、第 4 章 、第 5 章 。
目录
这一章会学到:
- 一、一天要记好几笔,怎么办?——看看记账本 v0 差在哪儿
- 二、先别写代码,把需求拆开——用大白话写出步骤,再画成流程
- 三、一笔账怎么存?——两个列表为什么容易对不上,换成「列表里装字典」
- 四、让程序一直问下去——用
while True加break,输入「完」就结束 - 五、钱都花哪儿了?——用字典按类别汇总
- 六、常见坑
- 七、自己查一次文档——
dict.items()到底返回了什么 - 八、记账本 v1
- 九、小结与回看
学完这一章,你的记账本可以一口气记下一整天的账,列出每一笔明细,再按类别算出各花了多少钱。
一、一天要记好几笔,怎么办?
咱们先回顾一下,实战 1 写出来的记账本 v0 是这样的:
item = input('买了什么:')
cost = float(input('花了多少钱:'))
print(f'记下了:{item},{cost:.2f} 元')输出的结果:
买了什么:奶茶
花了多少钱:12.5
记下了:奶茶,12.50 元问题很明显:它只能记一笔。可一天下来,早饭、地铁、午饭、奶茶……少说也有五六笔。
那么,照着样子多写几遍 input() ,行不行呢?
item1 = input('买了什么:')
cost1 = float(input('花了多少钱:'))
item2 = input('买了什么:')
cost2 = float(input('花了多少钱:'))
item3 = input('买了什么:')
cost3 = float(input('花了多少钱:'))
# ……今天要是记了 8 笔呢?写是能写,可问题马上就来了:
- 每天花几笔是不固定的,今天 3 笔,明天可能 8 笔。代码里到底写几个
input()? - 变量
item1、item2、item3……越写越多,最后算合计时,还得一个一个加起来。
是不是发现,这两个问题,前面几章其实都给过办法?
- 数量不固定、要装很多个东西,用列表。
- 同样的事要做很多遍,用循环。
所以这一章要做的,就是这件事:用列表把账装起来,用循环一笔一笔地问,记完了再汇总。
不过,先别急着写代码。动手之前,还有一件更要紧的事要做,下一节就来说。
二、先别写代码,把需求拆开
上一节说,先别急着写代码。为什么呢?因为很多童鞋一拿到题目,就直接打开编辑器开始敲。敲着敲着就乱了:这个变量放循环里面还是外面?什么时候停下来?合计又在哪儿算?
其实,写代码之前,先用大白话把要做的事写下来,很多问题自己就清楚了。
不信咱们试一试。假设没有电脑,你拿着一个小本子帮朋友记账,会怎么做?
- 准备一个空本子。
- 问朋友:买了什么?
- 朋友说「完了」,就不再问了,直接跳到第 6 步。
- 再问:花了多少钱?
- 把这一笔写到本子上,回到第 2 步接着问。
- 把本子上的每一笔念一遍,再加起来算个总数。
这几步,就是程序要做的事。把它们画成流程,中间那一圈「问了又问」就看得更清楚了:
① 准备一个空账本
② 问:买了什么?
├─ 回答「完」:跳到 ⑤
└─ 其他回答:往下走
③ 问:花了多少钱?
④ 把这一笔记进账本,回到 ②
⑤ 列出每一笔,算出合计画成流程的时候,清单里的第 3 步并进了第 ② 步,变成了它下面的两个分叉,所以流程比清单少了一步。
流程有了,接下来,给每一步找一个学过的零件,也就是看看每一步该用什么来写:
| 要做的事 | 用什么 |
|---|---|
| 准备一个空账本 | 空列表 entries = [] |
| 一遍一遍地问 | 循环 |
| 回答「完」就不问了 | if 判断,再用 break 跳出循环 |
| 记一笔账 | 列表的 append() |
| 算合计 | 再用一次循环,把金额一笔一笔加起来 |
你会发现,表格右边那一栏,全是前面几章学过的东西,没有一样是新的。
不过,还有一个问题表里没回答:「一笔账」本身该怎么存?它有名称、有金额,后面还要加上类别。这几样东西怎么放在一起,就是下一节要解决的问题。
三、一笔账怎么存?
上一节留下了一个问题:一笔账该怎么存?先想想,一笔账至少有两样东西:买了什么、花了多少钱。
最先想到的办法,可能是用两个列表,一个存名称,一个存金额:
items = ['奶茶', '地铁', '午饭']
costs = [12.5, 4, 25]
print(items[0], costs[0])
print(items[2], costs[2])输出的结果:
奶茶 12.5
午饭 25同一个位置上的名称和金额,正好凑成一笔账。看起来没毛病。
可是,要是删掉其中一笔呢?比如地铁那笔记错了,要删掉:
items = ['奶茶', '地铁', '午饭']
costs = [12.5, 4, 25]
del items[1]
print(items[1], costs[1])输出的结果:
午饭 4午饭怎么变成 4 块钱了?
因为只删了 items 里的「地铁」,忘了删 costs 里的 4 ,两个列表一下子就对不上了。而且程序没有报错,又是一个「不报错的 bug」。
说到底,两个列表要时刻保持一致,每次增删都得记着同时改两处,一不留神就会出错。那么,能不能把一笔账的名称和金额绑在一起,放进同一个东西里呢?
能。第 4 章学的字典,干的正是这个活儿:
entry = {'item': '奶茶', 'cost': 12.5}
print(entry['item'])
print(entry['cost'])输出的结果:
奶茶
12.5一个字典就是一笔账,用 'item' 、'cost' 这两个键,就能取出名称和金额。
一笔账是一个字典,那很多笔账呢?就是很多个字典。把它们装进一个列表里,再用 for 一笔一笔地打印出来:
entries = [
{'item': '奶茶', 'cost': 12.5},
{'item': '地铁', 'cost': 4},
{'item': '午饭', 'cost': 25},
]
for entry in entries:
print(f"{entry['item']} {entry['cost']:.2f} 元")输出的结果:
奶茶 12.50 元
地铁 4.00 元
午饭 25.00 元这段代码里,有个小地方要留意一下:f-string 外面的引号,换成了双引号。为什么要换呢?因为花括号里取键用的是单引号,外面要是也用单引号,Python 3.12 之前的版本会直接报语法错误。新版本虽然不报错,可读起来也很容易看混。
再回到删除的问题。这回要删掉地铁那笔,del entries[1] 一下就行。名称和金额是一起删掉的,不可能再对不上了。
算合计也不难,同样是用循环,一笔一笔地加:
entries = [
{'item': '奶茶', 'cost': 12.5},
{'item': '地铁', 'cost': 4},
{'item': '午饭', 'cost': 25},
]
total = 0
for entry in entries:
total = total + entry['cost']
print(f'合计:{total:.2f} 元')输出的结果:
合计:41.50 元total 一开始是 0,循环每转一圈,就把这一笔的金额加上去。是不是和第 5 章算 1 到 100 的和,是一个路子?
账本的样子定下来了。先做一道练习,用 for 循环打印明细、算出合计。下一节,再让程序自己一笔一笔地问。
编辑器里的 entries 存了今天的四笔账,每一笔都是一个字典。请用 for 循环把每一笔打印成一行,金额保留两位小数,同时算出合计,最后打印合计。
运行后的输出应该是:
早饭 8.00 元
地铁 4.00 元
午饭 25.00 元
奶茶 12.50 元
合计:49.50 元推荐一个,全网最全最友好的中文 AI 编程指南: coding.stormzhang.ai