376 Stimmen

Speichern eines Objekts (Datenpersistenz)

Ich habe ein Objekt wie dieses erstellt:

company1.name = 'Banane'
company1.value = 40

Ich möchte dieses Objekt speichern. Wie kann ich das tun?

3 Stimmen

Siehe Beispiel für Leute, die hierher kommen, um ein einfaches Beispiel dafür zu sehen, wie man pickle benutzt.

0 Stimmen

@MartinThoma: Warum ziehst du diese Antwort (scheinbar) der akzeptierten (von der verlinkten Frage) vor?

0 Stimmen

Zum Zeitpunkt, als ich verlinkt habe, enthielt die akzeptierte Antwort nicht protocol=pickle.HIGHEST_PROTOCOL. Meine Antwort gibt auch Alternativen zu pickle.

681voto

martineau Punkte 110783

Sie könnten das pickle Modul in der Standardbibliothek verwenden. Hier ist eine einfache Anwendung davon auf Ihr Beispiel:

import pickle

class Company(object):
    def __init__(self, name, value):
        self.name = name
        self.value = value

with open('company_data.pkl', 'wb') as outp:
    company1 = Company('Banane', 40)
    pickle.dump(company1, outp, pickle.HIGHEST_PROTOCOL)

    company2 = Company('Spam', 42)
    pickle.dump(company2, outp, pickle.HIGHEST_PROTOCOL)

del company1
del company2

with open('company_data.pkl', 'rb') as inp:
    company1 = pickle.load(inp)
    print(company1.name)  # -> Banane
    print(company1.value)  # -> 40

    company2 = pickle.load(inp)
    print(company2.name) # -> Spam
    print(company2.value)  # -> 42

Sie könnten auch Ihr eigenes einfaches Dienstprogramm definieren, das wie folgt fungiert und eine Datei öffnet und ein einzelnes Objekt darin schreibt:

def save_object(obj, dateiname):
    with open(dateiname, 'wb') as outp:  # überschreibt eine vorhandene Datei.
        pickle.dump(obj, outp, pickle.HIGHEST_PROTOCOL)

# beispielhafte Verwendung
save_object(company1, 'firma1.pkl')

Aktualisierung

Da dies eine so beliebte Antwort ist, möchte ich einige leicht fortgeschrittene Themen ansprechen.

cPickle (oder _pickle) vs pickle

Es ist fast immer vorzuziehen, tatsächlich das cPickle Modul anstelle von pickle zu verwenden, da ersteres in C geschrieben ist und wesentlich schneller ist. Es gibt einige subtile Unterschiede zwischen ihnen, aber in den meisten Situationen sind sie äquivalent und die C-Version bietet deutlich bessere Leistung. Das Umschalten darauf ist sehr einfach, ändern Sie einfach die import Anweisung zu:

import cPickle as pickle

In Python 3 wurde cPickle in _pickle umbenannt, aber dies ist nicht mehr notwendig, da das pickle Modul dies jetzt automatisch macht. Siehe Was ist der Unterschied zwischen pickle und _pickle in Python 3?.

Die Zusammenfassung ist, dass Sie etwas Ähnliches verwenden könnten, um sicherzustellen, dass Ihr Code immer die C-Version verwendet, wenn sie in beiden Python 2 und 3 verfügbar ist:

try:
    import cPickle as pickle
except ModuleNotFoundError:
    import pickle

Datenstromformate (Protokolle)

pickle kann Dateien in verschiedenen, Python-spezifischen Formaten lesen und schreiben, sogenannten Protokollen, wie in der Dokumentation beschrieben. "Protokollversion 0" ist ASCII und daher "menschlich lesbar". Versionen > 0 sind binär und die höchste verfügbare hängt davon ab, welche Python-Version verwendet wird. Der Standard hängt auch von der Python-Version ab. In Python 2 war der Standard Protokollversion 0, aber in Python 3.8.1 ist es Protokollversion 4. Im Modul in Python 3.x wurde pickle.DEFAULT_PROTOCOL hinzugefügt, das gibt es aber in Python 2 nicht.

Glücklicherweise gibt es eine Abkürzung, um pickle.HIGHEST_PROTOCOL bei jedem Aufruf zu schreiben (vorausgesetzt, das ist, was Sie wollen, und normalerweise ist es das), verwenden Sie einfach die buchstäbliche Zahl -1 - ähnlich dem Aufrufen des letzten Elements einer Sequenz über einen negativen Index. Anstatt also zu schreiben:

pickle.dump(obj, outp, pickle.HIGHEST_PROTOCOL)

