使用Python multiprocess库时导入包触发NameError问题咨询
NameError: name 'time' is not defined的问题 刚好碰到过类似的情况,我来给你拆解下为什么切换到multiprocess库会报错,以及对应的解决办法。
问题重现
你用标准库multiprocessing写的代码完全正常,但换成支持dill序列化的multiprocess库后,就触发了找不到time模块的错误。
正常运行的multiprocessing代码
from multiprocessing import Pool import time values = list(range(10)) def print_time_and_value(value): print(time.time(), value) if __name__ == '__main__': p = Pool(4) p.map(print_time_and_value, values)
切换到multiprocess后的报错信息
Traceback (most recent call last):
File "test.py", line 13, in
p.map(print_time_and_value, values)
File "C:\Users\User\Anaconda3_64bits\lib\site-packages\multiprocess\pool.py", line 268, in map
return self._map_async(func, iterable, mapstar, chunksize).get()
File "C:\Users\User\Anaconda3_64bits\lib\site-packages\multiprocess\pool.py", line 657, in get
raise self._value
NameError: name 'time' is not defined
为什么会出现这个差异?
multiprocess虽然是multiprocessing的分支,用dill替代了默认的pickle来处理不可序列化对象,但它的子进程启动逻辑和标准库不一样:
- 标准库
multiprocessing在Windows下用spawn方式启动子进程,会重新导入你的主模块,所以全局的import time会被执行,子进程自然能拿到time模块。 - 而
multiprocess的序列化机制是直接把函数对象打包,但不会自动带上函数依赖的全局导入。你的print_time_and_value函数依赖全局的time变量,但子进程里这个变量没被加载,所以就报错了。
可行的解决方案
你已经发现把导入移到函数内部能解决问题,这里再给你两种明确的方案:
方案1:在函数内部导入依赖模块
把import time放到函数里,这样子进程执行函数时会自己导入模块,完全不依赖全局变量:
from multiprocess import Pool values = list(range(10)) def print_time_and_value(value): import time print(time.time(), value) if __name__ == '__main__': p = Pool(4) p.map(print_time_and_value, values)
方案2:用初始化函数给子进程加载依赖
如果不想修改函数内部代码,可以用Pool的initializer参数,在每个子进程启动时统一导入需要的模块:
from multiprocess import Pool import time values = list(range(10)) def init_worker(): # 确保子进程能拿到time模块 global time import time def print_time_and_value(value): print(time.time(), value) if __name__ == '__main__': p = Pool(4, initializer=init_worker) p.map(print_time_and_value, values)
额外提示
multiprocess的这种行为是为了更灵活地处理序列化,但也带来了和标准库的差异——它不会像multiprocessing那样重新跑一遍主模块,而是直接序列化函数的执行环境。所以以后写代码时,要注意函数依赖的全局变量/模块,要么在内部导入,要么通过初始化函数确保子进程能拿到。
内容的提问来源于stack exchange,提问作者Rodrigo Bonadia

