Python多进程调用对象实例方法遇Pickle错误的原因与解决方法
这个问题我之前在Windows环境下用Python多进程时也踩过坑,核心原因和Windows系统中multiprocessing的spawn启动机制密切相关:
为什么会报错?
和Unix系统的fork机制不同,Windows没有进程fork的能力,所以multiprocessing默认用spawn方式创建子进程:它会启动一个全新的Python解释器,重新导入你的主脚本文件。
如果你的class A是定义在if __name__ == "__main__"代码块内部(或者虽然在外部,但子进程导入时无法正确找到类的定义),那么子进程在导入脚本时,不会执行if __name__ == "__main__"里的代码(因为子进程的__name__不是__main__),导致子进程完全不知道A这个类的存在。当主进程尝试把a.run这个实例方法序列化传递给子进程时,pickle模块找不到对应的类A,就会抛出AttributeError: Can't get attribute 'A' on <module '__main__' (built-in)>这个错误。
而你直接调用a.run()正常,是因为此时在主进程里,类A已经被定义,实例a也存在,不需要序列化传递,自然没问题。
解决办法
这里有几个可靠的解决方案,你可以根据自己的代码结构选择:
1. 将类定义移到if __name__ == "__main__"外部
这是最简单直接的方法,确保类A在全局作用域定义,这样子进程导入脚本时能顺利加载到类的定义:
from multiprocessing import Process # 把类定义放在全局作用域,子进程导入时能找到 class A: def __init__(self): self.a = "A" def run(self): print("Hello World") if __name__ == "__main__": a = A() p = Process(target=a.run) p.start() p.join() # 记得加上join等待子进程完成
2. 把实例方法调用包装成顶层函数
如果你的类定义不方便放在全局作用域,可以把实例的创建和方法调用封装成一个顶层函数,让子进程直接执行这个函数:
from multiprocessing import Process def run_a_task(): # 在子进程内部创建实例并调用方法 a = A() a.run() class A: def __init__(self): self.a = "A" def run(self): print("Hello World") if __name__ == "__main__": p = Process(target=run_a_task) p.start() p.join()
3. 将类放在单独的模块中导入
如果你的项目结构比较复杂,建议把类定义单独放在一个模块文件里,比如创建my_classes.py:
# my_classes.py class A: def __init__(self): self.a = "A" def run(self): print("Hello World")
然后在主脚本中导入这个类:
# 主脚本 from multiprocessing import Process from my_classes import A if __name__ == "__main__": a = A() p = Process(target=a.run) p.start() p.join()
这种方式能彻底避免子进程找不到类定义的问题,也更符合模块化的代码规范。
内容的提问来源于stack exchange,提问作者Gnawavibes