kannst du einfach schreiben:

pickle.dump(obj, outp, -1)

Auf diese Weise müssen Sie das Protokoll nur einmal angeben, wenn Sie ein Pickler Objekt für die Verwendung in mehreren Pickle-Operationen erstellen:

pickler = pickle.Pickler(outp, -1)
pickler.dump(obj1)
pickler.dump(obj2)
   etc...

Hinweis: Wenn Sie in einer Umgebung mit verschiedenen Python-Versionen arbeiten, sollten Sie wahrscheinlich eine bestimmte Protokollnummer explizit verwenden (dh fest codieren), die alle lesen können (spätere Versionen können im Allgemeinen Dateien lesen, die von früheren erstellt wurden).

Mehrere Objekte

Obwohl eine Pickle-Datei beliebig viele gepickelte Objekte enthalten kann, wie in den obigen Beispielen gezeigt, wenn die Anzahl unbekannt ist, ist es oft einfacher, sie alle in einer Art variabler Container, wie einer list, tuple oder dict zu speichern und sie alle gleichzeitig in einer einzigen Anweisung in die Datei zu schreiben:

tech_firmen = [
    Company('Apple', 114.18), Company('Google', 908.60), Company('Microsoft', 69.18)
]
save_object(tech_firmen, 'tech_firmen.pkl')

und die Liste und alles in ihr später mit folgendem wiederherzustellen:

with open('tech_firmen.pkl', 'rb') as inp:
    tech_firmen = pickle.load(inp)

Der Hauptvorteil ist, dass Sie nicht wissen müssen, wie viele Objektinstanzen gespeichert sind, um sie später zurückzuladen (obwohl das ohne diese Information möglich ist, erfordert es etwas spezialisierten Code). Sehen Sie die Antworten auf die verwandte Frage Speichern und Laden mehrerer Objekte in Pickle-Datei? für Details zu verschiedenen Möglichkeiten, dies zu tun. Persönlich mochte ich @Lutz Prechelt's Antwort am besten, daher wird darin im folgenden Beispielcode verwendet:

class Company:
    def __init__(self, name, value):
        self.name = name
        self.value = value

def pickle_loader(dateiname):
    """ Deserialisieren einer Datei mit gepickelten Objekten. """
    with open(dateiname, "rb") as f:
        while True:
            try:
                yield pickle.load(f)
            except EOFError:
                break

print('Unternehmen in Pickle-Datei:')
for firma in pickle_loader('firma_datum.pkl'):
    print('  name: {}, wert: {}'.format(firma.name, firma.value))

2 Stimmen

Dies ist für mich selten, weil ich mir vorgestellt habe, dass es eine einfachere Möglichkeit geben würde, ein Objekt zu speichern... Etwas wie 'saveobject(company1,c:\mypythonobjects)

9 Stimmen

@Peterstone: Wenn Sie nur ein Objekt speichern möchten, benötigen Sie nur etwa halb so viel Code wie in meinem Beispiel - ich habe es absichtlich so geschrieben, um zu zeigen, wie mehr als ein Objekt in dieselbe Datei gespeichert (und später wieder gelesen) werden könnte.

1 Stimmen

@Peterstone, es gibt einen sehr guten Grund für die Trennung der Verantwortlichkeiten. Auf diese Weise gibt es keine Einschränkung, wie die Daten aus dem Einmachprozess verwendet werden. Sie können sie auf die Festplatte speichern oder sie auch über eine Netzwerkverbindung senden.

75voto

Mike McKerns Punkte 30236

Ich denke, es ist eine ziemlich starke Annahme anzunehmen, dass das Objekt eine Klasse ist. Was ist, wenn es keine Klasse ist? Es gibt auch die Annahme, dass das Objekt nicht im Interpreter definiert wurde. Was, wenn es im Interpreter definiert wurde? Außerdem, was ist, wenn die Attribute dynamisch hinzugefügt wurden? Wenn einigen Python-Objekten nach der Erstellung Attribute zu ihrem __dict__ hinzugefügt werden, respektiert pickle nicht die Hinzufügung dieser Attribute (d. h. es 'vergisst', dass sie hinzugefügt wurden - weil pickle durch Verweis auf die Objektdefinition serialisiert).

In all diesen Fällen können pickle und cPickle dich schrecklich im Stich lassen.

Wenn du versuchst, ein Objekt (beliebig erstellt) zu speichern, bei dem du Attribute hast (entweder in der Objektdefinition hinzugefügt oder danach)... ist deine beste Wahl, dill zu verwenden, das fast alles in Python serialisieren kann.

