¿Clase de salmuera, más definición?

Este es un problema que sospecho que es común, pero no he encontrado una solución para él. Lo que quiero es bastante simple y aparentemente técnicamente viable: tengo una clase de python simple y quiero almacenarla en un disco, instancia y definición, en un solo archivo. Pickle almacenará los datos, pero no almacenará la definición de clase. Se podría argumentar que la definición de clase ya está almacenada en mi archivo .py, pero no quiero un archivo .py separado; mi objective es tener un archivo único independiente que pueda volver a aparecer en mi espacio de nombres con una sola línea de código.

Así que sí, sé que esto es posible utilizando dos archivos y dos líneas de código, pero lo quiero en un archivo y una línea de código. La razón por la cual es porque a menudo me encuentro en esta situación; Estoy trabajando en un gran conjunto de datos, manipulándolo en Python, y luego tengo que escribir mis datos cortados, cortados en dados y transformados en alguna estructura de directorios preexistente. Lo que no quiero es ensuciar estos directorios de datos con apéndices de clase Python mal llamados para mantener mi código y mis datos asociados, y lo que quiero menos es la molestia de mantener un registro y organizar todas estas pequeñas clases ad hoc definidas sobre la marcha en un guión de forma independiente.

Por lo tanto, la conveniencia no es tanto en la legibilidad del código, sino en la asociación entre el código y los datos, sin esfuerzo y sin esfuerzo. Eso me parece una meta digna, aunque entiendo que no es apropiado en la mayoría de las situaciones.

Entonces la pregunta es: ¿hay un paquete o fragmento de código que haga tal cosa, porque parece que no puedo encontrar ninguno?

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 

Como estabas buscando un forro , puedo hacerlo mejor. No te mostré que puedes enviar la clase y la instancia al mismo tiempo, y tal vez eso es lo que querías.

 >>> import dill >>> class Foo(object): ... def bar(self, x): ... return x+self.x ... x = 1 ... >>> b = Foo() >>> bx = 5 >>> >>> with open('blah.pkl', 'wb') as s: ... dill.dump((Foo, b), s) ... >>> 

Todavía no es una sola línea, sin embargo, funciona.

 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('blah.pkl', 'rb') as s: ... Foo, b = dill.load(s) ... >>> bx 5 >>> Foo.bar(b, 2) 7 

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. Aún así, no parece lo que querías.

Si quisieras obtener todo, podrías simplemente escabullirte toda la sesión. Y en una sola línea (dos contando la import ).

 >>> 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 >>> # the one line >>> 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 >>> # the one line >>> dill.load_session('foo.pkl') >>> bx 11 >>> b.bar(2) >>> bx 15 >>> foo(3) 9 

Por último, si desea que el transporte se “haga” de manera transparente (en lugar de usar un archivo), 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 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

Pickle no puede decodificar el código de Python, así que no creo que esto sea posible con pickle.

 >>> from pickle import * >>> def A(object): ... def __init__(self): ... self.potato = "Hello" ... print "Starting" ... >>> A.__code__ ", line 1> >>> dumps(A.__code__) Traceback (most recent call last): File "", line 1, in  File "/usr/lib/python2.6/pickle.py", line 1366, in dumps Pickler(file, protocol).dump(obj) File "/usr/lib/python2.6/pickle.py", line 224, in dump self.save(obj) File "/usr/lib/python2.6/pickle.py", line 306, in save rv = reduce(self.proto) File "/usr/lib/python2.6/copy_reg.py", line 70, in _reduce_ex raise TypeError, "can't pickle %s objects" % base.__name__ TypeError: can't pickle code objects