Wie kann man sich einlegen?

70

Ich möchte, dass meine Klasse Save and Load-Funktionen implementiert, die einfach ein Pickle der Klasse ausführen. Aber anscheinend können Sie "Selbst" nicht wie folgt verwenden. Wie kannst du das tun?

self = cPickle.load(f)

cPickle.dump(self,f,2)
Delgan
quelle
Ich könnte das Diktat aufgreifen , aber dann könnte ich die Klasse später nicht mehr ändern.
7
ausgezeichneter Titel, + 1'ed
pcko1

Antworten:

44

Das habe ich letztendlich getan. Durch das Aktualisieren der __dict__Mittel behalten wir alle neuen Mitgliedsvariablen bei, die ich der Klasse hinzufüge, und aktualisieren nur diejenigen, die vorhanden waren, als das Objekt zuletzt ausgewählt wurde. Es scheint am einfachsten zu sein, wenn der Speicher- und Ladecode in der Klasse selbst beibehalten wird, sodass beim Aufrufen von Code nur ein object.save () ausgeführt wird.

def load(self):
    f = open(self.filename, 'rb')
    tmp_dict = cPickle.load(f)
    f.close()          

    self.__dict__.update(tmp_dict) 


def save(self):
    f = open(self.filename, 'wb')
    cPickle.dump(self.__dict__, f, 2)
    f.close()
Gemeinschaft
quelle
5
Ein kleiner PEP8-Nitpick - die Funktionen sollten benannt loadund savein Kleinbuchstaben python.org/dev/peps/pep-0008/#function-names
Adam Matan
Dies ist ein merge. Sie sollten anrufen, self.__dict__.clear()bevor Sie anrufenself.__dict__.update(..)
deepelement
1
Gibt es einen bestimmten Grund, warum Sie keine "with" -Anweisungen verwenden?
Ekelhaft
1
Warum verwenden Sie den 3. Parameter = 2?
Agustin Barrachina
Diese Lösung hat einige nette Eigenschaften, aber einen wichtigen Nachteil: Sie müssen ein Objekt erstellen, bevor Sie die Ladefunktion aufrufen, und Sie haben möglicherweise keine Option, den Konstruktor mit gültigen Daten aufzurufen. Das Erstellen einer Factory-Klassenmethode ist daher sinnvoller.
Emem
26

Der Dump-Teil sollte wie vorgeschlagen funktionieren. Für den Ladeteil können Sie eine @ classmethod definieren , die eine Instanz aus einer bestimmten Datei lädt und zurückgibt.

@classmethod
def loader(cls,f):
    return cPickle.load(f)

dann würde der Anrufer so etwas tun wie:

class_instance = ClassName.loader(f)
Ofri Raviv
quelle
Was Sie also sagen, ist mit der Klasse Foo, Instanz foo Ich kann foo.Save () machen, aber ich kann nicht foo.Load () Ich müsste foo = foo.Load () - (Klassenmethoden können mit aufgerufen werden ein
7
Es ist durchaus sinnvoll, dass Sie foo = Foo.load () und nicht foo = Foo (); foo.load () verwenden. Wenn Foo beispielsweise einige Variablen hat, die an den Init übergeben werden MÜSSEN, müssen Sie sie für foo = Foo () nachholen. oder wenn der Init einige schwere Berechnungen von in der Instanz gespeicherten Variablen durchführt, wäre dies umsonst.
Ofri Raviv
1
Dies funktioniert, ursprünglich hatte ich foo = Foo ('somefilename') und Foo hat seine Daten selbst geladen. Jetzt mache ich a: foo = Foo.Load ('somefilename') Wenn ich die Definition von Foo ändere, kann ich die neuen Funktionen / Mitglieder in der mit Pickle geladenen foo-Instanz weiterhin verwenden.
8

Wenn Sie möchten, dass sich Ihre Klasse von einer gespeicherten Gurke selbst aktualisiert, müssen Sie so ziemlich alles verwenden __dict__.update, wie Sie es in Ihrer eigenen Antwort getan haben. Es ist jedoch so, als würde eine Katze ihren Schwanz jagen ... während Sie die Instanz bitten, sich im Wesentlichen auf den vorherigen Zustand zurückzusetzen.

Ihre Antwort hat eine leichte Änderung. Sie können tatsächlich einlegen self.

>>> import dill
>>> class Thing(object):
...   def save(self):
...     return dill.dumps(self)
...   def load(self, obj):
...     self.__dict__.update(dill.loads(obj).__dict__)
... 
>>> t = Thing()
>>> t.x = 1
>>> _t = t.save()
>>> t.x = 2
>>> t.x
2
>>> t.load(_t)
>>> t.x
1

Ich habe loadsund benutztdumps anstelle von loadund verwendet, dumpweil ich wollte, dass die Gurke in einer Zeichenfolge gespeichert wird. Das Verwenden von loadund dumpzu einer Datei funktioniert auch. Und tatsächlich kann ich dilleine Klasseninstanz zur späteren Verwendung in eine Datei einfügen ... selbst wenn die Klasse interaktiv definiert ist. Fortsetzung von oben ...

>>> with open('self.pik', 'w') as f:
...   dill.dump(t, f)
... 
>>> 

dann anhalten und neu starten ...

Python 2.7.10 (default, May 25 2015, 13:16:30) 
[GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import dill
>>> with open('self.pik', 'r') as f:
...   t = dill.load(f)
... 
>>> t.x
1
>>> print dill.source.getsource(t.__class__)
class Thing(object):
  def save(self):
    return dill.dumps(self)
  def load(self, obj):
    self.__dict__.update(dill.loads(obj).__dict__)

>>> 

Ich verwende dill, das hier verfügbar ist: https://github.com/uqfoundation

Mike McKerns
quelle
2

In den Dokumenten finden Sie ein Beispiel für das Auswählen einer Instanz . (Suchen Sie nach dem Beispiel "TextReader"). Die Idee ist , zu definieren __getstate__und __setstate__Methoden, mit denen Sie definieren , welche Daten Bedürfnisse gebeizt werden, und wie diese Daten zu verwenden , um Wieder instantiate das Objekt.

unutbu
quelle
1
Dies löst das Problem nicht, da Sie in der Load-Funktion der Klasse immer noch nicht self = cPickle.load (f) aufrufen können, um die Klasse mit den geladenen Daten zu füllen. Oder natürlich kann ich die Klassendaten selbst auswählen, aber ich versuche zu vermeiden, den gesamten Code zu schreiben und gezwungen zu sein, ihn zu aktualisieren, wenn sich die Variablen der Klassenmitglieder ändern.
0

So habe ich es gemacht. Der Vorteil ist, dass Sie kein neues Objekt erstellen müssen. Sie können es einfach direkt laden.

def save(self):
    with open(self.filename, 'wb') as f:
        pickle.dump(self, f)

@classmethod
def load(cls, filename):
    with open(filename, 'rb') as f:
        return pickle.load(f)

Wie man es benutzt:

class_object.save()
filename = class_object.filename
del class_object

class_object = ClassName.load(filename)
Sorin Dragan
quelle