Python 3.7.9中importlib导入的matplotlib格式化函数报内置函数未定义错误
问题排查
尝试将通过importlib动态导入的函数作为matplotlib的坐标轴格式化器使用时,函数内部无法访问Python内置函数,运行抛出NameError,即使在格式化器代码内显式写了内置函数导入逻辑,问题也依然存在。
复现代码
formatterCode = ''' from builtins import * def myFormatter(x, y): return str(float(x)) ''' print(formatterCode, file = open('formatter.py', 'w')) import os, importlib spec = importlib.util.spec_from_file_location('module.name', 'formatter.py') formatters = importlib.util.module_from_spec(spec) spec.loader.exec_module(formatters) import matplotlib, dill import matplotlib.pyplot as plt fig, ax = plt.subplots() myFormatter = matplotlib.ticker.FuncFormatter(formatters.myFormatter) ax.yaxis.set_major_formatter(myFormatter) ax.plot([1, 2], [1, 2]) with open('test.tmp', 'wb') as f: dill.dump(fig, f, recurse = False) plt.close('all') with open('test.tmp', 'rb') as f: figure = dill.load(f) figure.savefig("test.png") plt.close('all')
运行环境
Python 3.7.9 (default, Feb 18 2021, 03:10:35) [GCC 7.3.1 20180712 (Red Hat 7.3.1-12)] on linux
报错信息
File "formatter.py", line 4, in myFormatter return str(float(x)) NameError: name 'str' is not defined
问题成因
- 用
importlib从指定路径动态加载模块时,默认不会把模块注册到全局sys.modules索引表中,后续序列化、反序列化全流程根本定位不到模块的原始全局命名空间 - 序列化时传入的*
recurse = False参数*会让dill跳过对函数关联依赖的递归序列化,不会把函数引用的模块全局变量、内置函数绑定关系持久化,只保留函数本身的代码文本引用 - 格式化器代码里写的
from builtins import *,反而把原本Python解释器自动给每个函数绑定的内置函数引用,变成了模块全局作用域里的普通变量。一旦模块引用在序列化时丢失,这些绑定的普通变量就会彻底消失,自然触发找不到str、float这类内置函数的NameError
修复方案
- 动态模块加载完成后,显式将模块注册到
sys.modules,保证序列化反序列化时能准确定位到模块全局命名空间:
import sys # 模块加载完成后追加这行代码 sys.modules[spec.name] = formatters
- 移除dill序列化时的
recurse = False参数,让序列化流程正常保留函数的全局作用域绑定关系,不要跳过依赖递归 - 删除格式化器代码里的
from builtins import *逻辑,Python解释器会自动给每个函数关联对应模块的内置函数表,不需要手动导入内置名
内容的提问来源于stack exchange,提问作者user3876608
相关产品推荐
相关产品推荐

