Ho creato un oggetto come questo:
company1.name = 'banana'
company1.value = 40
Vorrei salvare questo oggetto. Come lo posso fare?
protocol=pickle.HIGHEST_PROTOCOL
. La mia risposta offre anche alternative al sottaceto.
Ho creato un oggetto come questo:
company1.name = 'banana'
company1.value = 40
Vorrei salvare questo oggetto. Come lo posso fare?
protocol=pickle.HIGHEST_PROTOCOL
. La mia risposta offre anche alternative al sottaceto.
Risposte:
È possibile utilizzare il pickle
modulo nella libreria standard. Ecco una sua elementare applicazione al tuo esempio:
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
Puoi anche definire la tua semplice utility come la seguente che apre un file e scrive un singolo oggetto su di esso:
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')
Dal momento che questa è una risposta così popolare, vorrei toccare alcuni argomenti di utilizzo leggermente avanzati.
cPickle
(o _pickle
) vspickle
È quasi sempre preferibile utilizzare effettivamente il cPickle
modulo piuttosto che pickle
perché il primo è scritto in C ed è molto più veloce. Ci sono alcune sottili differenze tra loro, ma nella maggior parte delle situazioni sono equivalenti e la versione C fornirà prestazioni notevolmente superiori. Passare ad esso non potrebbe essere più semplice, basta cambiare l' import
istruzione in questo:
import cPickle as pickle
In Python 3, è cPickle
stato rinominato _pickle
, ma farlo non è più necessario poiché il pickle
modulo ora lo fa automaticamente: vedi Che differenza tra pickle e _pickle in python 3? .
Il riassunto è che potresti usare qualcosa di simile al seguente per assicurarti che il tuo codice userà sempre la versione C quando è disponibile in Python 2 e 3:
try:
import cPickle as pickle
except ModuleNotFoundError:
import pickle
pickle
può leggere e scrivere file in diversi formati specifici di Python, chiamati protocolli come descritto nella documentazione , "Protocollo versione 0" è ASCII e quindi "leggibile dall'uomo". Le versioni> 0 sono binarie e la più alta disponibile dipende dalla versione di Python utilizzata. L'impostazione predefinita dipende anche dalla versione di Python. In Python 2 l'impostazione predefinita era Versione protocollo 0
, ma in Python 3.8.1 è la versione Protocollo 4
. In Python 3.x al modulo è stato pickle.DEFAULT_PROTOCOL
aggiunto un modulo , ma ciò non esiste in Python 2.
Fortunatamente c'è una scorciatoia per scrivere pickle.HIGHEST_PROTOCOL
in ogni chiamata (supponendo che sia quello che vuoi, e di solito lo fai), basta usare il numero letterale -1
- simile a fare riferimento all'ultimo elemento di una sequenza tramite un indice negativo. Quindi, invece di scrivere:
pickle.dump(obj, output, pickle.HIGHEST_PROTOCOL)
Puoi semplicemente scrivere:
pickle.dump(obj, output, -1)
In entrambi i casi, avresti specificato il protocollo una sola volta se avessi creato un Pickler
oggetto da utilizzare in più operazioni di pickle:
pickler = pickle.Pickler(output, -1)
pickler.dump(obj1)
pickler.dump(obj2)
etc...
Nota : se ti trovi in un ambiente con versioni diverse di Python, probabilmente vorrai utilizzare esplicitamente (ad esempio un hardcode) un numero di protocollo specifico che tutti possono leggere (le versioni successive possono generalmente leggere i file prodotti da quelli precedenti) .
Mentre un file di salamoia può contenere qualsiasi numero di oggetti in salamoia, come mostrato nei campioni di cui sopra, quando c'è un numero imprecisato di loro, è spesso più facile per memorizzare tutti in una sorta di contenitore variabile dimensioni, come una list
, tuple
o dict
e scrivere tutti al file in una sola chiamata:
tech_companies = [
Company('Apple', 114.18), Company('Google', 908.60), Company('Microsoft', 69.18)
]
save_object(tech_companies, 'tech_companies.pkl')
e ripristina l'elenco e tutto il resto in seguito con:
with open('tech_companies.pkl', 'rb') as input:
tech_companies = pickle.load(input)
Il vantaggio principale è che non è necessario sapere quante istanze di oggetto vengono salvate per poterle ricaricare in un secondo momento (anche se è possibile farlo senza tali informazioni , richiede un codice leggermente specializzato). Vedi le risposte alla domanda correlata Salvare e caricare più oggetti nel file pickle? per dettagli su diversi modi per farlo. Personalmente mi piace la risposta di @Lutz Prechelt al meglio. Ecco adattato agli esempi qui:
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))
company1
e company2
. Perché non elimini Company
e mostri anche cosa succede?
Penso che sia un presupposto piuttosto forte supporre che l'oggetto sia un class
. E se non fosse un class
? Si presume anche che l'oggetto non sia stato definito nell'interprete. E se fosse stato definito nell'interprete? Inoltre, cosa succede se gli attributi vengono aggiunti in modo dinamico? Quando alcuni oggetti Python hanno degli attributi aggiunti alla loro __dict__
creazione successiva, pickle
non rispetta l'aggiunta di quegli attributi (cioè "dimentica" che sono stati aggiunti - perché pickle
serializza facendo riferimento alla definizione dell'oggetto).
In tutti questi casi, pickle
e cPickle
può fallirti in modo orribile.
Se stai cercando di salvare un object
(creato arbitrariamente), dove hai degli attributi (o aggiunti nella definizione dell'oggetto, o in seguito) ... la tua scommessa migliore è usare dill
, che può serializzare quasi tutto in Python.
Iniziamo con una lezione ...
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)
...
>>>
Ora spegni e riavvia ...
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
non ce la faccio. Prova di Let dill
. Inseriremo un altro tipo di oggetto (a lambda
) per buona misura.
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)
...
>>>
E ora leggi il file.
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
>>>
Funziona. La ragione pickle
fallisce e dill
non lo fa, è che dill
tratta __main__
come un modulo (per la maggior parte), e può anche decapare le definizioni delle classi invece di decapitarle per riferimento (come pickle
fa). La ragione per cui si dill
può mettere sottaceto a lambda
è che gli dà un nome ... quindi può succedere la magia di decapaggio.
In realtà, c'è un modo più semplice per salvare tutti questi oggetti, specialmente se hai molti oggetti che hai creato. Scarica semplicemente l'intera sessione di Python e torna più tardi.
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')
>>>
Ora spegni il computer, vai a goderti un espresso o altro e torna più tardi ...
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>
L'unico svantaggio principale è che dill
non fa parte della libreria standard di Python. Quindi se non puoi installare un pacchetto Python sul tuo server, non puoi usarlo.
Tuttavia, se sei in grado di installare pacchetti Python sul tuo sistema, puoi ottenere le ultime novità dill
con git+https://github.com/uqfoundation/dill.git@master#egg=dill
. E puoi ottenere l'ultima versione rilasciata con pip install dill
.
TypeError: __new__() takes at least 2 arguments (1 given)
tentativo quando uso dill
(che sembra promettente) con un oggetto piuttosto complesso che include un file audio.
TypeError
quando fai cosa, esattamente? Questo di solito è un segno di avere il numero errato di argomenti durante l'istanza di un'istanza di classe. Se questo non fa parte del flusso di lavoro della domanda precedente, potresti pubblicarlo come un'altra domanda, inviarmelo tramite e-mail o aggiungerlo come problema sulla dill
pagina di github?
dill
problema.
dil
Mi dà MemoryError
però! così fa cPickle
, pickle
e hickle
.
Puoi usare anycache per fare il lavoro per te. Considera tutti i dettagli:
pickle
modulo Python per gestire lambda
e tutte le belle funzionalità di Python.Supponendo di avere una funzione myfunc
che crea l'istanza:
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 chiama myfunc
per la prima volta e sottrae il risultato a un file cachedir
usando un identificatore univoco (a seconda del nome della funzione e dei suoi argomenti) come nome file. Ad ogni corsa consecutiva, l'oggetto in salamoia viene caricato. Se cachedir
viene conservato tra le esecuzioni di Python, l'oggetto decapato viene prelevato dalla precedente esecuzione di Python.
Per ulteriori dettagli consultare la documentazione
anycache
per salvare più di un'istanza, per esempio, di un class
contenitore come list
(non era il risultato della chiamata di una funzione)?
Esempio rapido usando company1
dalla tua domanda, con python3.
import pickle
# Save the file
pickle.dump(company1, file = open("company1.pickle", "wb"))
# Reload the file
company1_reloaded = pickle.load(open("company1.pickle", "rb"))
Tuttavia, come notato questa risposta , spesso il sottaceto fallisce. Quindi dovresti davvero usare dill
.
import dill
# Save the file
dill.dump(company1, file = open("company1.pickle", "wb"))
# Reload the file
company1_reloaded = dill.load(open("company1.pickle", "rb"))