递归调用中Python列表作用域疑问:排列生成器为何不能用temp=temp[:-1]
递归中Python列表的作用域问题:为什么
temp = temp[:-1]不行,必须用del temp[-1] 这是个非常典型的Python可变对象在递归回溯场景下的坑,我来给你掰扯清楚背后的逻辑:
首先得明确两个核心点:
- Python列表是可变对象:多个变量可以同时指向同一个列表对象,修改其中一个变量指向的列表,所有引用它的变量都会看到变化。
- 嵌套函数的变量作用域规则:如果在嵌套函数里对变量进行赋值操作(比如
temp = ...),这个变量会被视为函数的局部变量,和外层同名变量彻底断开引用关系。
回到你的递归代码场景:
当你调用dfs(temp, visited)的时候,传入的temp是一个列表的引用,递归过程中所有层级的dfs函数里的temp都指向同一个列表对象(除非你重新赋值它)。
为什么temp = temp[:-1]会出问题?
temp[:-1]会创建一个原列表的切片副本,然后temp = ...是把当前dfs函数里的局部变量temp指向了这个新的副本。但原来的列表对象(也就是上层递归函数里的temp指向的那个)完全没被修改!
举个简化的小例子就能看明白:
def outer(): temp = [1, 2] def inner(): # 重新赋值temp,创建局部变量 temp = temp[:-1] print("inner里的temp:", temp) # 输出 [1] inner() print("outer里的temp:", temp) # 输出 [1,2]!原列表根本没动 outer()
放到你的递归里,这就意味着递归返回后,上层函数的temp仍然保留着刚才append的元素,后续循环再append新元素时,列表会越来越长,生成的排列自然全是错的。
为什么del temp[-1]是对的?
del temp[-1]并不是赋值操作,而是直接在当前temp指向的原列表对象上修改:移除最后一个元素。因为所有递归层级的temp都指向同一个列表,这个修改会被所有层级看到。
同样用简化例子验证:
def outer(): temp = [1, 2] def inner(): # 修改原列表对象 del temp[-1] print("inner里的temp:", temp) # 输出 [1] inner() print("outer里的temp:", temp) # 输出 [1]!原列表被修改了 outer()
在你的递归回溯中,这正好符合需求:递归返回后,把刚才添加的元素从原列表里删掉,让上层循环可以继续添加下一个元素,生成正确的排列。
再补充个等价写法
其实你也可以用temp.pop()来替代del temp[-1],效果是一样的,都是直接修改原列表对象,而且代码更简洁:
dfs(temp, visited) temp.pop() # 和del temp[-1]等价 visited[i] = False
内容的提问来源于stack exchange,提问作者yuzhoul
相关产品推荐
相关产品推荐

