2009-08-10 13 views
79

Estoy tratando de transferir una función a través de una conexión de red (usando asyncore). ¿Existe una manera fácil de serializar una función de Python (una que, al menos en este caso, no tendrá efectos secundarios) para una transferencia como esta?¿Hay una manera fácil de extraer una función de python (o serializar de otra manera su código)?

que lo ideal sería tener un par de funciones similares a estos:

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

usted tiene probado tu código? – monkut

Respuesta

105

Puede serializar la función bytecode y luego reconstruirla en la persona que llama. El módulo marshal se puede utilizar para serializar objetos de código, que luego se pueden volver a ensamblar en una función. es decir:

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

Luego, en el proceso a distancia (después de transferir code_string):

import marshal, types 

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

func(10) # gives 100 

algunas advertencias:

  • formato de Marshal (cualquier bytecode pitón para el caso) no puede ser compatable entre las principales versiones de Python.

  • Solo funcionará para la implementación de cpython.

  • Si la función hace referencia a los valores globales (incluidos los módulos importados, otras funciones, etc.) que necesita recoger, necesitará serializarlos también, o recrearlos en el lado remoto. Mi ejemplo simplemente le da el espacio de nombre global del proceso remoto.

  • Probablemente necesite hacer un poco más para soportar casos más complejos, como cierres o funciones del generador.

+1

En Python 2.5, el módulo "nuevo" está en desuso. 'new.function' debería reemplazarse por 'types.FunctionType', después de un "tipo de importación", creo. – EOL

+0

@EOL: Buen punto: en su lugar, he actualizado el código para usar el módulo de tipos. – Brian

+1

Gracias. Esto es exactamente lo que estaba buscando. Basado en algunas pruebas superficiales, funciona igual que para generadores. –

10

La forma más sencilla es probablemente inspect.getsource(object) (ver el inspect module) que devuelve una cadena con el código fuente de una función o una método.

+0

Esto se ve bien, excepto que el nombre de la función se define explícitamente en el código, lo cual es un poco problemático. Podría quitar la primera línea del código, pero eso es rompible haciendo algo como 'def \/n func():'. Podría incluir el nombre de la función con la función en sí, pero no tendría garantías de que el nombre no colisionaría, o tendría que poner la función en un contenedor, que todavía no es la solución más limpia, pero podría tener que hacer. –

+0

Tenga en cuenta que el módulo de inspección en realidad solo está preguntando a la función donde se definió, y luego leyendo en esas líneas desde el archivo de código fuente, apenas sofisticado. –

+1

Puede encontrar el nombre de la función usando su atributo .__ name__. Puedes hacer una regex replace en^def \ s * {name} \ s * (y darle el nombre que quieras. No es infalible, pero funcionará para la mayoría de las cosas. –

13

Pyro es capaz de do this for you.

+0

Tendría que seguir con la biblioteca estándar para este proyecto en particular. –

+15

Pero eso no significa que no puedas * ver * el código de Pyro para ver cómo se hace :) –

+0

Enlace roto: aquí está la Documentación de Pyro - http: //packages.python. org/Pyro4/ –

5

Todo depende de si se genera la función en tiempo de ejecución o no:

Si lo hace - inspect.getsource(object) no funcionará para las funciones generadas dinámicamente como se pone la fuente del objeto a partir de .py archivo, por lo que sólo funciona definido antes de la ejecución se puede recuperar como fuente.

Y si sus funciones se colocan de todos modos en archivos, ¿por qué no darles acceso al receptor y solo pasar nombres de módulos y funciones?

La única solución para las funciones creadas dinámicamente que puedo pensar es construir la función como una cadena antes de la transmisión, transmitir la fuente, y luego eval() en el lado del receptor.

Editar: la solución marshal se ve también muy inteligente, no sabía puede serializar algo thatn empotrados

1

Las funciones básicas utilizadas para este módulo cubre la consulta, además de obtener la mejor compresión sobre el cable; vea el código fuente instructivo:

y_serial.Módulo py :: almacén Objetos Python con SQLite

"Serialización + persistencia :: en unas pocas líneas de código, comprime y anota objetos de Python en SQLite, luego los recupera cronológicamente por palabras clave sin ningún SQL. El más útil es" estándar " módulo para una base de datos para almacenar datos sin esquema ".

http://yserial.sourceforge.net

25

Salida Dill, que se extiende biblioteca de salmuera de Python para apoyar una mayor variedad de tipos, incluyendo funciones:

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

También es compatible con las referencias a objetos en el cierre de la función:

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

eneldo también hace un buen trabajo al obtener el código fuente de las funciones y lambdas y guardarlas en el disco, si lo prefiere sobre el decapado de objetos. –

Cuestiones relacionadas