2013-05-18 10 views
19

devo salamoia un array di oggetti come questo:Python, cPickle, funzioni lambda decapaggio

import cPickle as pickle 
from numpy import sin, cos, array 
tmp = lambda x: sin(x)+cos(x) 
test = array([[tmp,tmp],[tmp,tmp]],dtype=object) 
pickle.dump(test, open('test.lambda','w')) 

e dà il seguente errore:

TypeError: can't pickle function objects 

C'è un modo per aggirare questo?

+0

Sembra una cosa strana da fare. Qual è il caso d'uso? – Aya

+0

@Aya lambdify in SymPy rende molto conveniente la creazione di funzioni lambda. E voglio valutarli usando Cython. Puoi [fare riferimento a quest'altra domanda per ulteriori informazioni] (http://stackoverflow.com/questions/16295140/numerical-integration-over-a-matrix-of-functions-sympy-and-scipy) –

+1

Beh, io non So molto di Cython, ma la soluzione di Martijn funzionerà solo se è possibile per Cython importare il file Python in cui è stata definita la funzione 'tmp (x)'. – Aya

risposta

22

Il modulo pickle incorporato non è in grado di serializzare diversi tipi di oggetti python (incluse funzioni lambda, funzioni nidificate e funzioni definite sulla riga di comando).

Il pacchetto picloud include un pickler più robusto, in grado di decapare funzioni lambda.

oggetti
from pickle import dumps 
f = lambda x: x * 5 
dumps(f) # error 
from cloud.serialization.cloudpickle import dumps 
dumps(f) # works 

PiCloud-serializzati possono essere de-serializzati mediante salamoia normale/cPickle load e loads funzioni.

Dill fornisce anche funzionalità simile

>>> import dill   
>>> f = lambda x: x * 5 
>>> dill.dumps(f) 
'\x80\x02cdill.dill\n_create_function\nq\x00(cdill.dill\n_unmarshal\nq\x01Uec\x01\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00C\x00\x00\x00s\x08\x00\x00\x00|\x00\x00d\x01\x00\x14S(\x02\x00\x00\x00Ni\x05\x00\x00\x00(\x00\x00\x00\x00(\x01\x00\x00\x00t\x01\x00\x00\x00x(\x00\x00\x00\x00(\x00\x00\x00\x00s\x07\x00\x00\x00<stdin>t\x08\x00\x00\x00<lambda>\x01\x00\x00\x00s\x00\x00\x00\x00q\x02\x85q\x03Rq\x04c__builtin__\n__main__\nU\x08<lambda>q\x05NN}q\x06tq\x07Rq\x08.' 
+0

grazie! con il pacchetto picloud ha funzionato! The Dill I non ha ancora provato ... Il pickle creato può essere caricato usando i classici pickle o cPickle modules –

+1

C'è un modo per usare questo pickler con la libreria multiprocessing? –

+0

La risposta è: una specie di http://stackoverflow.com/questions/19984152/what-can-multiprocessing-and-dill-do-together –

9

Dovrete usare una funzione di vero e proprio, invece, uno che è importabile (non nidificato all'interno di un'altra funzione):

import cPickle as pickle 
from numpy import sin, cos, array 
def tmp(x): 
    return sin(x)+cos(x) 
test = array([[tmp,tmp],[tmp,tmp]],dtype=object) 
pickle.dump(test, open('test.lambda','w')) 

L'oggetto funzione potrebbe ancora essere prodotto da un'espressione lambda, ma solo se si successivamente invia l'oggetto funzione risultante lo stesso nome:

tmp = lambda x: sin(x)+cos(x) 
tmp.__name__ = 'tmp' 
test = array([[tmp, tmp], [tmp, tmp]], dtype=object) 

perchè pickle memorizza solo il modulo e il nome di un oggetto funzione; nell'esempio sopra, tmp.__module__ e tmp.__name__ ora punta indietro nel punto in cui è possibile trovare nuovamente lo stesso oggetto quando si deseleziona.

+0

Immagino che quel tipo di risposta non possa essere usato sulle funzioni built-in dei moduli basati su C * (anche se il ᴏꜱ e l'architettura rimangono gli stessi) *. – user2284570

+0

@ user2284570: pickle dispone di strutture specifiche per la memorizzazione di riferimenti a strutture C. Tuttavia, per "decollare" una funzione, tutto ciò che viene memorizzato è un insieme di stringhe (modulo e nome all'interno del modulo) che vengono cancellate quando si rimuove nuovamente. –

+0

Quindi vuoi dire che è possibile salvare ma non ripristinare qualcosa di eseguibile? Mi interessa solo il ripristino * (la creazione del dump non deve essere eseguita in python) *. Non mi interessa cosa viene usato * (marshal o cPickle) * finché non vengono utilizzati moduli di terze parti con l'esecuzione di numpy. – user2284570

5

c'è un'altra soluzione: definire voi funziona come stringhe, salamoia/non-salamoia quindi utilizzare eval, es:

import cPickle as pickle 
from numpy import sin, cos, array 
tmp = "lambda x: sin(x)+cos(x)" 
test = array([[tmp,tmp],[tmp,tmp]],dtype=object) 
pickle.dump(test, open('test.lambda','w')) 
mytmp = array([[eval(x) for x in l] for l in pickle.load(open('test.lambda','r'))]) 
print mytmp 
# yields : [[<function <lambda> at 0x00000000033D4DD8> 
#   <function <lambda> at 0x00000000033D4E48>] 
#   [<function <lambda> at 0x00000000033D4EB8> 
#   <function <lambda> at 0x00000000033D4F28>]] 

Questo potrebbe essere più conveniente per altre soluzioni perché la rappresentazione in pickled sarebbe completamente autonoma senza dover utilizzare dipendenze esterne.