Speichern eines Objekts (Datenpersistenz)


233

Ich habe ein Objekt wie das folgende erstellt:

company1.name = 'banana' 
company1.value = 40

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


1
Im Beispiel für Leute, die hierher kommen, finden Sie ein einfaches Beispiel für die Verwendung von Gurke.
Martin Thoma

@MartinThoma: Warum bevorzugen Sie (scheinbar) diese Antwort gegenüber der akzeptierten (der verknüpften Frage )?
Martineau

Zu dem Zeitpunkt, als ich verlinkt habe, hatte die akzeptierte Antwort keine protocol=pickle.HIGHEST_PROTOCOL. Meine Antwort gibt auch Alternativen zu Gurke.
Martin Thoma

Antworten:


449

Sie können das pickleModul in der Standardbibliothek verwenden. Hier ist eine elementare Anwendung 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 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

Sie können auch Ihr eigenes einfaches Dienstprogramm wie das folgende definieren, das eine Datei öffnet und ein einzelnes Objekt darauf schreibt:

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

Aktualisieren

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

cPickle(oder _pickle) vs.pickle

Es ist fast immer vorzuziehen, das cPickleModul tatsächlich zu verwenden, anstatt pickleweil das erstere in C geschrieben ist und viel schneller ist. Es gibt einige subtile Unterschiede zwischen ihnen, aber in den meisten Situationen sind sie gleichwertig und die C-Version bietet eine überlegene Leistung. Der Wechsel könnte nicht einfacher sein. Ändern Sie einfach die importAnweisung wie folgt:

import cPickle as pickle

In Python 3 cPicklewurde umbenannt _pickle, dies ist jedoch nicht mehr erforderlich, da das pickleModul dies jetzt automatisch ausführt. Siehe Welcher Unterschied zwischen pickle und _pickle in Python 3? .

Der Überblick ist, dass Sie Folgendes verwenden können, um sicherzustellen, dass Ihr Code immer die C-Version verwendet, wenn er sowohl in Python 2 als auch in Python 3 verfügbar ist:

try:
    import cPickle as pickle
except ModuleNotFoundError:
    import pickle

Datenstromformate (Protokolle)

picklekann Dateien in verschiedenen, Python-spezifischen Formaten lesen und schreiben, die als Protokolle bezeichnet werden, wie in der Dokumentation beschrieben . "Protokollversion 0" ist ASCII und daher "für Menschen lesbar". Versionen> 0 sind binär und die höchste verfügbare Version hängt davon ab, welche Version von Python verwendet wird. Die Standardeinstellung hängt auch von der Python-Version ab. In Python 2 war die Standardversion die Protokollversion 0, in Python 3.8.1 die Protokollversion 4. In Python 3.x wurde dem Modul ein Modul pickle.DEFAULT_PROTOCOLhinzugefügt, das in Python 2 jedoch nicht vorhanden ist.

Glücklicherweise gibt es pickle.HIGHEST_PROTOCOLin jedem Aufruf eine Abkürzung zum Schreiben (vorausgesetzt, Sie möchten dies und tun dies normalerweise). Verwenden Sie einfach die Literalzahl -1- ähnlich wie beim Verweisen auf das letzte Element einer Sequenz über einen negativen Index. Also anstatt zu schreiben:

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

Sie können einfach schreiben:

pickle.dump(obj, output, -1)

In beiden Fällen hätten Sie das Protokoll nur einmal angegeben, wenn Sie ein PicklerObjekt zur Verwendung in mehreren Pickle-Vorgängen erstellt hätten:

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

Hinweis : Wenn Sie sich in einer Umgebung befinden, in der verschiedene Versionen von Python ausgeführt werden, möchten Sie wahrscheinlich explizit eine bestimmte Protokollnummer verwenden (dh Hardcode), die alle lesen können (spätere Versionen können im Allgemeinen Dateien lesen, die von früheren Versionen erstellt wurden). .

Mehrere Objekte

