1. 从零理解函数调用的本质
第一次接触函数调用时,我误以为它就是个"跳转指令"。直到某次调试时发现变量值莫名改变,才真正理解函数调用的核心是执行环境的隔离与重建。每个函数调用都会在内存中创建一个新的栈帧(stack frame),这个独立空间存放着函数的局部变量、参数和返回地址。
1.1 函数调用背后的内存模型
当Python执行def foo(x): return x+1这样的函数定义时,实际上做了三件事:
- 在内存中创建函数对象
- 将函数名
foo绑定到该对象 - 记录函数体代码的存储位置
调用foo(3)时的完整过程:
python复制# 伪代码展示调用过程
1. 在调用栈分配新栈帧
2. 参数x=3压入栈帧
3. 保存当前执行位置(返回地址)
4. 跳转到foo函数体
5. 执行x+1得到4
6. 将4存入返回值存储区
7. 弹出当前栈帧
8. 回到返回地址继续执行
关键细节:每次调用都会产生独立的命名空间。即使递归调用自身,每次调用的局部变量也互不干扰。
1.2 参数传递的两种方式
我曾在项目中被参数传递方式坑过。Python采用"对象引用传递",但效果因对象类型而异:
| 传递类型 | 可变对象(list/dict) | 不可变对象(int/str) |
|---|---|---|
| 行为 | 类似引用传递 | 类似值传递 |
| 示例 | 函数内修改影响外部 | 函数内修改创建新对象 |
python复制def modify(items):
items.append(4) # 影响外部列表
a = [1,2,3]
modify(a)
print(a) # 输出[1,2,3,4]
def try_change(num):
num += 1 # 创建新对象
b = 5
try_change(b)
print(b) # 仍输出5
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 递归的实战技巧与避坑指南
初学递归时,我总在纸上画满调用栈却仍理不清头绪。直到把递归拆解成三个要素才豁然开朗:
- 基准条件(base case
