Python 函数图解:参数、局部变量与 return 怎样流动
沿一条订单金额调用轨迹,看见实参怎样进入参数名、局部变量何时存在、return 怎样回到调用位置,以及 print 和可变默认参数的真实去向。
本文目录 7 节 · 点击展开
循环让同一段代码依次处理多条订单。函数则给一段处理逻辑起名字,并为它划出清楚的入口、内部空间和出口。
真正调用函数时,数据并不是“突然出现在函数里”。实参会先进入参数名,函数内部创建局部变量,return 再把结果送回原来的调用位置。这一篇就沿着一笔订单金额看完整条路径。
定义函数,不等于已经执行函数体
先定义一个订单金额函数:
def calculate_order_amount(price, quantity, discount_rate=0.0):
subtotal = price * quantity
discount = subtotal * discount_rate
final_amount = subtotal - discount
return final_amount
执行这段 def 时,Python 只是记住:以后可以用 calculate_order_amount 这个名字调用这段逻辑。函数体里的乘法、减法和 return 还没有运行。
真正的处理从调用开始:
def calculate_order_amount(price, quantity, discount_rate=0.0):
subtotal = price * quantity
discount = subtotal * discount_rate
final_amount = subtotal - discount
return final_amount
unit_price = 129.0
order_quantity = 2
amount = calculate_order_amount(
unit_price,
order_quantity,
discount_rate=0.1,
)
print(amount)
输出:
232.2
一次调用怎样进去,再带着结果出来
下面的调用轨迹分成两个空间:左右两端是函数外的调用方,中间带边框的区域是本次调用临时打开的函数空间。
232.2 的完整来路calculate_order_amount(129.0, 2, discount_rate=0.1)- 实参
129.0- 实参
2- 实参
0.1
先求出三个实参的值,再打开一次函数调用。
price → 129.0quantity → 2discount_rate → 0.1外部实参进入函数定义中的参数名。
subtotal → 258.0discount → 25.8final_amount → 232.2这些名字只服务于本次调用。
return final_amount送出 232.2执行到 return,函数体立即结束。
amount = 232.2- 保留
unit_price · order_quantity- 新增
amount
返回值替换原来的调用表达式,再绑定给 amount。
unit_price · order_quantity · amountprice · quantity · subtotal · discount · final_amount这条轨迹同时解释了三个常见词:
| 名称 | 在本例中是什么 | 什么时候存在 |
|---|---|---|
| 实参 | unit_price、order_quantity、0.1 的实际值 | 调用位置先求值 |
| 参数 | price、quantity、discount_rate | 函数调用期间 |
| 局部变量 | subtotal、discount、final_amount | 执行到对应赋值行后,直到本次调用结束 |
前两个实参按位置进入 price 和 quantity。discount_rate=0.1 是关键字参数,它明确指定进入哪个参数。若调用时不写折扣率,就使用定义中的默认值 0.0:
full_amount = calculate_order_amount(129.0, 2)
print(full_amount) # 258.0
局部变量不会自动跑到函数外面
subtotal 在函数内部确实得到过 258.0,但调用结束后,外部代码不能直接用这个局部名字:
amount = calculate_order_amount(129.0, 2)
print(amount) # 258.0
print(subtotal) # NameError: name 'subtotal' is not defined
需要带到外面的结果,应通过 return 送出,再由调用方使用自己的变量名接住。这个边界能防止函数内部的临时名字散落到整份脚本里。
print() 和 return 把结果送去了不同地方
下面两个函数都算出 258.0,但结果的去向不同:
def print_amount(price, quantity):
subtotal = price * quantity
print(subtotal)
def return_amount(price, quantity):
subtotal = price * quantity
return subtotal
printed_result = print_amount(129.0, 2)
returned_result = return_amount(129.0, 2)
print(printed_result) # None
print(returned_result * 1.1) # 283.8
print(subtotal)显示路径- 01 · 函数内部
subtotal → 258.0 - 02 · 终端输出屏幕显示
258.0 - 03 · 调用表达式的值
None函数没有 return - 04 · 外部状态
printed_result → None不能拿来继续算金额
return subtotal返回路径- 01 · 函数内部
subtotal → 258.0 - 02 · return把
258.0送回调用位置 - 03 · 外部状态
returned_result → 258.0 - 04 · 后续计算
258.0 * 1.1 → 283.8返回值可以继续流动
print() 适合调试、提示和最终展示;return 适合把计算结果交给后面的代码。数据处理函数通常应该返回结果,由最外层代码决定何时显示。
可变默认参数为什么记住了上一次调用
数字和文字用作默认参数通常很直观,列表则需要格外小心。下面的写法只在执行 def 时创建一次默认列表:
def remember_order(order_no, history=[]):
history.append(order_no)
return history
print(remember_order("A001"))
print(remember_order("A002"))
很多人会以为两次调用各有一张空列表,实际输出却是:
['A001']
['A001', 'A002']
history=[]- 01 · 定义函数创建默认列表 L1
L1 → [] - 02 · 调用 A001history 继续指向 L1
L1 → [“A001”] - 03 · 调用 A002再次拿到同一个 L1
L1 → [“A001”, “A002”]
函数调用结束了,但函数定义仍保留着对默认列表 L1 的引用。
history=None- 01 · 定义函数默认值只是 None
不提前创建列表 - 02 · 调用 A001本次创建列表 L1
L1 → [“A001”] - 03 · 调用 A002下次创建新列表 L2
L2 → [“A002”]
每次没有传 history 时,都在函数内部创建一张新列表。
安全写法是把 None 当作“调用方没有传列表”的信号:
def remember_order(order_no, history=None):
if history is None:
history = []
history.append(order_no)
return history
print(remember_order("A001"))
print(remember_order("A002"))
输出:
['A001']
['A002']
如果调用方明确传入一张列表,函数仍会使用那张列表。这种写法修复的是“省略参数时意外共享默认对象”,不是禁止函数接收列表。
可选补充参数数量真的不固定时,再使用 *args
def total_amount(*amounts):
total = 0.0
for amount in amounts:
total += amount
return total
print(total_amount(129.0, 88.0, 199.0)) # 416.0函数内部的 amounts 是由所有位置实参组成的元组。订单金额函数的参数数量固定时,直接写 price、quantity 会比 *args 更清楚。
什么时候值得写成函数
函数不是为了把代码切得越碎越好。出现这些信号时,再考虑提取:
- 同一段逻辑会被重复调用。
- 一段处理能用清楚的动作命名。
- 输入和返回结果可以明确说清。
- 提取后,主流程更容易顺着读下去。
例如 calculate_order_amount 清楚表达了输入是价格、数量和折扣率,输出是最终金额。若某一行只出现一次,而且内联更直观,就不必为了形式增加函数。
动手练习:先画调用轨迹,再运行
复制本章的 calculate_order_amount,完成下面四步:
- 调用
calculate_order_amount(88.0, 3),写出参数绑定、三个局部变量和返回值。 - 改用
discount_rate=0.2,先预测subtotal、discount、final_amount,再运行确认。 - 把
return final_amount暂时改成print(final_amount),解释调用方变量为什么会得到None。 - 分别运行有风险和安全的
remember_order两次,指出第二次调用开始前默认列表是什么状态。