Saltar al contenido

¿Cómo decapar una función de python con sus dependencias?

Después de buscar en diversos repositorios y páginas de internet al terminar nos encontramos con la solución que te enseñamos pronto.

Solución:

Actualizado en septiembre de 2020: Vea el comentario de @ogrisel a continuación. Los desarrolladores de PiCloud se mudaron a Dropbox poco después de que yo escribiera la versión original de esta respuesta en 2013, aunque mucha gente todavía usa el módulo cloudpickle siete años después. El módulo llegó a Apache Spark, donde se ha seguido manteniendo y mejorando. Estoy actualizando el ejemplo y el texto de fondo a continuación en consecuencia.

nubepickle

El paquete cloudpickle puede seleccionar una función, un método, una clase o incluso una lambda, así como cualquier dependencia. Para probarlo, solo pip install cloudpickle y luego:

import cloudpickle

def foo(x):
    return x*3

def bar(z):
    return foo(z)+1

x = cloudpickle.dumps(bar)
del foo
del bar

import pickle

f = pickle.loads(x)
print(f(3))  # displays "10"

En otras palabras, simplemente llame cloudpickle.dump() o cloudpickle.dumps() de la misma manera que usarías pickle.*luego use el nativo pickle.load() o pickle.loads() al deshielo.

Antecedentes

PiCcloud.com lanzó el cloud paquete python bajo la LGPL, y otros proyectos de código abierto comenzaron a usarlo rápidamente (google para cloudpickle.py para ver algunos). La gente de picloud.com tenía un incentivo para hacer el esfuerzo de hacer que el decapado de código de propósito general funcionara: todo su negocio se basó en eso. La idea era que si tenías cpu_intensive_function() y quería ejecutarlo en la red EC2 de Amazon, acaba de reemplazar:

cpu_intensive_function(some, args) 

con:

cloud.call(cpu_intensive_function, some, args)

Este último utilizado cloudpickle para recoger cualquier código y datos dependientes, enviarlo a EC2, ejecutarlo y devolverle los resultados cuando llamó cloud.result().

Picloud facturaba en incrementos de milisegundos, era increíblemente barato y lo usaba todo el tiempo para simulaciones de Monte Carlo y análisis de series temporales financieras, cuando necesitaba cientos de núcleos de CPU por solo unos segundos cada uno. Años después, todavía no puedo decir suficientes cosas buenas al respecto y ni siquiera trabajé allí.

He intentado básicamente el mismo enfoque para enviar g como f pero f todavía no puede ver g. ¿Cómo obtengo g en el espacio de nombres global para que pueda ser utilizado por f en el proceso de recepción?

Asignarlo al nombre global g. (Veo que estás asignando f para func2 en lugar de a f. Si estás haciendo algo así con gentonces está claro por qué f no puedo encontrar g. Recuerde que la resolución de nombres ocurre en tiempo de ejecución: g no se busca hasta que llamas f.)

Por supuesto, supongo que ya que no mostraste el código que estás usando para hacer esto.

Podría ser mejor crear un diccionario separado para usar en el espacio de nombres global para las funciones que está desmantelando: una caja de arena. De esa manera, todas sus variables globales estarán separadas del módulo en el que está haciendo esto. Entonces, podría hacer algo como esto:

sandbox = 

with open("functions.pickle", "rb") as funcfile:
    while True:
        try:
            code = marshal.load(funcfile)
        except EOFError:
             break
        sandbox[code.co_name] = types.FunctionType(code, sandbox, code.co_name)

En este ejemplo, asumo que ha puesto los objetos de código de todas sus funciones en un archivo, uno tras otro, y cuando los leo, obtengo el nombre del objeto de código y lo uso como base para el nombre del objeto de función y el nombre con el que se almacena en el diccionario sandbox.

Dentro de las funciones no encurtidas, el diccionario sandbox es su globals() y tan adentro f(), g obtiene su valor de sandbox["g"]. Llamar f entonces sería: sandbox["f"]("blah")

Cada módulo tiene sus propios globales, no hay globales universales. Podemos “implantar” funciones restauradas en algún módulo y utilizarlo como un módulo normal.

— salvar —

import marshal
def f(x):
    return x + 1
def g(x):
    return f(x) ** 2
funcfile = open("functions.pickle", "wb")
marshal.dump(f.func_code, funcfile)
marshal.dump(g.func_code, funcfile)
funcfile.close()

— restaurar —

import marshal
import types
open('sandbox.py', 'w').write('')  # create an empty module 'sandbox'
import sandbox
with open("functions.pickle", "rb") as funcfile:
    while True:
        try:
            code = marshal.load(funcfile)
        except EOFError:
             break
        func = types.FunctionType(code, sandbox.__dict__, code.co_name)
        setattr(sandbox, code.co_name, func)   # or sandbox.f = ... if the name is fixed
assert sandbox.g(3) == 16   # f(3) ** 2
# it is possible import them from other modules
from sandbox import g

Editado:
También puede importar algún módulo, por ejemplo, “sys” al espacio de nombres “sandbox” desde el exterior:

sandbox.sys = __import__('sys')

o lo mismo:

exec 'import sys' in sandbox.__dict__
assert 'sys' in sandbox, 'Verify imported into sandbox'

Su código original funcionaría si no lo haces en ipython interactivo sino en un programa de python o python interactivo normal!!!

Ipython usa un espacio de nombres extraño que no es un dictar de cualquier módulo de sys.modules. Python normal o cualquier uso del programa principal sys.modules['__main__'].__dict__ como globales(). Cualquier módulo utiliza that_module.__dict__ lo cual también está bien, solo ipython interactivo es un problema.

Si haces scroll puedes encontrar las anotaciones de otros usuarios, tú todavía puedes insertar el tuyo si te gusta.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *