2011-08-24 9 views
27

salmuera de Python (estoy hablando estándar de Python 2.5/2.6/2.7 aquí) no puede conservar en vinagre cerraduras, objetos de archivo, etc.¿Por qué los generadores no pueden ser en escabeche?

También puede no generadores de encurtidos y expresiones lambda (o cualquier otro código anónimo), porque la salmuera muy solo almacena referencias de nombre.

En caso de cerraduras y funciones dependientes del sistema operativo, el motivo por qué no puede extraerlos es obvio y tiene sentido.

Pero ¿por qué no puedes extraer generadores?


Nota: sólo por la claridad - Estoy interesado en la razón fundamental (o supuestos y opciones que entraron en esa decisión de diseño) por eso, no en "porque le da un pepinillo error".

Me doy cuenta de que la pregunta es un poco amplia, aquí hay una regla general de si su respuesta: "Si estas suposiciones se elevaran, o el tipo de generador permitido de alguna manera más restringido, ¿volverían a funcionar los generadores de decapado?"

+1

¿Cuándo tendría sentido encurtir un generador? – NullUserException

+10

@NullUser: no es demasiado difícil de imaginar; Estás iterando a través de uno y quieres detener tu programa y luego reanudarlo donde lo dejaste más tarde. –

+3

... o reanudar al mismo tiempo, pero desde un programa diferente (= la serialización también se utiliza en la transmisión de red) – Radim

Respuesta

39

Hay mucha información acerca de esto disponible. Para la "palabra oficial" sobre el tema, lea el (closed) Python bugtracker issue.

El razonamiento básico, por una de las personas que tomaron la decisión, es decir detallada sobre this blog:

Puesto que un generador es esencialmente una función trucado, que tendría que guardar su código de bytes, que es no garantiza que sea compatible con versiones anteriores de Python, y su marco, que contiene el estado del generador, como variables locales, cierres y el puntero de instrucción. Y esto último es bastante complicado de lograr, ya que básicamente requiere que todo el intérprete sea decapable. Por lo tanto, cualquier soporte para los generadores de decapado requeriría una gran cantidad de cambios en el núcleo de CPython.

Ahora si un objeto no compatible con pickle (por ejemplo, un identificador de archivo, un socket, una conexión de base de datos, etc.) ocurre en las variables locales de un generador, ese generador no se puede escanear automáticamente, independientemente de cualquier soporte de pickle para generadores que podríamos implementar. Entonces, en ese caso, aún deberá proporcionar los métodos personalizados __getstate__ y __setstate__. Este problema hace que cualquier soporte de decapado para generadores sea bastante limitado.

Y dos sugirió se mencionan soluciones:

De todos modos, si usted necesita para una de estas características, a continuación, busque en Stackless Python, que hace todo lo anterior. Y dado que el intérprete de Stackless es seleccionable, también obtiene migración de proceso de forma gratuita. Esto significa que puede interrumpir un tasklet (el nombre de los hilos verdes de Stackless), saltearlo, enviar el pickle a otra máquina, desapilarlo, reanudar el tasklet y voilà, acaba de migrar un proceso. ¡Esta es una característica genial!

Pero en mi humilde opinión, la mejor solución a este problema es reescribir los generadores como simples iteradores (es decir, uno con un método __next__). Los iteradores son fáciles y eficientes desde el punto de vista del espacio para eliminar porque su estado es explícito. Sin embargo, necesitaría manejar objetos que representen algún estado externo explícitamente; no puedes evitar esto

+0

Excelente respuesta, gracias. Encontré "generator_tools", un paquete de Python puro que dice hacer esto. No puedo hacer que funcione, así que supongo que usted (y Alexandre) tienen razón ... – Radim

+0

Ese paquete se menciona en http://metaoptimize.com/blog/2009/12/22/why-cant-you -pickle-generators-in-python-workaround-pattern-for-saving-training-state/que también tiene otro patrón de solución. – agf

+1

¿Cómo puede dar una respuesta y votar "cerrar" al mismo tiempo? Ahora estoy esperando curiosamente el "debate, los argumentos y la discusión extendida" :-) – Radim

19

De hecho, puede, dependiendo de la implementación. PyPy y Stackless Python ambos permiten esto (en cierta medida de todos modos):

Python 2.7.1 (dcae7aed462b, Aug 17 2011, 09:46:15) 
[PyPy 1.6.0 with GCC 4.0.1] on darwin 
Type "help", "copyright", "credits" or "license" for more information. 
And now for something completely different: ``Not your usual analyses.'' 
>>>> import pickle 
>>>> gen = (x for x in range(100)) 
>>>> next(gen) 
0 
>>>> pickled = pickle.dumps(gen) 
>>>> next(pickle.loads(pickled)) 
1 

En CPython también es posible crear un iterator object para simular un generador pickable.

+0

-1: Todo es cierto, pero responde una pregunta diferente. – Radim

+1

@Radim respondió mi pregunta. –

Cuestiones relacionadas