2012-06-27 18 views
7

El siguiente programa:¿Por qué no puedo usar operator.itemgetter en un multiprocesamiento.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"]) 

falla con el siguiente error:

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 

¿Por qué obtengo el error (en CPython 2.7 y 3.2 en Linux x64), y por qué desaparecen si descomentar la tercera línea?

Respuesta

6

El problema aquí es que el módulo de multiprocesamiento pasa objetos por copia en los otros procesos (obviamente), y itemgetter objetos no son copiables utilizando cualquiera de los medios obvias:

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. 

El problema no es incluso intentando llamar a f dentro de los otros procesos; está intentando copiarlo en primer lugar. (Si miras los rastros de la pila, que omití arriba, verás mucha más información sobre por qué esto falla.)

Por supuesto, generalmente esto no importa, porque es casi tan fácil y eficiente de construir un nuevo itemgetter sobre la marcha para copiar uno. Y esto es lo que está haciendo su función alternativa "f". (Copiar una función que crea un objeto en el momento no requiere copiar un objeto, por supuesto.)

Puede convertir "f" en una lambda. O escriba una función trivial (llamada o lambda) que haga lo mismo sin usar itemgetter. O escriba un reemplazo de elemento de reemplazo que se pueda copiar (lo que obviamente no sería tan difícil). Pero no puede usar directamente los objetos de ItemGetter tal como lo desea.

+0

Guau, realmente aprendí algo de su respuesta. +1 y gracias. – steveha

+1

Por cierto, en caso de que se lo pregunte, la verdadera razón por la cual itemgetter no se puede copiar (y no se puede comparar, no se puede codificar, no tiene la utilidad obvia, etc.) isn ' Tanto que alguien pensó que sería una mala idea implementar todo eso en operator.c, ya que nadie pensaba que fuera lo suficientemente importante como para que valiera la pena implementarlo. Hay un hilo de python-dev o python-ideas sobre eso en alguna parte. – abarnert

+1

Tenga en cuenta que también falla con una lambda, pero con un 'PicklingError: No se puede escanear : búsqueda de atributos __builtin __. Failed failed. – phihag

Cuestiones relacionadas