Guardar y cargar objetos y usar pickle

114

Estoy tratando de guardar y cargar objetos usando el picklemódulo.
Primero declaro mis objetos:

>>> class Fruits:pass
...
>>> banana = Fruits()

>>> banana.color = 'yellow'
>>> banana.value = 30

Después de eso, abro un archivo llamado 'Fruits.obj' (anteriormente creé un nuevo archivo .txt y cambié el nombre de 'Fruits.obj'):

>>> import pickle
>>> filehandler = open(b"Fruits.obj","wb")
>>> pickle.dump(banana,filehandler)

Luego de hacer esto cierro mi sesión y comencé una nueva y pongo la siguiente (tratando de acceder al objeto que se suponía que estaba guardado):

file = open("Fruits.obj",'r')
object_file = pickle.load(file)

Pero tengo este mensaje:

Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\Python31\lib\pickle.py", line 1365, in load
encoding=encoding, errors=errors).load()
ValueError: read() from the underlying stream did notreturn bytes

No sé qué hacer porque no entiendo este mensaje. ¿Alguien sabe cómo puedo cargar mi objeto 'banana'? ¡Gracias!

EDITAR: Como algunos de ustedes han sugerido, puse:

>>> import pickle
>>> file = open("Fruits.obj",'rb')

No hubo problema, pero el siguiente que puse fue:

>>> object_file = pickle.load(file)

Y tengo error:

Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\Python31\lib\pickle.py", line 1365, in load
encoding=encoding, errors=errors).load()
EOFError
Peterstone
fuente

Respuestas:

74

En cuanto a su segundo problema:

 Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
 File "C:\Python31\lib\pickle.py", line
 1365, in load encoding=encoding,
 errors=errors).load() EOFError

Una vez que haya leído el contenido del archivo, el puntero del archivo estará al final del archivo; no habrá más datos para leer. Tienes que rebobinar el archivo para que se vuelva a leer desde el principio:

file.seek(0)

Sin embargo, lo que normalmente desea hacer es utilizar un administrador de contexto para abrir el archivo y leer los datos de él. De esta manera, el archivo se cerrará automáticamente después de que el bloque termine de ejecutarse, lo que también te ayudará a organizar las operaciones de tu archivo en partes significativas.

Finalmente, cPickle es una implementación más rápida del módulo pickle en C. Entonces:

In [1]: import cPickle

In [2]: d = {"a": 1, "b": 2}

In [4]: with open(r"someobject.pickle", "wb") as output_file:
   ...:     cPickle.dump(d, output_file)
   ...:

# pickle_file will be closed at this point, preventing your from accessing it any further

In [5]: with open(r"someobject.pickle", "rb") as input_file:
   ...:     e = cPickle.load(input_file)
   ...:

In [7]: print e
------> print(e)
{'a': 1, 'b': 2}
Jim Brissom
fuente
¿Qué tipo de estructura de datos es esta 'd = {"a": 1, "b": 2}'?
Peterstone
1
@Peterstone: {"a": 1, "b": 2}crea un diccionario con las claves "a"y "b"en él. Esto se denomina expresión de visualización de diccionario en la documentación en línea. Es solo una de las diferentes formas dicten que se puede construir un objeto de tipo , que es uno de los varios tipos de datos estándar integrados disponibles en Python.
Martineau
2
¿Por qué la letra 'r' procede del nombre del archivo? No veo eso en los documentos. Además, dificulta el uso de una variable para el nombre del archivo.
SherylHohman
7
Mirar esta respuesta hoy y darse cuenta de que solo se aplica a Python 2.x. En Python 3.x, se debe usar directamente pickleque se importará cpickleautomáticamente si es posible. docs.python.org/3.1/whatsnew/3.0.html#library-changes
Eskapp
41

Lo siguiente funciona para mí:

class Fruits: pass

banana = Fruits()

banana.color = 'yellow'
banana.value = 30

import pickle

filehandler = open("Fruits.obj","wb")
pickle.dump(banana,filehandler)
filehandler.close()

file = open("Fruits.obj",'rb')
object_file = pickle.load(file)
file.close()

print(object_file.color, object_file.value, sep=', ')
# yellow, 30
martineau
fuente
Esto me funciona, pero lo que persigo es cerrar una sesión, abrir una nueva y cargar lo que guardo en una sesión pasada. Cierro la sesión después de poner la línea "filehandler.close ()" y abro uno nuevo y pongo el resto de su código, luego después de poner "object_file = pickle.load (file)" me sale este error: Traceback ( última llamada más reciente): Archivo "<pyshell # 5>", línea 1, en <módulo> object_file = pickle.load (archivo) Archivo "C: \ Python31 \ lib \ pickle.py", línea 1365, en codificación de carga = codificación, errores = errores) .load () AttributeError: el objeto 'módulo' no tiene atributo 'Fruits'
Peterstone
3
@Peterstone: En la segunda sesión, deberá tener una definición de class Fruitsdefinido para que pickle.load()pueda reconstituir el objeto a partir de los datos que se guardaron en el archivo binario. La mejor práctica para este tipo de cosas es poner la class Fruitsdefinición en un archivo .py separado (convirtiéndolo en un módulo personalizado) y luego importese módulo o elementos de él cuando sea necesario (es decir, ambas sesiones). Por ejemplo, si lo pone en un archivo llamado MyDataDefs.py, podría escribir from MyDataDefs import Fruits. Avíseme si esto no está claro y actualizaré mi respuesta en consecuencia.
martineau
En realidad, PEP 8 recomienda usar todos los caracteres en minúscula para los nombres de los módulos, por lo que el ejemplo al final de mi último comentario debería haber estado en un archivo llamado my_data_defs.pyusing from my_data_defs import Fruits.
martineau
24

