本节摘要:默认情况下代码从上到下一行行执行,但真实程序必须"看情况办事":输入不同指令走不同分支,批量任务用循环重复。本节讲 if 分支、while 循环、for 遍历三件套,并把它们拧成轻记账 v0.1 的交互主循环——程序启动后反复等待指令,直到用户说退出。这一节写完,你的脚本第一次有了"应用"的样子。
阅读完本节,你应当能够:
上一节的脚本只会从头跑到尾:打印汇总、结束。可你想要的账本是对话式的——敲"记"就记账,敲"查"就汇总,敲"退"才退出。这需要两种新能力:按条件选路(分支)和把一段代码反复执行(循环)。所有编程语言的控制流都是这两件事的组合,Python 用缩进来划定每个分支和循环的边界,这是它和其他语言最直观的不同。
command = "查" if command == "记": print("进入记账流程") elif command == "查": print("显示账目汇总") elif command == "退": print("再见") else: print("没听懂这个指令")
三个细节值得嚼一嚼。其一,== 是比较,= 是赋值,写混是终身高频笔误。其二,elif 是"否则如果",逐条往下试,命中一条就跳过其余,所以条件的排列顺序就是优先级。其三,else 兜底处理"意料之外",交互程序里它常用来提示无效输入——轻记账的 else 正是这个用途。
条件本身由比较与逻辑运算符组成:>、<、>=、!=,以及"并且" and、"或者" or、"取反" not。例如"金额超过 100 并且未报销"写成 amount > 100 and not is_refunded。
交互程序的特点是:你不知道用户会敲多少条指令,只知道"没敲退出就继续"。这正是 while 的适用场景——条件成立就一直转:
running = True while running: command = input("请输入指令(记 / 查 / 退):") if command == "退": running = False # 下一轮循环条件不成立,退出 elif command == "记": print("记账功能稍后实现") else: print("暂不支持", command) print("程序结束")
input() 会暂停程序等用户敲字,敲完回车返回一个字符串。运行这段代码,程序就"活"了:你敲什么它应什么,敲"退"才停。轻记账的主循环骨架就是这个形状,后面每一章都只是往循环的分支里填更丰满的实现。
上一节末尾出现过它,现在拆开讲。for 把列表里的元素逐个取出:
ledger = [ {"item": "早饭", "amount": 12.5}, {"item": "地铁", "amount": 4.0}, {"item": "奶茶", "amount": 18.0}, ] total = 0 big = [] for record in ledger: total += record["amount"] # += 是累加简写 if record["amount"] >= 10: big.append(record["item"]) print(f"合计 {total} 元") # 合计 34.5 元 print("大额消费:", big) # 大额消费: ['早饭', '奶茶']
循环体里也可以用分支,这正是"控制流组合"的含义。两个控制词的区别要用场景记:
for record in ledger: if record["amount"] < 5: continue # 小额的跳过,继续下一笔 if total > 100: break # 累计超 100,整个循环提前结束 total += record["amount"]
continue 跳过本轮剩余语句进入下一轮;break 直接终止整个循环。再介绍一个高频搭档 range:
for i in range(1, 4): # 1、2、3,含头不含尾 print(f"第 {i} 天")
把分支、循环、上一节的数据结构全部接起来,这就是全书项目的起点:
ledger = [] total = 0.0 running = True while running: command = input("指令(记 / 查 / 退):") if command == "记": item = input("用途:") amount = float(input("金额:")) ledger.append({"item": item, "amount": amount}) print(f"已记:{item} {amount} 元") elif command == "查": for i, record in enumerate(ledger, start=1): print(f"{i}. {record['item']} {record['amount']} 元") print(f"共 {len(ledger)} 笔,合计 {sum(r['amount'] for r in ledger):.1f} 元") elif command == "退": running = False else: print("指令无效,请输入 记 / 查 / 退") print("账本已关闭")
一次完整会话的输出长这样:
指令(记 / 查 / 退):记 用途:早饭 金额:12.5 已记:早饭 12.5 元 指令(记 / 查 / 退):查 1. 早饭 12.5 元 共 1 笔,合计 12.5 元 指令(记 / 查 / 退):退 账本已关闭
变式一:加一个"删"指令,输入序号删除对应账目,注意删完序号会整体前移。变式二:故意在金额处输入"abc",程序当场崩溃——float() 转不动非数字字符串。这个崩溃不是意外,是下一节异常处理的正式出场理由。
amount > 0 再写 amount > 100,后者永远轮不到running = False 这类"出口语句"别漏enumerate 同时拿序号与元素脚本能对话了,却经不起一次手滑。下一节专门处理"程序崩了怎么办"之前的铺垫先放一放,我们先让账目在两次运行之间活下来——文件与持久化。