本节摘要:代码一过百行,"从上读到下"就变成了体力活。函数把一段有明确职责的代码封进名字里,主流程退化成几行"调零件"的清单;模块再把函数按主题分进不同文件。本节讲函数的定义、参数、返回值与作用域,然后把轻记账 v0.1 重构成函数版——功能一分不变,可读性和可改性上一个台阶,也为第二章拆文件铺路。
阅读完本节,你应当能够:
看看 v0.1 的主循环:记账逻辑六行、汇总逻辑五行,全挤在分支里。将来要加"统计某天支出""导出账目",主循环会膨胀到两三百行,想改汇总就得在几百行里翻找。函数解决的就是这个问题:给一段代码起名字,之后用名字调用它。改功能时只改函数内部,调用处一行不动——这是"可维护"三个字最朴素的实现。

def add_record(ledger, item, amount): """往账本里记一笔,返回追加后的总条数。""" ledger.append({"item": item, "amount": amount}) return len(ledger)
五个部件:def 开头;函数名小写下划线;圆括号里是参数——函数的"进料口";三引号字符串是文档字符串,说明这函数干什么、返回什么,编辑器悬停时能看到;return 是出口,把结果交还给调用者。没有 return 的函数返回 None。
调用就是"名字加实参":
ledger = [] n = add_record(ledger, "早饭", 12.5) print(n) # 1
参数有三种写法,各有用途:
def calc_total(ledger, since=None, only_big=False): total = 0.0 for record in ledger: if since and record.get("date", "") < since: continue if only_big and record["amount"] < 10: continue total += record["amount"] return total print(calc_total(ledger)) # 位置参数 print(calc_total(ledger, only_big=True)) # 关键字参数:可读性最好 print(calc_total(ledger, since="2026-09-02"))# 默认参数:不传就用默认值
since=None 这种"可选开关"设计在后面章节反复出现——API 的查询参数、ORM 的过滤条件,全是这个模式。
函数内部创建的变量是局部变量,函数返回就消失;函数外的是全局变量,谁都能读:
total = 0.0 # 全局 def add_amount(amount): temp = amount * 2 # 局部,函数结束即销毁 return temp print(add_amount(10)) # 20 # print(temp) # NameError —— 外面看不见 temp
要在函数内修改全局变量,得用 global 声明。但更好的做法是不用全局:把数据当参数传进去、把结果当返回值带出来。参数进、返回值出,函数像一台干净的机器,测试和复用都容易。轻记账重构版就坚持"数据靠参数流动",第 3 章的类会把这个习惯推到极致。
def add_record(ledger, item, amount): ledger.append({"item": item, "amount": amount}) def show_all(ledger): for i, record in enumerate(ledger, start=1): print(f"{i}. {record['item']} {record['amount']} 元") def calc_total(ledger): return sum(record["amount"] for record in ledger) def main(): ledger = [] running = True while running: command = input("指令(记 / 查 / 退):") if command == "记": add_record(ledger, input("用途:"), float(input("金额:"))) elif command == "查": show_all(ledger) print(f"合计 {calc_total(ledger):.1f} 元") elif command == "退": running = False else: print("指令无效") main()
读 main,主流程四行指令一目了然。变式一:加 find_big(ledger, threshold) 函数,返回大额账目列表,在"查"分支里顺便显示。变式二:把账本数据 ledger 从 main 的局部变量改成函数间传递——你会发现参数一多就心烦,这正是第 3 章用类把"数据 + 操作"打包的动机。
函数攒多了要分文件。Python 里一个源文件就是一个模块,import 拿来用:
# 账目工具模块里定义了 calc_total # 主脚本里: # import ledger_tools # print(ledger_tools.calc_total(my_ledger))
标准库就是官方写好的模块集合:datetime 管时间、json 管数据交换,第二章会真正用起来。自己写的模块和标准库用法完全一致——这种"官方与民开同构"的整洁,是 Python 生态能滚雪球的原因之一。
零件的形状都有了,下一章先把"存不住"治了——文件读写与 JSON 持久化,让账目在关机之后依然活着。