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 で) エラーが発生するのはなぜですか? また、3 行目のコメントを外すとエラーが消えるのはなぜですか?

4

1 に答える 1

6

ここでの問題は、マルチプロセッシング モジュールが (明らかに) コピーによって他のプロセスにオブジェクトを渡し、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 を構築するのは、コピーするのとほぼ同じくらい簡単で効率的だからです。そして、これが代替の「f」関数が行っていることです。(もちろん、その場で itemgetter を作成する関数をコピーしても、itemgetter をコピーする必要はありません。)

「f」をラムダに変えることができます。または、itemgetter を使用せずに同じことを行う簡単な関数 (名前付きまたはラムダ) を記述します。または、コピー可能な itemgetter の置換を作成します (これは明らかにそれほど難しくありません)。しかし、あなたが望むように itemgetter オブジェクトを直接使用することはできません。

于 2012-06-27T23:06:58.977 に答える