Python函数跨模块导入异常:独立模块可用,算法模块失效
问题分析:遗传算法函数跨模块调用的无限循环问题
问题描述
编写了一段猜测字符对"ab"的遗传算法代码,其中generate_random_candidate函数在单独的scratch_2模块中导入调用时运行完全正常;但从包含完整遗传算法逻辑的scratch_1模块中导入该函数并调用时,程序陷入无限循环,打印全局变量tried_candidates始终为空集合,无法正常返回结果。疑惑同一函数为何因所在模块不同出现运行差异。
导入调用代码
from scratch_2 import generate_random_candidate print(generate_random_candidate())
scratch_2模块代码
import random import string target = "ab" letras = string.ascii_lowercase tried_candidates = set() def generate_random_candidate(): while True: intento = random.choice(letras) + random.choice(letras) if intento not in tried_candidates: tried_candidates.add(intento) return intento
scratch_1模块代码
import random import string import matplotlib.pyplot as plt import plotly.graph_objects as go target = "ab" letras = string.ascii_lowercase tried_candidates = set() def generate_random_candidate(): while True: intento = random.choice(letras) + random.choice(letras) if intento not in tried_candidates: tried_candidates.add(intento) return intento def generate_children(parent): hijos = [] while True: intento = parent[0] + random.choice(letras) if intento not in tried_candidates: hijos.append(intento) break while True: intento = random.choice(letras) + parent[1] if intento not in tried_candidates: hijos.append(intento) break return hijos def evaluate_candidate(candidate): score = 0 if candidate[0] == target[0]: score += 1 if candidate[1] == target[1]: score += 1 """ if candidate[0] == target[1]: score += 0.1 if candidate[1] == target[0]: score += 0.1 """ return score def g (): tried_candidates = set() population_size = 10 population = [] for _ in range(population_size): population.append(generate_random_candidate()) contador = 1 while True: tried_candidates.update(population) scores = [] for candidate in population: scores.append(evaluate_candidate(candidate)) children = [] Flag = False for i, score in enumerate(scores): if score == 2: Flag = True break if score == 1 : x = generate_children(population[i]) tried_candidates.update(x) children.extend(generate_children(population[i])) if len(children) < population_size: new_candidates = [] for _ in range(population_size - len(children)): y = generate_random_candidate() tried_candidates.update(y) new_candidates.append(y) children.extend(new_candidates) population = children else: meter = children[population_size:] population = children[:population_size] tried_candidates.difference_update(meter) found = False for candidate in population: if evaluate_candidate(candidate) == 2 or Flag == True: found = True return contador contador = contador +1 resultados = [] for i in range (100000): resultados.append(g())
问题根源
核心问题是全局变量的作用域冲突,以及函数对全局变量的依赖导致的行为差异:
- 在
scratch_2中,generate_random_candidate操作的是模块级的全局tried_candidates集合,每次调用都会正确记录已尝试的候选值,不会出现无限循环(除非所有26×26=676种组合被耗尽,测试阶段不会触发)。 - 在
scratch_1的g()函数里,定义了局部变量tried_candidates = set(),但generate_random_candidate和generate_children依然引用scratch_1模块级的全局tried_candidates集合,而非g()的局部集合:g()不断将新生成的候选值加入局部集合,但生成函数检查的是全局空集合,导致全局集合持续积累候选值,而局部集合完全不同步。- 原代码中
tried_candidates.update(y)的y是单个字符串,update方法会将字符串拆分为字符加入集合(如"ab"会变成{'a','b'}),彻底破坏了候选值的记录逻辑,最终导致generate_random_candidate生成重复候选值时陷入死循环。
修复方案
核心思路是消除函数对全局变量的依赖,改为通过参数传递状态集合,避免作用域混淆:
1. 修改生成函数,传递状态集合
将generate_random_candidate和generate_children改为接受tried_candidates参数:
def generate_random_candidate(tried_candidates): while True: intento = random.choice(letras) + random.choice(letras) if intento not in tried_candidates: tried_candidates.add(intento) return intento def generate_children(parent, tried_candidates): hijos = [] while True: intento = parent[0] + random.choice(letras) if intento not in tried_candidates: hijos.append(intento) break while True: intento = random.choice(letras) + parent[1] if intento not in tried_candidates: hijos.append(intento) break return hijos
2. 修改g()函数,传递局部集合
在g()中调用生成函数时,传入局部的tried_candidates集合,并修复两处逻辑错误:
def g(): tried_candidates = set() population_size = 10 population = [] for _ in range(population_size): population.append(generate_random_candidate(tried_candidates)) contador = 1 while True: tried_candidates.update(population) scores = [] for candidate in population: scores.append(evaluate_candidate(candidate)) children = [] Flag = False for i, score in enumerate(scores): if score == 2: Flag = True break if score == 1 : x = generate_children(population[i], tried_candidates) tried_candidates.update(x) children.extend(x) # 直接复用已生成的子节点,避免重复调用 if len(children) < population_size: new_candidates = [] for _ in range(population_size - len(children)): y = generate_random_candidate(tried_candidates) new_candidates.append(y) children.extend(new_candidates) population = children else: meter = children[population_size:] population = children[:population_size] tried_candidates.difference_update(meter) found = False for candidate in population: if evaluate_candidate(candidate) == 2 or Flag == True: found = True return contador contador += 1
额外修复说明
- 原代码中
children.extend(generate_children(population[i]))会重复调用生成函数,导致生成重复子节点,改为复用已生成的x。 - 原代码中
tried_candidates.update(y)属于冗余操作,因为generate_random_candidate已经将y加入集合,且update处理单个字符串会拆分字符,完全不符合需求,直接删除。
内容的提问来源于stack exchange,提问作者pedro rodriguez
相关产品推荐
相关产品推荐

