Guardar un objeto (Persistencia de datos)
He creado un objeto como este:
company1.name = 'banana'
company1.value = 40
Me gustaría guardar este objeto. ¿Cómo puedo hacer eso?
3 answers
Puede usar el módulo pickle
en la biblioteca estándar.
He aquí una aplicación elemental de la misma a 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 podría escribir una utilidad simple como la siguiente que abre un archivo y escribe un solo objeto en él:
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')
Actualización:
Dado que esta es una respuesta tan popular, me gustaría tocar algunos temas de uso ligeramente avanzados.
cPickle
(o _pickle
) vs pickle
Casi siempre es preferible utilizar el cPickle
módulo en 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 it no podría ser más fácil, simplemente cambie la instrucción import
a esto:
import cPickle as pickle
En Python 3, cPickle
fue renombrado _pickle
, pero hacer esto ya no es necesario ya que el módulo pickle
ahora lo hace automáticamente-ver Qué diferencia entre pickle y _pickle en python 3?.
El resumen es que podría usar algo como lo siguiente para asegurarse de que su código siempre use la versión C cuando esté disponible tanto en Python 2 como en 3:
try:
import cPickle as pickle
except ModuleNotFoundError:
import pickle
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 0 del protocolo" es ASCII y por lo tanto "legible por humanos". Versiones > 1 son binarios y el más alto disponible depende de la versión de Python que se esté utilizando. El valor predeterminado también depende de la versión de Python. En Python 2 la versión predeterminada era Protocol version 0
, pero en Python 3.6, es Protocol version 3
. En Python 3.x el módulo tenía un pickle.DEFAULT_PROTOCOL
añadido, pero eso no existe en Python 2.
Afortunadamente hay abreviatura para escribir pickle.HIGHEST_PROTOCOL
en cada llamada (suponiendo que eso es lo que quieres, y generalmente lo haces)-solo usa el número literal -1
.
Así que, en su lugar de la escritura:
pickle.dump(obj, output, pickle.HIGHEST_PROTOCOL)
Puedes escribir:
pickle.dump(obj, output, -1)
De cualquier manera, solo tendría que especificar el protocolo una vez si creó un objeto Pickler
para usar en múltiples operaciones de pickle:
pickler = pickle.Pickler(output, -1)
pickler.dump(obj1)
pickler.dump(obj2)
etc...
Múltiples objetos
Mientras que un archivo de pepinillos puede contener cualquier número de objetos en escabeche, como se muestra en las muestras anteriores, cuando hay un número desconocido de ellos, a menudo es más fácil almacenarlos todos en algún tipo de contenedor de tamaño variable, como 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 lo que hay 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 cargarlas de nuevo más tarde (aunque hacerlo sin esa información es posible, requiere un código ligeramente especializado). Vea las respuestas a la pregunta relacionada ¿Guardar y cargar varios objetos en el archivo pickle? para más detalles sobre diferentes maneras de hacer esto. Personalmente Me gusta la respuesta de @Lutz Prechelt la mejor. 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))
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-21 12:27:19
Creo que es una suposición bastante fuerte asumir que el objeto es un class
. ¿Y si no es un class
? También está la suposición de que el objeto no fue definido en el intérprete. ¿Y si se definiera en el intérprete? Además, ¿qué pasaría si los atributos se agregaran 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 because porque pickle
serializa por referencia a la definición del objeto).
En todos estos casos, pickle
y cPickle
puede fallar terriblemente.
Si está buscando guardar un object
(creado arbitrariamente), donde tiene atributos (ya sea agregados en la definición del objeto, o después) your su mejor opción es usar dill
, que puede serializar casi cualquier cosa en python.
Empezamos 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'
>>>
Oops {pickle
no puedo manejarlo. Probemos dill
. Agregaremos otro tipo de objeto (a lambda
) para una buena medida.
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 lea 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 pickle
falla, y dill
no, es que dill
trata __main__
como un módulo (la mayor parte), y también puede pickle definiciones de clase en lugar de decapado por referencia (como pickle
hace). La razón por la que dill
puede encurtir un lambda
es que le da un nombre then entonces la magia del encurtido puede suceder.
En realidad, hay una manera más fácil de guardar todos estos objetos, especialmente si tienes muchos objetos que has creado. Simplemente volcar toda la sesión de Python, y volver 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 inconveniente importante es que dill
no es parte de la biblioteca estándar de python. Así que si no puedes instalar un paquete python en tu servidor, entonces no puedes usarlo.
Sin embargo, si puede instalar paquetes python en su sistema, puede obtener la última dill
con git+https://github.com/uqfoundation/dill.git@master#egg=dill
. Y puede obtener la última versión lanzada con pip install dill
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-13 00:54:58
Puedes usar anycache para hacer el trabajo por ti. Considera todos los detalles:
- Utiliza dill como motor,
que extiende el módulo python
pickle
para manejarlambda
y todos los características de python. - Almacena diferentes objetos en diferentes archivos y los recarga correctamente.
- Limita el tamaño de la caché
- Permite borrar la caché
- Permite compartir objetos entre varias ejecuciones
- Permite el respeto de los archivos de entrada que influir en el resultado
Asumiendo 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
la primera vez y encurte el resultado a 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, se carga el objeto decapado.
Si el cachedir
se conserva entre ejecuciones de python, el objeto pickled se toma de la ejecución anterior de python.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-11-19 20:27:41