También te olvidas de leerlo como binario.

En tu parte de escritura tienes:

open(b"Fruits.obj","wb") # Note the wb part (Write Binary)

En la parte de lectura tienes:

file = open("Fruits.obj",'r') # Note the r part, there should be a b too

Así que reemplázalo con:

file = open("Fruits.obj",'rb')

Y funcionará :)


En cuanto a su segundo error, lo más probable es que no se cierre / sincronice el archivo correctamente.

Prueba este fragmento de código para escribir:

>>> import pickle
>>> filehandler = open(b"Fruits.obj","wb")
>>> pickle.dump(banana,filehandler)
>>> filehandler.close()

Y esto (sin cambios) para leer:

>>> import pickle
>>> file = open("Fruits.obj",'rb')
>>> object_file = pickle.load(file)

Una versión más ordenada sería usar la withdeclaración.

Para la escritura:

>>> import pickle
>>> with open('Fruits.obj', 'wb') as fp:
>>>     pickle.dump(banana, fp)

Para leer:

>>> import pickle
>>> with open('Fruits.obj', 'rb') as fp:
>>>     banana = pickle.load(fp)
Wolph
fuente
1
Utilizo su versión que usa la declaración with y obtengo este mensaje: Traceback (última llamada más reciente): File "<pyshell # 20>", línea 1, en <module> print (banana.color) AttributeError: 'Fruits' el objeto no tiene atributo 'color'
Peterstone
17

Siempre abierto en modo binario, en este caso

file = open("Fruits.obj",'rb')
ismail
fuente
6

No abriste el archivo en modo binario.

open("Fruits.obj",'rb')

Deberia trabajar.

Para su segundo error, lo más probable es que el archivo esté vacío, lo que significa que sin darse cuenta lo vació o utilizó el nombre de archivo incorrecto o algo así.

(Esto es asumiendo que realmente cerró su sesión. Si no es así, es porque no cerró el archivo entre la escritura y la lectura).

Probé tu código y funciona.

Lennart Regebro
fuente
3

Parece que desea guardar sus instancias de clase en todas las sesiones, y usar picklees una forma decente de hacerlo. Sin embargo, hay un paquete llamado kleptoque abstrae el almacenamiento de objetos en una interfaz de diccionario, por lo que puede elegir seleccionar objetos y guardarlos en un archivo (como se muestra a continuación), o seleccionar los objetos y guardarlos en una base de datos, o en lugar de use pickle use json, o muchas otras opciones. Lo bueno de esto kleptoes que al abstraerse en una interfaz común, lo hace fácil para que no tenga que recordar los detalles de bajo nivel sobre cómo guardar mediante decapado en un archivo, o de otra manera.

Tenga en cuenta que funciona para atributos de clase agregados dinámicamente, lo que pickle no puede hacer ...

dude@hilbert>$ python
Python 2.7.6 (default, Nov 12 2013, 13:26:39) 
[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.
>>> from klepto.archives import file_archive 
>>> db = file_archive('fruits.txt')
>>> class Fruits: pass
... 
>>> banana = Fruits()
>>> banana.color = 'yellow'
>>> banana.value = 30
>>> 
>>> db['banana'] = banana 
>>> db.dump()
>>> 

Luego reiniciamos ...

dude@hilbert>$ python
Python 2.7.6 (default, Nov 12 2013, 13:26:39) 
[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.
>>> from klepto.archives import file_archive
>>> db = file_archive('fruits.txt')
>>> db.load()
>>> 
>>> db['banana'].color
'yellow'
>>> 

Klepto funciona en python2 y python3.

Obtenga el código aquí: https://github.com/uqfoundation

Mike McKerns
fuente
1

Puede usar anycache para hacer el trabajo por usted. Suponiendo que tiene una función myfuncque crea la instancia:

from anycache import anycache

class Fruits:pass

@anycache(cachedir='/path/to/your/cache')    
def myfunc()
    banana = Fruits()
    banana.color = 'yellow'
    banana.value = 30
return banana

Anycache llama myfuncpor primera vez y selecciona el resultado en un archivo cachedirutilizando un identificador único (según el nombre de la función y los argumentos) como nombre de archivo. En cualquier ejecución consecutiva, se carga el objeto en escabeche.

Si cachedirse conserva entre ejecuciones de Python, el objeto en escabeche se toma de la ejecución de Python anterior.

Los argumentos de la función también se tienen en cuenta. Una implementación refactorizada funciona de la misma manera:

from anycache import anycache

class Fruits:pass

@anycache(cachedir='/path/to/your/cache')    
def myfunc(color, value)
    fruit = Fruits()
    fruit.color = color
    fruit.value = value
return fruit
c0fec0de
fuente