Python函数内exec创建动态变量 循环/列表推导式报NameError
问题根因
该错误由Python函数内部的作用域编译规则导致,核心逻辑如下:
- Python在编译函数阶段就会静态扫描函数体内所有直接引用、直接赋值的变量,将其标记为局部变量,这类变量的查找逻辑在编译期就已绑定,不会在运行时动态检索局部命名空间字典。
- 函数内调用
exec()执行赋值语句时,如果不手动指定命名空间参数,exec()内创建的变量会被写入locals()返回的局部字典——这就是你调用dir()能看到f1/f2/f3存在的原因。 - 但编译阶段生成的局部变量查找逻辑,不会主动读取运行时才被
exec()塞进局部字典的变量。你在代码中直接写f1.predict()、f.append(2)时,解释器在静态扫描阶段没找到这些变量的定义,执行时就会直接抛出NameError,不会去局部字典里动态查找。 - 交互环境逐行执行、函数外(模块全局作用域)执行不报错的原因是:全局作用域的变量查找本身就是动态检索
globals()字典的,exec()在全局作用域创建的变量会直接进入全局字典,后续引用可以被正常查到。
复现逻辑验证
你提供的最小测试用例完全符合上述规则:
def test(): exec("f= []") f.append(2) print(f) if __name__=="__main__": test()
执行该脚本必然抛出NameError: name 'f' is not defined,哪怕在exec语句后加print(locals())能明确看到f存在于局部字典中,直接引用依然会报错。
推荐修复方式
不要使用exec()动态注入局部变量,改用字典显式管理加载的模型,逻辑清晰且无作用域问题:
import pickle def predict(new_data): model_list = fetch_needed_models() # ["f1","f2","f3"] loaded_models = {} for model_name in model_list: with open(f"./{model_name}.pkl", "rb") as file: loaded_models[model_name] = pickle.load(file) # 调用时直接从字典取对应模型 f1_pred = [loaded_models["f1"].predict(x) for x in new_data]
不推荐的变通方案:提前在函数内初始化对应变量、或声明变量为
global/nonlocal骗过编译器静态检查,这类写法可维护性极差,极易引入隐蔽bug。
内容的提问来源于stack exchange,提问作者CutePoison
相关产品推荐
相关产品推荐