Wir fangen mit einer Klasse an...

Python 2.7.8 (default, 13. Juli 2014, 02:29:54) 
[GCC 4.2.1 Kompatibles Apple Clang 4.1 ((tags/Apple/clang-421.11.66))] auf Darvin
Geben Sie "help", "copyright", "credits" oder "license" für mehr Informationen ein.
>>> import pickle
>>> class Firma:
...     pass
... 
>>> firma1 = Firma()
>>> firma1.name = 'Banane'
>>> firma1.wert = 40
>>> with open('firma.pkl', 'wb') as f:
...     pickle.dump(firma1, f, pickle.HIGHEST_PROTOCOL)
... 
>>> 

Jetzt herunterfahren und neu starten...

Python 2.7.8 (default, 13. Juli 2014, 02:29:54) 
[GCC 4.2.1 Kompatibles Apple Clang 4.1 ((tags/Apple/clang-421.11.66))] auf Darvin
Geben Sie "help", "copyright", "credits" oder "license" für mehr Informationen ein.
>>> import pickle
>>> with open('firma.pkl', 'rb') as f:
...     firma1 = pickle.load(f)
... 
Traceback (Die aktuellste letzte Anwendung):
  Datei "", Zeile 2, in 
  Datei "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", Zeile 1378, in load
    return Entpacker(file).load()
  Datei "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", Zeile 858, in load
dispatch[key](self)
  Datei "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", Zeile 1090, in load_global
    klass = self.find_class(module, name)
  Datei "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", Zeile 1126, in find_class
    klass = getattr(mod, name)
AttributeError: 'Modul' Objekt hat kein Attribut 'Firma'
>>> 

Oops... pickle kann damit nicht umgehen. Lass uns dill versuchen. Wir werden eine weitere Objektart hinzufügen (ein lambda) zur Sicherheit.

Python 2.7.8 (default, 13. Juli 2014, 02:29:54) 
[GCC 4.2.1 Kompatibles Apple Clang 4.1 ((tags/Apple/clang-421.11.66))] auf Darvin
Geben Sie "help", "copyright", "credits" oder "license" für mehr Informationen ein.
>>> import dill       
>>> class Firma:
...     pass
... 
>>> firma1 = Firma()
>>> firma1.name = 'Banane'
>>> firma1.wert = 40
>>> 
>>> firma2 = lambda x:x
>>> firma2.name = 'Rhabarber'
>>> firma2.wert = 42
>>> 
>>> with open('firma_dill.pkl', 'wb') as f:
...     dill.dump(firma1, f)
...     dill.dump(firma2, f)
... 
>>> 

Und jetzt die Datei lesen.

Python 2.7.8 (default, 13. Juli 2014, 02:29:54) 
[GCC 4.2.1 Kompatibles Apple Clang 4.1 ((tags/Apple/clang-421.11.66))] auf Darvin
Geben Sie "help", "copyright", "credits" oder "license" für mehr Informationen ein.
>>> import dill
>>> with open('firma_dill.pkl', 'rb') as f:
...     firma1 = dill.load(f)
...     firma2 = dill.load(f)
... 
>>> firma1 
<__main__.Firma Instanz bei 0x107909128>
>>> firma1.name
'Banane'
>>> firma1.wert
40
>>> firma2.name
'Rhabarber'
>>> firma2.wert
42
>>>    

Es funktioniert. Der Grund, warum pickle versagt und dill nicht, liegt darin, dass dill __main__ wie ein Modul behandelt (weitgehend) und auch Klassendefinitionen anstelle von Referenzen pickeln kann (wie pickle es tut). Der Grund, warum dill ein lambda einwecken kann, ist, dass es ihm einen Namen gibt... dann kann die Pöckelmagie passieren.

Eigentlich gibt es einen einfacheren Weg, all diese Objekte zu speichern, insbesondere wenn du viele Objekte erstellt hast. Speichere einfach die gesamte Python-Sitzung und komm später darauf zurück.

Python 2.7.8 (default, 13. Juli 2014, 02:29:54) 
[GCC 4.2.1 Kompatibles Apple Clang 4.1 ((tags/Apple/clang-421.11.66))] auf Darvin
Geben Sie "help", "copyright", "credits" oder "license" für mehr Informationen ein.
>>> import dill
>>> class Firma:
...     pass
... 
>>> firma1 = Firma()
>>> firma1.name = 'Banane'
>>> firma1.wert = 40
>>> 
>>> firma2 = lambda x:x
>>> firma2.name = 'Rhabarber'
>>> firma2.wert = 42
>>> 
>>> dill.dump_session('dill.pkl')
>>> 

