Python递归循环中列表元素变化及deep_reverse逻辑疑问
deep_reverse递归上下文问题解答
问题背景
现有支持全层级深反转的deep_reverse函数,可递归反转列表及所有嵌套子列表,函数实现与运行打印结果如下:
def deep_reverse(arr): # 终止条件 if len(arr) < 1: return arr reversed_items = [] # 存储最终返回结果 for item in arr[::-1]: # 如果当前元素是列表,递归调用deep_reverse完成子列表反转 if type(item) is list: item = deep_reverse(item) # 将处理后的元素追加到结果列表 reversed_items.append(item) print(reversed_items) return reversed_items deep_reverse([1, 2, [3, 4, 5], 6, 7]) # 运行打印输出 [7] [7, 6] [5] [5, 4] [5, 4, 3] [7, 6, [5, 4, 3]] [7, 6, [5, 4, 3], 2] [7, 6, [5, 4, 3], 2, 1]
疑问点:每次调用deep_reverse都会初始化独立的空reversed_items列表,递归处理子列表[3,4,5]时会进入新的函数上下文生成独立列表,为何处理完子列表回到外层时,外层的reversed_items还保留之前追加的7、6元素,追加子列表反转结果后直接得到[7, 6, [5, 4, 3]]?
核心原理
这个现象本质是Python函数调用的栈帧隔离特性:
- 每一次函数调用都会生成独立的栈帧(stack frame),每个栈帧内的局部变量是完全独立的内存对象,不同调用层级的同名变量不会互相覆盖、篡改。
- 递归调用触发时,当前外层函数的执行状态会被完整保存在自己的栈帧里,暂停执行等内层递归返回结果后,才会继续往下走,保存的状态不会丢失。
逐步骤执行流程拆解
结合打印结果,整个执行过程可以拆成3个阶段:
- 外层调用初始化阶段
- 最外层调用
deep_reverse([1, 2, [3, 4, 5], 6, 7]),生成外层栈帧,初始化属于外层的空列表reversed_items - 倒序遍历原列表,第一个元素7不是列表,直接追加到外层的
reversed_items,此时外层列表值为[7],对应第一行打印 - 第二个遍历到的元素是6,不是列表,追加后外层列表值为
[7,6],对应第二行打印 - 第三个遍历到的元素是子列表
[3,4,5],触发递归调用,此时外层栈帧把当前状态(reversed_items = [7,6]、当前遍历到第三个元素)完整保存,暂停执行
- 最外层调用
- 内层递归执行阶段
- 调用
deep_reverse([3,4,5])生成内层栈帧,初始化和外层完全无关的空reversed_items,这个新列表和外层存的[7,6]是内存里完全独立的两个对象 - 倒序遍历子列表,依次追加5、4、3,内层的
reversed_items依次变为[5]、[5,4]、[5,4,3],对应第三到第五行打印 - 内层循环执行完毕,把
[5,4,3]作为返回值传回外层,随后内层栈帧销毁,内层的reversed_items生命周期结束
- 调用
- 外层调用恢复执行阶段
- 外层拿到递归返回的
[5,4,3],从之前暂停的位置继续往下执行:把返回的子列表追加到自己的reversed_items里 - 因为外层栈帧保存的状态从来没被改动过,之前存的7、6自然保留,追加完的结果就是
[7,6,[5,4,3]],对应第六行打印 - 后续循环继续遍历到2、1,依次追加到外层列表,最终得到完整的深反转结果,对应最后两行打印
- 外层拿到递归返回的
可以自行做验证:在递归调用前后分别打印
id(reversed_items),会发现外层的reversed_items内存地址全程不变,递归过程中打印的内层reversed_items地址是完全不同的另一个值,二者没有任何关联。
内容的提问来源于stack exchange,提问作者Moe
相关产品推荐
相关产品推荐

