Python生成器函数遍历中断后出现异常耦合行为的问题排查
问题根源:Python可变默认参数的隐形陷阱
兄弟,这个问题我之前踩过一模一样的坑!核心原因就是Python函数的可变默认参数只会在函数定义时初始化一次,而不是每次调用函数都重新创建新的对象。你看你的get_paths函数里,path=[]和visited=set([])这俩默认参数,其实所有对get_paths的调用,都在共享同一个列表和集合!
具体发生了啥?
- 第一次调用
get_paths时,生成器开始执行,visited集合跟着递归不断添加、移除节点,path也在逐步拼接路径。 - 你第一个循环用
break跳出,相当于生成器中途暂停了——此时visited的状态停在了中途(不是空集合),生成器的执行上下文也保留了这个状态。 - 第二次再调用
get_paths时,函数并没有新建空的path和visited,而是直接用上一次剩下的那个visited集合!这就导致第二次的生成器不是从头开始找路径,而是从第一次暂停的位置继续往下跑,自然只输出后续的路径,而不是全部。
为啥不break就正常?
如果第一次循环不break,生成器会把所有路径都跑完,最后递归里的visited.remove(s)会把所有加进去的节点都移除干净,visited回到空集合状态。第二次调用时状态是干净的,所以能正常输出所有路径。
为啥重跑函数定义单元格就好?
重跑函数定义单元格时,相当于重新定义了get_paths函数,这时候默认参数path=[]和visited=set([])会被重新初始化,变成新的空列表和集合,后续调用自然就从头开始了。
怎么修复?
要解决这个问题,得确保每次调用函数都创建新的可变对象,避开默认参数的坑。修改成这样就行:
def get_paths(ug, s, t, path=None, visited=None): # 每次调用时才初始化新的列表和集合 if path is None: path = [] if visited is None: visited = set() if s == t: yield path.copy() # 返回副本,避免后续修改影响已生成的路径 visited.add(s) for n in ug[s]: if n in visited: continue yield from get_paths(ug, n, t, path + [n], visited) visited.remove(s)
另外提一句,yield path的时候最好返回path.copy(),因为列表是可变对象,如果后续代码修改了生成的路径,会影响已经yield出来的结果,返回副本就安全多了。
内容的提问来源于stack exchange,提问作者Silver
相关产品推荐
相关产品推荐

