Decapado una definición de clase

¿Hay una manera de escoger una definición de clase?

Lo que me gustaría hacer es elegir la definición (que puede crearse dinámicamente) y luego enviarla a través de una conexión TCP para que se pueda crear una instancia en el otro extremo.

Entiendo que puede haber dependencias, como módulos y variables globales en las que se basa la clase. También me gustaría agruparlas en el proceso de decapado, pero no me preocupa la detección automática de las dependencias porque está bien si la responsabilidad recae en el usuario para especificarlas.

Por desgracia, no directamente. Puede enviar el formulario de cadena de la statement de class , o un formulario de código de bytes, y “rehidratar” con un exec en el extremo receptor.

Si usa dill , le permite tratar __main__ como si fuera un módulo de python (en su mayor parte). Por lo tanto, puede serializar clases definidas interactivamente, y similares. dill también (de manera predeterminada) puede transportar la definición de clase como parte del encurtido.

 >>> class MyTest(object): ... def foo(self, x): ... return self.x * x ... x = 4 ... >>> f = MyTest() >>> import dill >>> >>> with open('test.pkl', 'wb') as s: ... dill.dump(f, s) ... >>> 

Luego cierre el intérprete y envíe el archivo test.pkl través de TCP. En su máquina remota, ahora puede obtener la instancia de clase.

 Python 2.7.9 (default, Dec 11 2014, 01:21:43) [GCC 4.2.1 Compatible Apple Clang 4.1 ((tags/Apple/clang-421.11.66))] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> import dill >>> with open('test.pkl', 'rb') as s: ... f = dill.load(s) ... >>> f <__main__.MyTest object at 0x1069348d0> >>> fx 4 >>> f.foo(2) 8 >>> 

Pero, ¿cómo obtener la definición de clase? Así que esto no es exactamente lo que querías. Lo siguiente es, sin embargo.

 >>> class MyTest2(object): ... def bar(self, x): ... return x*x + self.x ... x = 1 ... >>> import dill >>> with open('test2.pkl', 'wb') as s: ... dill.dump(MyTest2, s) ... >>> 

Luego, después de enviar el archivo … puede obtener la definición de clase.

 Python 2.7.9 (default, Dec 11 2014, 01:21:43) [GCC 4.2.1 Compatible Apple Clang 4.1 ((tags/Apple/clang-421.11.66))] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> import dill >>> with open('test2.pkl', 'rb') as s: ... MyTest2 = dill.load(s) ... >>> print dill.source.getsource(MyTest2) class MyTest2(object): def bar(self, x): return x*x + self.x x = 1 >>> f = MyTest2() >>> fx 1 >>> f.bar(4) 17 

Entonces, dentro de dill , hay dill.source , y eso tiene métodos que pueden detectar dependencias de funciones y clases, y llevarlas junto con el pickle (en su mayor parte).

 >>> def foo(x): ... return x*x ... >>> class Bar(object): ... def zap(self, x): ... return foo(x) * self.x ... x = 3 ... >>> print dill.source.importable(Bar.zap, source=True) def foo(x): return x*x def zap(self, x): return foo(x) * self.x 

Así que eso no es “perfecto” (o quizás no lo que se espera) … pero sí serializa el código para un método construido dinámicamente y sus dependencias. Simplemente no obtiene el rest de la clase, pero el rest de la clase no es necesario en este caso.

Si quisieras obtener todo, podrías simplemente escabullirte toda la sesión.

 >>> import dill >>> def foo(x): ... return x*x ... >>> class Blah(object): ... def bar(self, x): ... self.x = (lambda x:foo(x)+self.x)(x) ... x = 2 ... >>> b = Blah() >>> bx 2 >>> b.bar(3) >>> bx 11 >>> dill.dump_session('foo.pkl') >>> 

Luego en la máquina remota …

 Python 2.7.9 (default, Dec 11 2014, 01:21:43) [GCC 4.2.1 Compatible Apple Clang 4.1 ((tags/Apple/clang-421.11.66))] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> import dill >>> dill.load_session('foo.pkl') >>> bx 11 >>> b.bar(2) >>> bx 15 >>> foo(3) 9 

Por último, si desea que el transporte se realice “de manera transparente”, puede usar pathos.pp o ppft , que brindan la capacidad de enviar objetos a un segundo servidor de Python (en una máquina remota) o al proceso de python. Usan dill debajo del capó y simplemente pasan el código a través del cable.

 >>> class More(object): ... def squared(self, x): ... return x*x ... >>> import pathos >>> >>> p = pathos.pp.ParallelPythonPool(servers=('localhost,1234',)) >>> >>> m = More() >>> p.map(m.squared, range(5)) [0, 1, 4, 9, 16] 

El argumento de los servers es opcional, y aquí solo se conecta a la máquina local en el puerto 1234 … pero si usa el nombre y el puerto de la máquina remota (o también), disparará a la máquina remota – “sin esfuerzo” .

Obtenga dill , pathos y ppft aquí: https://github.com/uqfoundation

La documentación hace un buen trabajo al explicar qué se puede y qué no se puede decapar, y por qué.

http://docs.python.org/library/pickle.html#what-can-be-pickled-and-unpickled

Básicamente, si la clase o el módulo es importable por su nombre cuando no se selecciona, debería funcionar, a menos que planee cambiar la definición de su clase entre ahora y cuando la elimine. En la definición de clase a continuación, solo el nombre de clase “Test” y el nombre del método “mymethod” serán decapados. Si selecciona la definición de clase, luego cambia la definición para que attr sea un valor diferente, y mymethod hace algo completamente diferente, el pickle recogerá la nueva definición.

 class Test(object): attr = 5 def mymethod(self, arg): return arg