7

以下程序:

import multiprocessing,operator
f = operator.itemgetter(0)
# def f(*a): return operator.itemgetter(0)(*a)
if __name__ == '__main__':
    multiprocessing.Pool(1).map(f, ["ab"])

失败并出现以下错误:

Process PoolWorker-1:
Traceback (most recent call last):
  File "/usr/lib/python3.2/multiprocessing/process.py", line 267, in _bootstrap
    self.run()
  File "/usr/lib/python3.2/multiprocessing/process.py", line 116, in run
    self._target(*self._args, **self._kwargs)
  File "/usr/lib/python3.2/multiprocessing/pool.py", line 102, in worker
    task = get()
  File "/usr/lib/python3.2/multiprocessing/queues.py", line 382, in get
    return recv()
TypeError: itemgetter expected 1 arguments, got 0

为什么会出现错误(在 Linux x64 上的 cPython 2.7 和 3.2 上),如果我取消注释第三行,为什么它会消失?

4

1 回答 1

6

这里的问题是 multiprocessing 模块通过复制将对象传递到其他进程(显然),并且 itemgetter 对象不能使用任何明显的方式复制:

In [10]: a = operator.itemgetter(0)
Out[10]: copy.copy(a)
TypeError: itemgetter expected 1 arguments, got 0

In [10]: a = operator.itemgetter(0)
Out[10]: copy.deepcopy(a)
TypeError: itemgetter expected 1 arguments, got 0

In [10]: a = operator.itemgetter(0)
Out[10]: pickle.dumps(a)
TypeError: can't pickle itemgetter objects

# etc.

问题甚至不是试图在其他进程中调用 f ;它首先试图复制它。(如果您查看我在上面省略的堆栈跟踪,您会看到更多关于失败原因的信息。)

当然通常这并不重要,因为动态构建一个新的 itemgetter 几乎和复制一个 itemgetter 一样容易和有效。这就是您的替代“f”函数正在做的事情。(当然,复制动态创建 itemgetter 的函数不需要复制 itemgetter。)

您可以将“f”转换为 lambda。或者编写一个简单的函数(命名或 lambda),在不使用 itemgetter 的情况下做同样的事情。或者编写一个可复制的 itemgetter 替换(这显然不会那么难)。但是您不能按您想要的方式直接使用 itemgetter 对象。

于 2012-06-27T23:06:58.977 回答