python - example - ¿Puedo "decapar los objetos locales" si uso una clase derivada?
python pickle save object (4)
La referencia de pickle
indica que el conjunto de objetos que pueden ser encurtidos es bastante limitado. De hecho, tengo una función que devuelve una clase generada dinámicamente, y encontré que no puedo descifrar instancias de esa clase:
>>> import pickle
>>> def f():
... class A: pass
... return A
...
>>> LocalA = f()
>>> la = LocalA()
>>> with open(''testing.pickle'', ''wb'') as f:
... pickle.dump(la, f, pickle.HIGHEST_PROTOCOL)
...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
AttributeError: Can''t pickle local object ''f.<locals>.A''
Tales objetos son demasiado complicados para el pickle
. De acuerdo. Ahora, lo que es mágico es que, si trato de encurtir un objeto similar, pero de una clase derivada, ¡funciona!
>>> class DerivedA(LocalA): pass
...
>>> da = DerivedA()
>>> with open(''testing.pickle'', ''wb'') as f:
... pickle.dump(da, f, pickle.HIGHEST_PROTOCOL)
...
>>>
¿Que esta pasando aqui? Si esto es tan fácil, ¿por qué no utilizar este método alternativo para implementar un método de dump
que permita que los "objetos locales" sean decapados?
Creo que no leíste la referencia que citas cuidadosamente. La referencia también indica claramente que solo los siguientes objetos se pueden recoger:
- funciones definidas en el nivel superior de un módulo (usando def, no> lambda)
- Funciones incorporadas definidas en el nivel superior de un módulo
- Clases que se definen en el nivel superior de un módulo.
Tu ejemplo
>>> def f():
... class A: pass
... return A
no define una clase en el nivel superior de un módulo, define una clase dentro del alcance de f()
. pickle
trabaja en clases globales , no en clases locales. Esto falla automáticamente la prueba pickleable.
DerivedA
es una clase global, así que todo está bien.
En cuanto a por qué solo las clases y funciones de nivel superior (global para usted) no pueden ser decapadas, la referencia también responde a esa pregunta (negrita):
Tenga en cuenta que las funciones (incorporadas y definidas por el usuario) se seleccionan por referencia de nombre "completamente calificado" , no por valor. Esto significa que solo el nombre de la función está decapado, junto con el nombre del módulo en el que se define la función . Ni el código de la función, ni ninguno de sus atributos de función están decapados . Por lo tanto, el módulo de definición debe ser importable en el entorno de despeje, y el módulo debe contener el objeto nombrado, de lo contrario se generará una excepción.
De manera similar, las clases se declinan por referencia nombrada, por lo que se aplican las mismas restricciones en el entorno de descifrado.
Así que ahí lo tienen. pickle
solo serializa los objetos por referencia de nombre, no por las instrucciones en bruto contenidas dentro del objeto. Esto se debe a que pickle''s
trabajo pickle''s
es serializar la jerarquía de objetos , y nada más.
No estoy de acuerdo, usted puede encurtir ambos. Solo necesitas usar un mejor serializador, como el dill
. dill
(por defecto) clases de encurtidos guardando la definición de la clase en lugar de decapar por referencia, para que no falle en su primer caso. Incluso puede usar dill
para obtener el código fuente, si lo desea.
>>> import dill as pickle
>>> def f():
... class A: pass
... return A
...
>>> localA = f()
>>> la = localA()
>>>
>>> _la = pickle.dumps(la)
>>> la_ = pickle.loads(_la)
>>>
>>> class DerivedA(localA): pass
...
>>> da = DerivedA()
>>> _da = pickle.dumps(da)
>>> da_ = pickle.loads(_da)
>>>
>>> print(pickle.source.getsource(la_.__class__))
class A: pass
>>>
Solo se pueden elegir las instancias de clases definidas en el nivel superior del módulo.
Sin embargo, puede escoger instancias de clases definidas localmente si las promueve a un nivel superior.
Debe establecer el atributo de clase __ qualname__ de la clase local. Luego, debe asignar la clase a una variable de nivel superior del mismo nombre.
def define_class(name):
class local_class:
pass
local_class.__qualname__ = name
return local_class
class_A = define_class(''class_A'') # picklable
class_B = define_class(''class_B'') # picklable
class_X = define_class(''class_Y'') # unpicklable, names don''t match
DerivedA
instancias de DerivedA
son seleccionables porque DerivedA
está disponible a través de una variable global que coincide con su nombre completo, que es la forma en que pickle
busca las clases cuando se deshace de la selección.
El problema al tratar de hacer algo como esto con las clases locales es que no hay nada que identifique a qué clase A
corresponde una instancia. Si ejecuta f
dos veces, obtiene dos clases A
y no hay forma de saber cuál debería ser la clase de instancias A
de otra ejecución del programa. Si no ejecutas f
en absoluto, no obtienes clases A
, ¿y qué diablos haces sobre el tipo de instancias no seleccionadas?