Salvataggio e caricamento di oggetti e utilizzo di pickle


114

Sto cercando di salvare e caricare oggetti usando il picklemodulo.
Per prima cosa dichiaro i miei oggetti:

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

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

Successivamente apro un file chiamato "Fruits.obj" (in precedenza ho creato un nuovo file .txt e ho rinominato "Fruits.obj"):

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

Fatto ciò chiudo la mia sessione e ne inizio una nuova e metto la successiva (cercando di accedere all'oggetto che doveva essere salvato):

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

Ma ho questo messaggio:

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

Non so cosa fare perché non capisco questo messaggio. Qualcuno sa come posso caricare il mio oggetto "banana"? Grazie!

EDIT: Come alcuni di voi hanno suggerito, ho messo:

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

Non ci sono stati problemi, ma il successivo che ho inserito è stato:

>>> object_file = pickle.load(file)

E ho un errore:

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


Risposte:


74

Per quanto riguarda il tuo secondo 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

Dopo aver letto il contenuto del file, il puntatore del file si troverà alla fine del file - non ci saranno ulteriori dati da leggere. Devi riavvolgere il file in modo che venga letto di nuovo dall'inizio:

file.seek(0)

Quello che di solito vuoi fare, però, è usare un gestore di contesto per aprire il file e leggere i dati da esso. In questo modo, il file verrà chiuso automaticamente al termine dell'esecuzione del blocco, il che ti aiuterà anche a organizzare le operazioni sui file in blocchi significativi.

Infine, cPickle è un'implementazione più veloce del modulo pickle in C. Quindi:

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}

Che tipo di struttura dati è questa "d = {" a ": 1," b ": 2}"?
Peterstone

1
@Peterstone: {"a": 1, "b": 2}crea un dizionario con le chiavi "a"e "b"al suo interno. Questa è chiamata espressione di visualizzazione del dizionario nella documentazione in linea. È solo uno dei diversi modi in cui un oggetto di tipo dict, che è uno dei numerosi tipi di dati incorporati standard disponibili in Python, può essere costruito.
martineau

2
Perché la lettera "r" procede al nome del file? Non lo vedo nei documenti. Inoltre, rende difficile utilizzare una variabile per il nome del file.
SherylHohman

7
Guardando questa risposta oggi e notando che si applica solo a Python 2.x. In Python 3.x, si dovrebbe usare direttamente pickleche verrà importato cpickleautomaticamente se possibile. docs.python.org/3.1/whatsnew/3.0.html#library-changes
Eskapp

41

Il seguente funziona per me:

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

Questo mi funziona, ma quello che cerco è chiudere una sessione, aprirne una nuova e caricare ciò che ho salvato in una sessione precedente. Chiudo la sessione dopo aver inserito la riga "filehandler.close ()" e ne apro uno nuovo e metto il resto del codice, poi dopo aver messo "object_file = pickle.load (file)" ottengo questo errore: Traceback ( chiamata più recente last): File "<pyshell # 5>", riga 1, in <module> object_file = pickle.load (file) File "C: \ Python31 \ lib \ pickle.py", riga 1365, in load encoding = codifica, errori = errori) .load () AttributeError: l'oggetto 'module' non ha l'attributo 'Fruits'
Peterstone

3
@Peterstone: Nella seconda sessione dovrai avere una definizione di class Fruitsdefinito in modo che pickle.load()possa ricostituire l'oggetto dai dati che sono stati salvati nel file binario. La migliore pratica per questo genere di cose è mettere la class Fruitsdefinizione in un file .py separato (rendendolo un modulo personalizzato) e poi importquel modulo o gli elementi da esso quando necessario (cioè entrambe le sessioni). Ad esempio, se lo metti in un file denominato, MyDataDefs.pypotresti scrivere from MyDataDefs import Fruits. Fammi sapere se questo non è chiaro e aggiornerò la mia risposta di conseguenza.
martineau

In realtà PEP 8 consiglia di utilizzare tutti i caratteri minuscoli per i nomi dei moduli, quindi l'esempio alla fine del mio ultimo commento avrebbe dovuto essere in un file denominato my_data_defs.pyusing from my_data_defs import Fruits.
martineau

24

Ti stai dimenticando di leggerlo anche come binario.

Nella tua parte di scrittura hai:

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

Nella parte di lettura hai:

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

Quindi sostituiscilo con:

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

E funzionerà :)


Per quanto riguarda il secondo errore, è molto probabile che non si chiuda / sincronizzi correttamente il file.

Prova questo bit di codice per scrivere:

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

E questo (invariato) da leggere:

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

Una versione più ordinata userebbe la withdichiarazione.

Per scrivere:

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

Per leggere:

>>> import pickle
>>> with open('Fruits.obj', 'rb') as fp:
>>>     banana = pickle.load(fp)

1
Uso la tua versione che utilizza l'istruzione with e ottengo questo messaggio: Traceback (ultima chiamata più recente): File "<pyshell # 20>", riga 1, in <module> print (banana.color) AttributeError: 'Fruits' l'oggetto non ha attributo "colore"
Peterstone

17

Apri sempre in modalità binaria, in questo caso

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

6

Non hai aperto il file in modalità binaria.

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

Dovrebbe funzionare.

Per il tuo secondo errore, il file è molto probabilmente vuoto, il che significa che l'hai svuotato inavvertitamente o utilizzato il nome del file sbagliato o qualcosa del genere.

(Questo presuppone che tu abbia davvero chiuso la sessione. In caso contrario, è perché non hai chiuso il file tra la scrittura e la lettura).

Ho testato il tuo codice e funziona.


3

Sembra che tu voglia salvare le istanze della tua classe attraverso le sessioni e l'utilizzo pickleè un modo decente per farlo. Tuttavia, esiste un pacchetto chiamato kleptoche astrae il salvataggio di oggetti su un'interfaccia di dizionario, quindi puoi scegliere di mettere in risalto gli oggetti e salvarli in un file (come mostrato di seguito), o mettere in risalto gli oggetti e salvarli in un database, o invece di usa pickle usa json o molte altre opzioni. La cosa bella kleptoè che astrarre a un'interfaccia comune, lo rende facile in modo da non dover ricordare i dettagli di basso livello su come salvare tramite decapaggio in un file o altro.

Nota che funziona per attributi di classe aggiunti dinamicamente, cosa che pickle non può fare ...

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()
>>> 

Quindi ricominciamo ...

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 funziona su python2 e python3.

Ottieni il codice qui: https://github.com/uqfoundation


1

Puoi usare anycache per fare il lavoro per te. Supponendo che tu abbia una funzione myfuncche crea l'istanza:

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 chiama myfuncla prima volta e sottrae il risultato a un file cachedirutilizzando un identificatore univoco (a seconda del nome della funzione e degli argomenti) come nome del file. In ogni esecuzione consecutiva, l'oggetto decapato viene caricato.

Se cachedirviene conservato tra le esecuzioni di python, l'oggetto pickled viene preso dalla precedente esecuzione di python.

Vengono presi in considerazione anche gli argomenti della funzione. Un'implementazione refactoring funziona allo stesso modo:

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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.