Python类中前置条件循环依赖引发的无限循环问题求解
解决Level类循环依赖导致的前置条件无限递归问题
我创建了一个Level类,包含name和层级前置条件两个属性,希望通过all_preconditions属性获取某一层级的所有前置条件集合(例如:Level 3的前置是{Level 2},Level 2的前置是{Level 1},那么Level 3的所有前置为{Level 1, Level 2})。
原代码如下:
class Level: def __init__(self, name: str, preconditions: list[Level] = [], ): self.name = name self.preconditions = set(preconditions) def __repr__(self): return f'Level(name="{self.name}", preconditions={self.preconditions})' @property def all_preconditions(self) -> set[Level]: preconditions = set() for p in self.preconditions: preconditions.update(p.preconditions) p = p.preconditions return preconditions
当前代码在正常场景下可用,但存在两个问题:
- 构造函数默认参数使用了可变类型
list[],这会导致所有未传入preconditions的Level实例共享同一个集合,是Python常见陷阱。 - 存在循环依赖时(例如Level A的前置是{Level B},Level B的前置是{Level A}),会触发无限递归,导致输出无限循环。
修复方案
1. 修复构造函数的默认参数问题
将可变默认参数替换为None,再在内部初始化空集合,避免实例共享集合的问题。
2. 为all_preconditions添加已访问集合,避免循环递归
通过维护一个记录已处理Level实例的集合,确保每个实例只被处理一次,既阻止循环递归,也避免重复收集前置条件。
修改后的完整代码
class Level: def __init__(self, name: str, preconditions: list[Level] | None = None, ): self.name = name # 修复可变默认参数问题 self.preconditions = set(preconditions) if preconditions is not None else set() def __repr__(self): # 简化输出,避免嵌套循环显示问题 return f'Level(name="{self.name}")' @property def all_preconditions(self) -> set[Level]: visited = set() result = set() def collect_preconditions(level: Level): if level in visited: return visited.add(level) # 添加当前层级的直接前置 result.update(level.preconditions) # 递归收集每个直接前置的所有前置 for p in level.preconditions: collect_preconditions(p) collect_preconditions(self) return result
代码说明
- 构造函数修复:用
None作为默认参数,彻底避免多个实例共享同一集合的问题。 - 已访问集合
visited:递归收集前置时先检查当前Level是否已处理,若已处理则直接返回,从根源阻止循环递归。 - 简化
__repr__:避免打印集合时触发无限嵌套的显示问题,只输出核心的name属性。
测试验证
循环依赖场景
# 创建互相依赖的两个层级 a = Level("A") b = Level("B") a.preconditions.add(b) b.preconditions.add(a) print(a.all_preconditions) # 输出: {Level(name="B")} print(b.all_preconditions) # 输出: {Level(name="A")}
正常层级依赖场景
level1 = Level("Level 1") level2 = Level("Level 2", [level1]) level3 = Level("Level 3", [level2]) print(level3.all_preconditions) # 输出: {Level(name="Level 1"), Level(name="Level 2")}
内容的提问来源于stack exchange,提问作者ezgibec
相关产品推荐
相关产品推荐

