You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python多进程报错:AttributeError: 无法获取模块中的calc函数

多进程Pool.map抛出AttributeError的原因及解决方案

问题复现

运行以下代码时出现AttributeError: Can't get attribute 'calc' on <module '__main__' ...>:

import numpy as np, pandas as pd
import multiprocessing, itertools, timeit
from functools import partial

processes = 5 * multiprocessing.cpu_count()
print(f'processes: {processes}')
pool = multiprocessing.Pool(processes=processes)

def calc(x, y):
   return x+y

def calc_all():
   pairs = [[1,1], [2,2], [3,3]]
   results = pool.map(calc, pairs)
   print(results)

if __name__ == '__main__':
   calc_all()

错误栈:

File "/usr/local/lib/python3.12/multiprocessing/process.py", line 314, in _bootstrap
    self.run()
  File "/usr/local/lib/python3.12/multiprocessing/process.py", line 108, in run
    self._target(*self._args, **self._kwargs)
  File "/usr/local/lib/python3.12/multiprocessing/pool.py", line 114, in worker
    task = get()
           ^^^^^
  File "/usr/local/lib/python3.12/multiprocessing/queues.py", line 389, in get
    return _ForkingPickler.loads(res)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: Can't get attribute 'calc' on <module '__main__' from '/workspaces/calc.py'>

问题原因

这是Unix/Linux默认的多进程fork模式下的模块加载顺序问题:

  1. 主进程在全局作用域创建了Pool对象,这部分代码不在if __name__ == '__main__':块内。
  2. 子进程启动时,会重新导入主模块(__main__)来获取任务中用到的函数对象。
  3. 子进程导入主模块时,会先执行全局代码:先创建Pool,然后才定义calc函数。
  4. 当子进程尝试反序列化主进程传来的calc函数时,此时子进程的__main__模块中还没定义calc,所以Pickler找不到这个属性,抛出错误。

另外原代码还有隐藏问题:pool.map(calc, pairs)会把每个[1,1]列表作为单个参数传给calc,但calc需要两个独立参数,后续还会触发参数不匹配错误。

无需拆分模块的解决方案

核心是把Pool的创建逻辑移到if __name__ == '__main__':块内,避免子进程重复执行Pool创建代码,同时保证子进程导入模块时函数已被定义,顺便修正参数传递问题:

import numpy as np, pandas as pd
import multiprocessing, itertools, timeit
from functools import partial

# 先定义所有函数,确保子进程导入时能找到
def calc(x, y):
   return x+y

def calc_all(pool):
   pairs = [[1,1], [2,2], [3,3]]
   # 用starmap解包每个列表的元素作为calc的两个参数
   results = pool.starmap(calc, pairs)
   print(results)

if __name__ == '__main__':
   # 仅主进程创建Pool
   processes = 5 * multiprocessing.cpu_count()
   print(f'processes: {processes}')
   pool = multiprocessing.Pool(processes=processes)
   calc_all(pool)
   # 显式关闭Pool并等待子进程完成
   pool.close()
   pool.join()

为什么拆分模块能解决问题

当把calc放到单独模块时,子进程导入该模块时,模块内的函数是按顺序定义好的,不存在全局代码执行顺序的问题,Pickler可以直接找到calc函数,因此不会报错。

内容的提问来源于stack exchange,提问作者mike01010

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 23:37:50