You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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()的局部集合:
    1. g()不断将新生成的候选值加入局部集合,但生成函数检查的是全局空集合,导致全局集合持续积累候选值,而局部集合完全不同步。
    2. 原代码中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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 14:05:58