Python递归调用传递列表参数时不同写法结果差异问题咨询
问题解答
核心原因
两种写法的差异本质是Python变量赋值时机、表达式从左到右的求值规则,以及可变对象修改/重绑定的区别共同导致的。
f1与f2的差异细节
f1的执行逻辑
f1将两次递归调用拆分到了两个独立的赋值语句中:
# 第一次赋值:执行完第一个递归调用后,立刻更新l1变量的指向 l1 = [*l1, *f1(l1, num-1)] # 第二次赋值:传入的l1已经是第一次更新后的值 l1 = [*l1, *f1(l1, num -1)]
执行顺序是:
- 调用第一个
f1(l1, num-1),拿到返回值 - 解包生成新列表,赋值给当前作用域的
l1变量,此时l1已经指向修改后的新列表 - 调用第二个
f1(l1, num-1),传入的是更新后的l1,所以第二次调用能拿到第一次修改的结果。
f2的执行逻辑
f2将两次递归调用写在了同一个列表构造表达式中:
l1 = [*l1, *f2(l1, num-1),*f2(l1, num -1)]
Python表达式严格按照从左到右的顺序求值,所有子表达式求值完成后才会执行最终的赋值操作,执行顺序是:
- 先读取当前
l1的值(初始值,还没被修改) - 调用第一个
f2(l1, num-1),传入初始的l1,函数内部的l1赋值是局部变量重绑定,不会修改外层作用域传入的原列表对象,调用完成后外层的l1还是初始值 - 调用第二个
f2(l1, num-1),传入的还是初始的l1,所以两次调用的入参完全相同 - 所有子表达式求值完成后,才将最终构造的新列表赋值给
l1,因此第二次递归调用拿不到第一次的修改结果。
f3的特殊情况说明
f3和f2的核心区别是:f3是原地修改列表对象,而不是重绑定变量:
def f3(l1): print(l1) l1 +=[1] # += 对于列表是原地修改操作,直接改变原对象的内容,不是生成新列表重绑定变量 return l1
执行顺序是:
- 第一个
f3(l1)调用时,直接修改了传入的原列表对象的内容,此时l1指向的对象本身已经发生变化 - 第二个
f3(l1)调用时,传入的还是同一个列表对象,因此能看到前一次调用的修改结果 - 所有子表达式求值完成后,才构造新列表赋值给外层的
l1。
f2的递归调用中没有修改最初传入的原列表对象,只是函数内部给局部变量绑定了新的列表,因此前一次调用的修改不会影响后续同表达式内的调用入参,和f3的场景有本质区别。
内容的提问来源于stack exchange,提问作者Аркадий Момосов
相关产品推荐
相关产品推荐

