wake-up-neo.com

Speichern eines Objekts (Datenpersistenz)

Ich habe ein Objekt wie dieses erstellt:

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

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

195
Peterstone

Sie können das Modul pickle 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 Hilfsprogramm 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 auf ein paar etwas fortgeschrittene Verwendungsthemen eingehen.

cPickle (oder _pickle) vs pickle

Es ist fast immer vorzuziehen, das cPickle -Modul anstelle von pickle zu verwenden, da 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. Es könnte nicht einfacher sein, dorthin zu wechseln. Ändern Sie einfach die Anweisung import wie folgt:

import cPickle as pickle

In Python 3 wurde cPickle umbenannt _pickle, dies ist jedoch nicht mehr erforderlich, da das Modul pickle dies jetzt automatisch ausführt - siehe Welcher Unterschied zwischen pickle und _pickle in python= 3? =.

Das Problem ist, dass Sie Folgendes verwenden können, um sicherzustellen, dass Ihr Code immer die C-Version verwendet, wenn sie sowohl in Python 2 als auch in === (Python $ === 2) verfügbar ist 3:

try:
    import cPickle as pickle
except ModuleNotFoundError:
    import pickle

Datenstromformate (Protokolle)

pickle kann Dateien in verschiedenen, Python-spezifischen Formaten lesen und schreiben, die als Protokolle bezeichnet werden, wie in Dokumentation , "Protokoll" beschrieben version 0 "ist ASCII und daher" lesbar ". Versionen> 1 sind binär und die höchste verfügbare Version hängt davon ab, welche Version von Python verwendet wird Der Standard hängt auch von der Python Version ab. In Python 2 war der Standard die Protokollversion 0, aber in Python 3.7 ist es Protokollversion 3. In Python 3.x hatte das Modul ein pickle.DEFAULT_PROTOCOL hinzugefügt, aber das existiert nicht in Python 2.

Zum Glück gibt es eine Abkürzung zum Schreiben von pickle.HIGHEST_PROTOCOL Verwenden Sie bei jedem Anruf (vorausgesetzt, Sie möchten und tun dies in der Regel) einfach die wörtliche Nummer -1 - Ähnlich wie beim Referenzieren des letzten Elements 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 müssten Sie das Protokoll nur einmal angeben, wenn Sie ein Pickler -Objekt zur Verwendung in mehreren Beizvorgä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 Python-Versionen ausgeführt werden, möchten Sie wahrscheinlich explizit eine bestimmte Protokollnummer (dh einen Hardcode) verwenden, die von allen gelesen werden kann (Spätere Versionen können im Allgemeinen Dateien lesen, die von früheren Versionen erstellt wurden.).

Mehrere Objekte

Während eine Pickle-Datei eine beliebige Anzahl von gebeizten Objekten enthalten kann , wie in den obigen Beispielen gezeigt, ist es oft einfacher, sie alle in irgendeiner Form zu speichern, wenn eine unbekannte Anzahl vorhanden ist Container mit variabler Größe, wie ein list, Tuple oder dict, und schreiben Sie 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 stelle die Liste und alles darin später wieder her mit:

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

Der Hauptvorteil ist, dass Sie nicht wissen müssen, wie viele Objektinstanzen gespeichert sind, um sie später wieder zu laden (obwohl dies ohne diese Informationen möglich ist , erfordert es einige geringfügige Spezialcode). Siehe die Antworten auf die entsprechende Frage Speichern und Laden mehrerer Objekte in einer Pickle-Datei? für Einzelheiten zu den verschiedenen Möglichkeiten. Persönlich [~ # ~] Ich [~ # ~] mag @Lutz Prechelt's Antwort das Beste. Hier ist es den Beispielen 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))
387
martineau

Ich denke, es ist eine ziemlich starke Annahme, dass das Objekt ein class ist. Was ist, wenn es kein class ist? Es gibt auch die Annahme, dass das Objekt nicht im Interpreter definiert wurde. Was ist, wenn es im Interpreter definiert wurde? Was ist auch, wenn die Attribute dynamisch hinzugefügt wurden? Wenn bei einigen python - Objekten nach der Erstellung Attribute zu ihrem __dict__ Hinzugefügt wurden, beachtet pickle das Hinzufügen dieser Attribute nicht (dh es "vergisst", was sie waren) hinzugefügt - weil pickle durch Bezugnahme auf die Objektdefinition serialisiert wird).

In all diesen Fällen können pickle und cPickle schrecklich versagen.

Wenn Sie ein object (willkürlich erstellt) speichern möchten, in dem Sie Attribute haben (entweder in der Objektdefinition hinzugefügt oder danach), sollten Sie dill verwenden, das serialisiert werden kann fast alles in Python.

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

Hoppla ... pickle kann damit nicht umgehen. Versuchen wir es mit dill. Wir werden einen anderen Objekttyp (ein 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 lies 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, warum pickle fehlschlägt und dill nicht, ist, dass dill__main__ (Größtenteils) wie ein Modul behandelt und auch pickeln kann Klassendefinitionen statt Beizen durch Referenz (wie es pickle tut). Der Grund, warum dill ein lambda einsammeln kann, ist, dass es einen Namen hat ... dann kann es zu Magie kommen.

Tatsächlich gibt es eine einfachere Möglichkeit, alle diese Objekte zu speichern, insbesondere wenn Sie viele Objekte erstellt haben. Entleere einfach die gesamte python Sitzung und komme 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')
>>> 

Fahren Sie jetzt Ihren Computer herunter, 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 dill nicht Teil der python Standardbibliothek ist. Wenn Sie also kein python Paket auf installieren können Ihr Server, dann können Sie es nicht verwenden.

Wenn Sie jedoch in der Lage sind, python Pakete auf Ihrem System zu installieren, können Sie mit git+https://github.com/uqfoundation/[email protected]#Egg=dill Das neueste dill und die neueste veröffentlichte Version erhalten mit pip install dill.

44
Mike McKerns

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

  • Es benutzt dill als Backend, welches das python pickle Modul erweitert, um lambda und alle Nice python Funktionen.
  • Es speichert verschiedene Objekte in verschiedenen Dateien und lädt sie ordnungsgemäß neu.
  • Beschränkt 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 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 zum ersten Mal myfunc auf und kopiert das Ergebnis in eine Datei in cachedir, wobei ein eindeutiger Bezeichner (abhängig vom Funktionsnamen und seinen Argumenten) als Dateiname verwendet wird. Bei jedem nachfolgenden Durchlauf wird das eingelegte Objekt geladen. Wenn das cachedir zwischen python Läufen beibehalten wird, wird das ausgewählte Objekt aus dem vorherigen python Läufen übernommen.

Für weitere Details siehe Dokumentation

3
c0fec0de