2012-06-27 14 views
7

Poniższy program:Dlaczego nie można użyć operatora.itemgetter w wieloprocesorowych.Pool?

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

nie powiedzie się z powodu następującego błędu:

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 

Dlaczego pojawia się błąd (na CPython 2.7 i 3.2 na Linux x64), a dlaczego to zniknąć, jeśli odkomentować trzecią linię?

Odpowiedz

6

tu problemem jest to, że moduł wieloprocesorowe przechodzi obiektów według skopiowane do innych procesów (oczywiście) i itemgetter przedmioty nie są copyable za pomocą dowolnego z oczywistych sposobów:

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. 

Problem jest jeszcze próba wywołania f wewnątrz innych procesów; najpierw próbuje go skopiować. (Jeśli spojrzysz na ślady stosów, które pominąłem powyżej, zobaczysz znacznie więcej informacji na temat przyczyn niepowodzenia.)

Oczywiście zazwyczaj to nie ma znaczenia, ponieważ jest prawie tak łatwe i efektywne w budowie nowy itemgetter w locie, aby go skopiować. I to właśnie robi twoja alternatywna funkcja "f". (Kopiowanie funkcji, która tworzy itemgetter w locie, oczywiście nie wymaga skopiowania itemgettera.)

Możesz zmienić "f" w lambda. Lub napisz funkcję trywialną (o nazwie lub lambda), która robi to samo, bez użycia itemgettera. Lub napisz zastąpienie itemgettera, które można skopiować (co oczywiście nie byłoby wcale takie trudne). Ale nie możesz bezpośrednio używać obiektów Itemgetter jako - jest to sposób, w jaki chcesz.

+0

Wow, naprawdę nauczyłem się czegoś z twojej odpowiedzi. +1 i dziękuję. – steveha

+1

Nawiasem mówiąc, w przypadku, gdy zastanawiasz się, prawdziwy powód, dla którego itemgetter nie może być skopiowany (i nie może być porównywany, nie może być zaszyfrowany, nie ma oczywistego przydatnego repr, itp.) Isn ' t tak bardzo, że ktokolwiek myślał, że byłoby to złym pomysłem zaimplementować to wszystko w operatora.c, ponieważ nikt nie uważał, że jest on wystarczająco ważny, aby być wartym wdrożenia. Jest gdzieś wątek python-dev lub python-ideas. – abarnert

+1

Należy zauważyć, że nie działa również z lambda, ale z dużo bardziej sensownym 'PicklingError: Can not pickle : atrybut lookup __builtin __. Function failed'. – phihag

Powiązane problemy