Während eine Beize - Datei kann eine beliebige Anzahl von eingelegten Objekten enthält, wie in den obigen Proben gezeigt, wenn eine unbekannte Anzahl von ihnen gibt es, ist es oft einfacher , sie zu speichern alle in irgendeiner Art von variabler Größe Behältern, wie ein list, tupleoder dictund Schreib sie alle in einem einzigen Aufruf in die Datei:

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

und stellen Sie die Liste und alles darin später wieder her mit:

with open('tech_companies.pkl', 'rb') as input:
    tech_companies = pickle.load(input)

Der große Vorteil ist , dass Sie nicht wissen müssen, wie viele Objektinstanzen , um gespeichert werden , um sie später wieder zu laden (wenn auch ohne diese Informationen so zu tun ist möglich, es einig etwas spezialisierten Code erfordert). Antworten auf die zugehörige Frage anzeigen Speichern und Laden mehrerer Objekte in einer Pickle-Datei? Einzelheiten zu verschiedenen Möglichkeiten finden Sie hier. Persönlich ich wie @Lutz Prechelt die Antwort die beste. Hier ist es an die Beispiele hier angepasst:

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

1
Das ist selten für mich, weil ich mir vorgestellt habe, dass es einen einfacheren Weg gibt, ein Objekt zu speichern ... So etwas wie 'saveobject (company1, c: \ mypythonobjects)
Peterstone

4
@Peterstone: Wenn Sie nur ein Objekt speichern möchten, benötigen Sie nur etwa halb so viel Code wie in meinem Beispiel. Ich habe ihn absichtlich so geschrieben, wie ich es getan habe, um zu zeigen, wie mehr als ein Objekt gespeichert (und später zurückgelesen werden kann) aus) der gleichen Datei.
Martineau

1
@ Peterstone, es gibt einen sehr guten Grund für die Aufgabentrennung. Auf diese Weise gibt es keine Einschränkung hinsichtlich der Verwendung der Daten aus dem Beizprozess. Sie können es auf einer Disc speichern oder über eine Netzwerkverbindung senden.
Harald Scheirich

3
@martinaeau, dies war eine Reaktion auf die Bemerkung von perstones, dass man nur eine Funktion haben sollte, um ein Objekt auf der Festplatte zu speichern. Die Verantwortung der Gurken besteht nur darin, ein Objekt in Daten umzuwandeln, die als Block behandelt werden können. Das Schreiben von Dingen in eine Datei liegt in der Verantwortung der Dateiobjekte. Indem man die Dinge getrennt hält, ermöglicht man eine höhere Wiederverwendung, z. B. das Senden der eingelegten Daten über eine Netzwerkverbindung oder das Speichern in einer Datenbank, wobei alle Verantwortlichkeiten von der tatsächlichen Daten- <-> Objektkonvertierung getrennt sind
Harald Scheirich

1
Sie löschen company1und company2. Warum löschst du nicht auch Companyund zeigst, was passiert?
Mike McKerns

49

Ich denke, es ist eine ziemlich starke Annahme anzunehmen, dass das Objekt ein ist class. Was ist, wenn es kein ist class? Es gibt auch die Annahme, dass das Objekt nicht im Interpreter definiert wurde. Was ist, wenn es im Interpreter definiert wurde? Was wäre, wenn die Attribute dynamisch hinzugefügt würden? Wenn bei einigen Python-Objekten __dict__nach der Erstellung Attribute hinzugefügt werden , picklewird das Hinzufügen dieser Attribute nicht berücksichtigt (dh es wird vergessen, dass sie hinzugefügt wurden - da sie pickleunter Bezugnahme auf die Objektdefinition serialisiert werden).

In all diesen Fällen pickleund cPicklekann Sie schrecklich scheitern.

Wenn Sie eine object(willkürlich erstellte) speichern möchten, in der Sie Attribute haben (entweder in der Objektdefinition hinzugefügt oder danach), ist es am besten dill, diese zu verwenden , mit der fast alles in Python serialisiert werden kann.

Wir beginnen mit einer Klasse…

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

Jetzt herunterfahren und neu starten ...

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'
>>> 

