2011-08-24 13 views
27

salamoia di Python (sto parlando standard di Python 2.5/2.6/2.7 qui) non può salamoia serrature, oggetti file eccPerché i generatori non possono essere messi in salamoia?

Anche può non generatori di salamoia e le espressioni lambda (o qualsiasi altro codice anonimo), perché la salamoia davvero memorizza solo i riferimenti ai nomi.

In caso di serrature e le caratteristiche del sistema operativo-dipendente, la ragione per cui non si può Pickle loro è evidente e ha un senso.

Ma perché non riesci a decapitare i generatori?


Nota: solo per chiarezza - Mi interessa il motivo fondamentale (o ipotesi e scelte che è andato in questa decisione di progettazione) perché, non in "perché ti dà un sottaceto errore".

Mi rendo conto che la domanda è un po 'ampia, quindi ecco una regola empirica se la tua risposta è stata: "Se questi presupposti fossero stati sollevati, o il tipo di generatore consentito in qualche modo più limitato, i generatori di decapaggio funzionerebbero di nuovo?"

+1

Quando avrebbe senso mettere sott'acqua un generatore? – NullUserException

+10

@NullUser: Non è troppo difficile da immaginare; Stai iterando attraverso uno e vuoi fermare il tuo programma e poi riprendere da dove ti sei interrotto più tardi. –

+3

... o riprendere allo stesso tempo, ma da un altro programma (= la serializzazione viene anche utilizzata nella trasmissione di rete) – Radim

risposta

39

Ci sono molte informazioni a riguardo disponibili. Per la "parola ufficiale" sul problema, leggi lo (closed) Python bugtracker issue.

Il ragionamento di base, da una delle persone che hanno fatto la decisione, è dettagliate sui this blog:

Dal momento che un generatore è essenzialmente una funzione truccata, avremmo bisogno di salvare la sua bytecode, che è non garantisce di essere retrocompatibile tra le versioni di Python e il suo frame, che contiene lo stato del generatore come le variabili locali, le chiusure e il puntatore dell'istruzione. E quest'ultimo è piuttosto ingombrante da realizzare, poiché richiede fondamentalmente di rendere l'intero interprete selezionabile. Quindi, qualsiasi supporto per i generatori di picking richiederebbe un gran numero di modifiche al core di CPython.

Ora, se un oggetto supportato da salamoia (ad esempio, una maniglia di file, una presa, una connessione al database, ecc) si verifica nelle variabili locali di un generatore, allora generatore potrebbe non essere decapate automaticamente, indipendentemente da qualsiasi supporto salamoia per i generatori che potremmo implementare. In tal caso, sarà comunque necessario fornire i metodi personalizzati __getstate__ e __setstate__. Questo problema rende il supporto di pickling per i generatori piuttosto limitato.

E due suggerito soluzioni alternative sono menzionati:

In ogni caso, se avete bisogno di una tale funzione, quindi cercare in Stackless Python che fa tutto quanto sopra. E poiché l'interprete di Stackless è selezionabile, puoi anche ottenere la migrazione dei processi gratuitamente. Ciò significa che è possibile interrompere un tasklet (il nome per i thread verdi di Stackless), metterlo sottaceto, inviare il pickle a un'altra macchina, deselezionarlo, riprendere l'attività e voilà è appena stata eseguita la migrazione di un processo. Questa è una caratteristica fantastica!

Ma a mio modesto parere, la migliore soluzione a questo problema è quella di riscrivere i generatori come semplici iteratori (cioè uno con un metodo __next__). Gli iteratori sono facili ed efficienti per quanto riguarda lo spazio, perché il loro stato è esplicito. Dovresti comunque gestire gli oggetti che rappresentano uno stato esterno in modo esplicito; non puoi aggirare questo.

+0

Ottima risposta, grazie. Ho trovato "generator_tools", un puro pacchetto Python che afferma di farlo. Non riesco a farlo funzionare, quindi suppongo che tu (e Alexandre) abbia ragione ... – Radim

+0

Questo pacchetto è menzionato su http://metaoptimize.com/blog/2009/12/22/why-cant-you -pickle-generators-in-python-workaround-pattern-for-saving-training-state/che ha anche un altro pattern di soluzione. – agf

+1

Come puoi fornire una risposta e votare "chiudi" allo stesso tempo? Ora sto aspettando con curiosità "il dibattito, gli argomenti e la discussione estesa" :-) – Radim

19

In realtà è possibile, a seconda dell'implementazione. PyPy e Stackless Python entrambi permettono questo (in una certa misura comunque):

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 

In CPython è anche possibile creare un iterator object per simulare un generatore pickable.

+0

-1: Tutto vero, ma risponde a una domanda diversa – Radim

+1

@Radim ha risposto alla mia domanda. –