Jetzt deinen Computer herunterfahren, einen Espresso genießen oder was auch immer, und später zurückkommen...

Python 2.7.8 (default, 13. Juli 2014, 02:29:54) 
[GCC 4.2.1 Kompatibles Apple Clang 4.1 ((tags/Apple/clang-421.11.66))] auf Darvin
Geben Sie "help", "copyright", "credits" oder "license" für mehr Informationen ein.
>>> import dill
>>> dill.load_session('dill.pkl')
>>> firma1.name
'Banane'
>>> firma1.wert
40
>>> firma2.name
'Rhabarber'
>>> firma2.wert
42
>>> firma2
 bei 0x1065f2938>

Der einzige große Nachteil ist, dass dill nicht Teil der Python-Standardbibliothek ist. Also wenn du kein Python-Paket auf deinem Server installieren kannst, kannst du es nicht verwenden.

Wenn du jedoch in der Lage bist, Python-Pakete auf deinem System zu installieren, kannst du die neueste Version von dill mit git+https://github.com/uqfoundation/dill.git@master#egg=dill erhalten. Und du kannst die neueste veröffentlichte Version mit pip install dill bekommen.

0 Stimmen

Ich erhalte einen TypeError: __new__() erfordert mindestens 2 Argumente (1 gegeben) Fehler, wenn ich versuche, dill (das vielversprechend aussieht) mit einem ziemlich komplexen Objekt zu verwenden, das eine Audiodatei enthält.

1 Stimmen

@MikeiLL: Sie erhalten einen TypeError, wenn Sie was genau tun? Das ist normalerweise ein Anzeichen dafür, dass Sie die falsche Anzahl von Argumenten beim Instanziieren einer Klasseninstanz haben. Wenn dies nicht Teil des Workflows der obigen Frage ist, könnten Sie es als eine weitere Frage posten, mir per E-Mail senden oder es als Problem auf der dill GitHub-Seite hinzufügen.

3 Stimmen

Für alle, die mitlesen, hier ist die verwandte Frage, die @MikeLL gepostet hat - laut der Antwort war es anscheinend kein dill Problem.

18voto

Anthony Ebert Punkte 585

Schnelles Beispiel mit company1 aus Ihrer Fragestellung, mit python3.

import pickle

# Datei speichern
pickle.dump(company1, file = open("company1.pickle", "wb"))

# Datei neu laden
company1_reloaded = pickle.load(open("company1.pickle", "rb"))

Allerdings, wie diese Antwort bemerkte, scheitert `pickle` oft. Deshalb sollten Sie wirklich dill verwenden.

import dill

# Datei speichern
dill.dump(company1, file = open("company1.pickle", "wb"))

# Datei neu laden
company1_reloaded = dill.load(open("company1.pickle", "rb"))

7voto

c0fec0de Punkte 611

Sie können anycache verwenden, um die Arbeit für Sie zu erledigen. Es berücksichtigt alle Details:

  • Es verwendet dill als Backend, das das Python-pickle-Modul erweitert, um lambda und alle netten Python-Funktionen zu verarbeiten.
  • Es speichert verschiedene Objekte in verschiedenen Dateien und lädt sie korrekt neu.
  • Limitiert die Cache-Größe
  • Erlaubt das Löschen des Caches
  • Erlaubt das Teilen von Objekten zwischen mehreren Durchläufen
  • Erlaubt die Berücksichtigung von Eingabedateien, die das Ergebnis beeinflussen

Angenommen, Sie haben eine Funktion myfunc, die die Instanz erstellt:

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 ruft myfunc beim ersten Mal auf und pickelt das Ergebnis in eine Datei in cachedir mit einem eindeutigen Bezeichner (abhängig vom Funktionsnamen und den Argumenten) als Dateiname. Bei jedem weiteren Durchlauf wird das gepickelte Objekt geladen. Wenn das cachedir zwischen den Python-Durchläufen erhalten bleibt, wird das gepickelte Objekt vom vorherigen Python-Durchlauf verwendet.

Für weitere Details siehe die Dokumentation

0 Stimmen

Wie würde man anycache verwenden, um mehr als eine Instanz von beispielsweise einer Klasse oder einem Behälter wie einer Liste (die nicht das Ergebnis des Aufrufs einer Funktion war) zu speichern?

4voto

Neuere Versionen von pandas haben auch eine Funktionalität zum Speichern von Pickles.

Ich finde es einfacher. z.B.

pd.to_pickle(object_to_save,'/temp/saved_pkl.pickle' )

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X