Ups ... picklekann nicht damit umgehen. Lass es uns versuchen dill. Wir werden einen anderen Objekttyp (a lambda) für ein gutes Maß einwerfen .

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

Und jetzt lesen Sie die Datei.

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

Es klappt. Der Grund pickle, warum dies fehlschlägt und dillnicht, ist, dass es (größtenteils) wie ein Modul dillbehandelt __main__wird und auch Klassendefinitionen einbinden kann, anstatt durch Referenz zu beizen (wie es der pickleFall ist). Der Grund, warum a dilleingelegt werden kann, lambdaist, dass es ihm einen Namen gibt… dann kann Beizmagie passieren.

Tatsächlich gibt es eine einfachere Möglichkeit, alle diese Objekte zu speichern, insbesondere wenn Sie viele Objekte erstellt haben. Speichern Sie einfach die gesamte Python-Sitzung und kehren Sie später darauf zurück.

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

Schalten Sie jetzt Ihren Computer aus, genießen Sie einen Espresso oder was auch immer und kommen Sie später wieder ...

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>

Der einzige große Nachteil ist, dass er dillnicht Teil der Python-Standardbibliothek ist. Wenn Sie also kein Python-Paket auf Ihrem Server installieren können, können Sie es nicht verwenden.

Wenn Sie jedoch Python-Pakete auf Ihrem System installieren können, erhalten Sie die neuesten dillInformationen git+https://github.com/uqfoundation/dill.git@master#egg=dill. Und Sie können die neueste veröffentlichte Version mit erhalten pip install dill.


Ich bekomme eine, TypeError: __new__() takes at least 2 arguments (1 given)wenn ich versuche dill(was vielversprechend aussieht) mit einem ziemlich komplexen Objekt zu arbeiten, das eine Audiodatei enthält.
MikeiLL

1
@ MikeiLL: Du bekommst eine, TypeErrorwenn du was genau machst? Dies ist normalerweise ein Zeichen dafür, dass beim Instanziieren einer Klasseninstanz die falsche Anzahl von Argumenten vorliegt. Wenn dies nicht Teil des Workflows der obigen Frage ist, können Sie es als eine andere Frage posten, mir per E-Mail dillsenden oder als Problem auf der Github-Seite hinzufügen?
Mike McKerns

3
Für alle, die mitmachen , ist hier die verwandte Frage, die @MikeLL gestellt hat - aus der Antwort geht hervor, dass es anscheinend kein dillProblem war.
Martineau

dilIch gebe mir MemoryErroraber! so tut cPickle, pickleund hickle.
Färid Alijani

4

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

  • Es verwendet Dill als Backend, wodurch das Python- pickleModul lambdaund alle netten Python-Funktionen erweitert werden.
  • Es speichert verschiedene Objekte in verschiedenen Dateien und lädt sie ordnungsgemäß neu.
  • Begrenzt die Cache-Größe
  • Ermöglicht das Löschen des Cache
  • Ermöglicht die gemeinsame Nutzung von Objekten zwischen mehreren Läufen
  • Ermöglicht die Berücksichtigung von Eingabedateien, die das Ergebnis beeinflussen

Angenommen, Sie haben eine Funktion, myfuncdie 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 myfuncbeim ersten Mal auf und wählt das Ergebnis in eine Datei aus, cachedirindem ein eindeutiger Bezeichner (abhängig vom Funktionsnamen und seinen Argumenten) als Dateiname verwendet wird. Bei jedem aufeinanderfolgenden Lauf wird das eingelegte Objekt geladen. Wenn das cachedirzwischen Python-Läufen erhalten bleibt, wird das eingelegte Objekt aus dem vorherigen Python-Lauf übernommen.

Weitere Details finden Sie in der Dokumentation


Wie würde man anycachemehr als eine Instanz von beispielsweise einem classoder einem Container wie a speichern list(das war nicht das Ergebnis des Aufrufs einer Funktion)?
Martineau

2

Schnelles Beispiel company1aus Ihrer Frage mit 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"))

Wie diese Antwort jedoch feststellte, versagt die Gurke häufig. Also solltest du wirklich verwenden 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"))
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.