2009-08-10 8 views
79

Sto provando a trasferire una funzione attraverso una connessione di rete (usando asyncore). C'è un modo semplice per serializzare una funzione python (una che, almeno in questo caso, non avrà effetti collaterali) per il trasferimento come questo?C'è un modo semplice per decapitare una funzione python (o per serializzare in altro modo il suo codice)?

Vorrei idealmente avere una coppia di funzioni simili a questi:

def transmit(func): 
    obj = pickle.dumps(func) 
    [send obj across the network] 

def receive(): 
    [receive obj from the network] 
    func = pickle.loads(s) 
    func() 
+3

Avete hai provato il tuo codice? – monkut

risposta

105

È possibile serializzare la funzione bytecode e quindi ricostruirla sul chiamante. Il modulo marshal può essere utilizzato per serializzare gli oggetti codice, che possono essere riassemblati in una funzione. Per esempio:

import marshal 
def foo(x): return x*x 
code_string = marshal.dumps(foo.func_code) 

Poi nel processo remoto (dopo il trasferimento code_string):

import marshal, types 

code = marshal.loads(code_string) 
func = types.FunctionType(code, globals(), "some_func_name") 

func(10) # gives 100 

Alcune avvertenze:

  • formato marescialla (qualsiasi bytecode pitone per questo) non possono essere compatibile tra le principali versioni di Python.

  • Funzionerà solo per l'implementazione cpython.

  • Se la funzione fa riferimento a globali (inclusi moduli importati, altre funzioni, ecc.) Che è necessario raccogliere, è necessario serializzare anche questi o ricrearli sul lato remoto. Il mio esempio fornisce solo lo spazio dei nomi globale del processo remoto.

  • Probabilmente avrete bisogno di fare un po 'di più per supportare casi più complessi, come le chiusure o le funzioni del generatore.

+1

In Python 2.5, il "nuovo" modulo è deprecato. 'new.function' dovrebbe essere sostituito da 'types.FunctionType', dopo un "tipo di importazione", credo. – EOL

+0

@EOL: buon punto: ho aggiornato il codice per utilizzare il modulo tipi. – Brian

+1

Grazie. Questo e 'esattamente quello che stavo cercando. Basato su alcuni test superficiali, funziona come è per i generatori. –

10

Il modo più semplice è probabilmente inspect.getsource(object) (vedi la inspect module), che restituisce una stringa con il codice sorgente di una funzione o di un metodo.

+0

Questo sembra buono, tranne che il nome della funzione è esplicitamente definito nel codice, che è leggermente problematico. Potrei spogliare la prima riga del codice, ma questo è irrisolvibile facendo qualcosa come "def \/n func():". Potrei decapitare il nome della funzione con la funzione stessa, ma non avrei garanzie che il nome non colliderebbe, o dovrei mettere la funzione in un wrapper, che non è ancora la soluzione più pulita, ma potrebbe essere necessario. –

+0

Si noti che il modulo inspect in realtà sta solo chiedendo la funzione in cui è stato definito e quindi sta leggendo in quelle righe dal file del codice sorgente - difficilmente sofisticato. –

+1

Puoi trovare il nome della funzione usando il suo attributo .__ nome__. Potresti fare una regex replace su^def \ s * {name} \ s * (e dargli il nome che preferisci. Non è infallibile, ma funzionerà per la maggior parte delle cose. –

13

Pyro è in grado di do this for you.

+0

Avrei bisogno di rimanere con la libreria standard per questo particolare progetto: –

+15

Ma questo non significa che non è possibile * guardare * il codice di Pyro per vedere come è fatto :) –

+0

Link interrotto - ecco la documentazione Pyro - http: //packages.python. org/Pyro4/ –

5

Tutto dipende dal fatto che si genera la funzione in fase di esecuzione o no:

Se lo fai - inspect.getsource(object) non funzionerà per le funzioni generati in modo dinamico, come si arriva di origine di oggetto da .py di file, in modo che solo le funzioni definito prima che l'esecuzione possa essere recuperata come sorgente.

E se le tue funzioni sono inserite in file in ogni caso, perché non dare al ricevitore l'accesso ad esse e solo passare attorno ai nomi dei moduli e delle funzioni.

L'unica soluzione per le funzioni create dinamicamente che posso pensare è costruire la funzione come stringa prima della trasmissione, trasmettere la sorgente e quindi eval() sul lato ricevitore.

Edit: la soluzione marshal sembra anche abbastanza intelligente, non sapeva è possibile serializzare qualcosa di diverso thatn built-in

1

Le funzioni di base utilizzate per questo modulo copre la tua ricerca, più si ottiene la migliore compressione sopra il filo; vedere il codice sorgente istruttivo:

y_serial.py module :: warehouse Oggetti Python con SQLite

"Serializzazione + persistenza :: in poche righe di codice, comprime e annota oggetti Python in SQLite, quindi li recupera in ordine cronologico tramite parole chiave senza SQL. modulo per un database per memorizzare i dati senza schema. "

http://yserial.sourceforge.net

25

Partenza Dill, che si estende biblioteca salamoia di Python per sostenere una maggiore varietà di tipi, comprese le funzioni di:

>>> import dill as pickle 
>>> def f(x): return x + 1 
... 
>>> g = pickle.dumps(f) 
>>> f(1) 
2 
>>> pickle.loads(g)(1) 
2 

Supporta anche i riferimenti agli oggetti in chiusura della funzione:

>>> def plusTwo(x): return f(f(x)) 
... 
>>> pickle.loads(pickle.dumps(plusTwo))(1) 
3 
+1

aneto fa anche un buon lavoro di ottenere il codice sorgente da funzioni e lambda e salvandoli su disco, se preferisci il decapaggio di oggetti. –

Problemi correlati