Python多进程报Can't pickle local object错误求解
问题根因
两个报错本质都是Python多进程的序列化机制限制导致的:
multiprocessing创建子进程时,需要通过pickle将target函数、调用参数序列化后传递给子进程,pickle 对函数的序列化要求非常严格:仅支持序列化定义在模块顶层的、可通过模块路径直接导入的函数,嵌套定义的局部函数、lambda匿名函数都无法被正常序列化/反序列化。- 第一个报错
Can't pickle local object 'calc.<locals>.addi'触发原因:你代码里的addi是嵌套在calc函数内部的局部函数,哪怕写了global addi声明,函数对象本身依然是在calc的局部作用域中创建的,不属于模块顶层对象,pickle无法处理这类局部对象。这里的global仅表示函数执行时会把创建出的addi函数对象绑定到全局变量,不会改变函数定义的作用域。 - 第二个报错
Can't get attribute 'addi' on <module '__mp_main__'触发原因:Python 3.8在macOS/Windows上默认使用spawn模式启动子进程,子进程会重新导入主模块,尝试从主模块的顶层作用域查找反序列化需要的addi函数。但addi只有在calc()函数被调用时才会被创建并赋值给全局变量,子进程导入模块时不会执行calc()内部的逻辑,自然找不到对应属性。
注:你贴出的代码1和代码2内容完全一致,加global的调整并没有解决函数嵌套定义的核心问题。
修复方案
将多进程调用的target函数移动到模块顶层定义即可,修正后可运行代码如下:
import multiprocessing as mp import os # 多进程target必须定义在模块顶层 def addi(num1, num2): print(num1 + num2) def calc(num1, num2): m = mp.Process(target=addi, args=(num1, num2)) m.start() print("here is main", os.getpid()) m.join() if __name__ == "__main__": calc(5, 6)
额外注意事项
- 所有需要传递给子进程的自定义对象(包括函数、自定义类实例),都必须定义在模块顶层,不要嵌套在其他函数或类内部。
- 不要依赖
global声明“导出”嵌套函数,该方式无法满足pickle的序列化寻址要求。 - 你当前把多进程启动逻辑放在
if __name__ == "__main__":块内的写法是正确的,spawn模式下必须做这个保护,避免子进程递归启动。
内容的提问来源于stack exchange,提问作者Anushka
相关产品推荐
相关产品推荐

