python - custom - Guardar un objeto(persistencia de datos)
pickle save text (3)
Creé un objeto como este:
company1.name = ''banana''
company1.value = 40
Me gustaría guardar este objeto ¿Cómo puedo hacer eso?
Creo que es una suposición bastante fuerte suponer que el objeto es una class
. ¿Qué pasa si no es una class
? También existe la suposición de que el objeto no estaba definido en el intérprete. ¿Qué pasa si se definió en el intérprete? Además, ¿qué pasa si los atributos se agregaron dinámicamente? Cuando algunos objetos Python tienen atributos agregados a su __dict__
después de la creación, pickle
no respeta la adición de esos atributos (es decir, ''olvida'' que fueron agregados, porque pickle
serializa por referencia a la definición del objeto).
En todos estos casos, pickle
y cPickle
pueden fallar horriblemente.
Si está buscando guardar un object
(creado arbitrariamente), donde tiene atributos (ya sea añadidos en la definición del objeto o después) ... su mejor opción es usar dill
, que puede serializar casi cualquier cosa en python.
Comenzamos con una clase ...
Python 2.7.8 (default, Jul 13 2014, 02:29:54)
[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 pickle
>>> class Company:
... pass
...
>>> company1 = Company()
>>> company1.name = ''banana''
>>> company1.value = 40
>>> with open(''company.pkl'', ''wb'') as f:
... pickle.dump(company1, f, pickle.HIGHEST_PROTOCOL)
...
>>>
Ahora apague y reinicie ...
Python 2.7.8 (default, Jul 13 2014, 02:29:54)
[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 pickle
>>> with open(''company.pkl'', ''rb'') as f:
... company1 = pickle.load(f)
...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 1378, in load
return Unpickler(file).load()
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 858, in load
dispatch[key](self)
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 1090, in load_global
klass = self.find_class(module, name)
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 1126, in find_class
klass = getattr(mod, name)
AttributeError: ''module'' object has no attribute ''Company''
>>>
Vaya ... pickle
no puede manejarlo. Probemos dill
. Lanzaremos otro tipo de objeto (una lambda
) por si acaso.
Python 2.7.8 (default, Jul 13 2014, 02:29:54)
[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
>>> class Company:
... pass
...
>>> company1 = Company()
>>> company1.name = ''banana''
>>> company1.value = 40
>>>
>>> company2 = lambda x:x
>>> company2.name = ''rhubarb''
>>> company2.value = 42
>>>
>>> with open(''company_dill.pkl'', ''wb'') as f:
... dill.dump(company1, f)
... dill.dump(company2, f)
...
>>>
Y ahora lee el archivo.
Python 2.7.8 (default, Jul 13 2014, 02:29:54)
[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(''company_dill.pkl'', ''rb'') as f:
... company1 = dill.load(f)
... company2 = dill.load(f)
...
>>> company1
<__main__.Company instance at 0x107909128>
>>> company1.name
''banana''
>>> company1.value
40
>>> company2.name
''rhubarb''
>>> company2.value
42
>>>
Funciona. La razón por la cual el pickle
falla, y el dill
no, es que el dill
trata __main__
como un módulo (en su mayor parte), y también puede eliminar las definiciones de clase en lugar del decapado por referencia (como pickle
). La razón por la cual el dill
puede encurtir una lambda
es porque le da un nombre ... entonces la magia de decapado puede suceder.
En realidad, hay una manera más fácil de guardar todos estos objetos, especialmente si tiene muchos objetos que ha creado. Simplemente descargue la sesión completa de Python y vuelva a ella más tarde.
Python 2.7.8 (default, Jul 13 2014, 02:29:54)
[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
>>> class Company:
... pass
...
>>> company1 = Company()
>>> company1.name = ''banana''
>>> company1.value = 40
>>>
>>> company2 = lambda x:x
>>> company2.name = ''rhubarb''
>>> company2.value = 42
>>>
>>> dill.dump_session(''dill.pkl'')
>>>
Ahora apague su computadora, vaya a disfrutar de un espresso o lo que sea, y vuelva más tarde ...
Python 2.7.8 (default, Jul 13 2014, 02:29:54)
[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(''dill.pkl'')
>>> company1.name
''banana''
>>> company1.value
40
>>> company2.name
''rhubarb''
>>> company2.value
42
>>> company2
<function <lambda> at 0x1065f2938>
El único gran inconveniente es que el dill
no es parte de la biblioteca estándar de Python. Entonces, si no puede instalar un paquete de Python en su servidor, entonces no puede usarlo.
Sin embargo, si puede instalar paquetes de Python en su sistema, puede obtener el dill
más reciente con git+https://github.com/uqfoundation/dill.git@master#egg=dill
. Y puede obtener la última versión lanzada con pip install dill
.
Puede usar anycache para hacer el trabajo por usted. Considera todos los detalles:
- Utiliza dill como backend, que extiende el módulo de
pickle
para manejarlambda
y todas las bonitas características de python. - Almacena diferentes objetos en diferentes archivos y los vuelve a cargar correctamente.
- Limita el tamaño de la caché
- Permite borrar el caché
- Permite compartir objetos entre múltiples ejecuciones
- Permite el respeto de los archivos de entrada que influyen en el resultado
Suponiendo que tiene una función myfunc
que crea la instancia:
from anycache import anycache
class Company(object):
def __init__(self, name, value):
self.name = name
self.value = value
@anycache(cachedir=''/path/to/your/cache'')
def myfunc(name, value)
return Company(name, value)
Anycache llama a myfunc
por primera vez y guarda el resultado en un archivo en cachedir
usando un identificador único (dependiendo del nombre de la función y sus argumentos) como nombre de archivo. En cualquier ejecución consecutiva, el objeto escabechado se carga. Si el cachedir
se conserva entre ejecuciones python, el objeto escabechado se toma de la ejecución anterior de python.
Para más detalles, consulte la documentación
Puede usar el módulo pickle
en la biblioteca estándar. Aquí hay una aplicación elemental de este para su ejemplo:
import pickle
class Company(object):
def __init__(self, name, value):
self.name = name
self.value = value
with open(''company_data.pkl'', ''wb'') as output:
company1 = Company(''banana'', 40)
pickle.dump(company1, output, pickle.HIGHEST_PROTOCOL)
company2 = Company(''spam'', 42)
pickle.dump(company2, output, pickle.HIGHEST_PROTOCOL)
del company1
del company2
with open(''company_data.pkl'', ''rb'') as input:
company1 = pickle.load(input)
print(company1.name) # -> banana
print(company1.value) # -> 40
company2 = pickle.load(input)
print(company2.name) # -> spam
print(company2.value) # -> 42
También puede escribir una utilidad simple como la siguiente que abre un archivo y le escribe un único objeto:
def save_object(obj, filename):
with open(filename, ''wb'') as output: # Overwrites any existing file.
pickle.dump(obj, output, pickle.HIGHEST_PROTOCOL)
# sample usage
save_object(company1, ''company1.pkl'')
Actualizar:
Dado que esta es una respuesta tan popular, me gustaría tocar algunos temas de uso ligeramente avanzados.
cPickle
vs pickle
Casi siempre es preferible utilizar el módulo cPickle
lugar de pickle
porque el primero está escrito en C y es mucho más rápido. Hay algunas diferencias sutiles entre ellos, pero en la mayoría de las situaciones son equivalentes y la versión C proporcionará un rendimiento muy superior. Cambiar a él no podría ser más fácil, simplemente cambie la declaración de import
a esto:
import cPickle as pickle
En Python 3, cPickle
se renombró _pickle
, pero hacer esto ya no es necesario, ya que el módulo pickle
ahora lo hace de forma automática: ¿qué diferencia hay entre pickle y _pickle en python 3? .
Formatos de flujo de datos (protocolos)
pickle
puede leer y escribir archivos en varios formatos diferentes, específicos de Python, llamados protocolos . La "versión de protocolo 0" es ASCII y, por lo tanto, "legible por humanos". Las versiones> 1 son binarias y la más alta disponible depende de qué versión de Python se esté utilizando. El valor predeterminado también depende de la versión de Python. En Python 2, el valor predeterminado era la versión de protocolo 0
, pero en Python 3.6 es la versión de protocolo 3
. En Python 3.x el módulo tenía un pickle.DEFAULT_PROTOCOL
agregado a él, pero eso no existe en Python 2.
Afortunadamente hay una forma abreviada de escribir pickle.HIGHEST_PROTOCOL
en cada llamada (asumiendo que es lo que quieres, y usualmente lo haces) -solo usa el número literal -1
. Entonces, en lugar de escribir:
pickle.dump(obj, output, pickle.HIGHEST_PROTOCOL)
Puedes escribir:
pickle.dump(obj, output, -1)
De cualquier manera, solo habría especificado el protocolo una vez si creó un objeto Pickler
para usar en operaciones de pickle múltiples:
pickler = pickle.Pickler(output, -1)
pickler.dump(obj1)
pickler.dump(obj2)
etc...
Múltiples objetos
Mientras que un archivo de pickle puede contener cualquier cantidad de objetos encurtidos, como se muestra en las muestras anteriores, cuando hay un número desconocido de ellos, a menudo es más fácil almacenarlos en un contenedor de tamaño variable, como una list
, tuple
o dict
y escribirlos todos en el archivo en una sola llamada:
tech_companies = [
Company(''Apple'', 114.18), Company(''Google'', 908.60), Company(''Microsoft'', 69.18)
]
save_object(tech_companies, ''tech_companies.pkl'')
y restaurar la lista y todo en ella más tarde con:
with open(''tech_companies.pkl'', ''rb'') as input:
tech_companies = pickle.load(input)
La principal ventaja es que no necesita saber cuántas instancias de objetos se guardan para volver a cargarlas más tarde (aunque hacerlo sin esa información es posible, requiere algún código ligeramente especializado). Consulte las respuestas a la pregunta relacionada ¿Cómo guardar y cargar varios objetos en el archivo de pickle? para detalles sobre diferentes maneras de hacer esto. Personalmente, me gusta la answer @Lutz Prechelt de la mejor manera. Aquí está adaptado a los ejemplos aquí:
class Company:
def __init__(self, name, value):
self.name = name
self.value = value
def pickled_items(filename):
""" Unpickle a file of pickled data. """
with open(filename, "rb") as f:
while True:
try:
yield pickle.load(f)
except EOFError:
break
print(''Companies in pickle file:'')
for company in pickled_items(''company_data.pkl''):
print('' name: {}, value: {}''.format(company.name, company.value))