Instanz einer Klasse picklen

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
AntagonisT
User
Beiträge: 42
Registriert: Samstag 11. Juli 2009, 16:36

Hallo, ich steh wieder mal völlig auf dem Schlauch:

Ich möchte einfach eine Instanz einer Klasse mit pickle abspeichern und später wieder laden.

hier ein möglichst vereinfachtes Beispiel:

Code: Alles auswählen

import pickle

class Point():
    def __init__(self):
        self.x = 0
        self.y = 0        

    def __str__(self):
        return ('x: ' + str(self.x) + " " + 'y: ' + str(self.y))

    def save_point(self):
        file = open ("test.bin", "wb")
        pickle.dump (self, file)

    def open_point(self):    ###brauch ich da ein "self"? gehört "open_point()" überhaupt in die Klasse?
        data = open("test.bin", "rb")
        ### und jetzt?! wie kann ich sagen "nimm 'data' und tu das als neue Instanz p1 nehmen"?        

Ich erstelle mir einen Punkt p1:

Code: Alles auswählen

>>> p1 = Point()
>>> p1.x = 5
>>> p1.y = 7
>>> print(p1)
x: 5 y: 7
Der soll dann in eine Datei test.bin geschrieben werden:

Code: Alles auswählen

>>>p1.save_point()
>>>
dann verändere ich meinen Punkt p1...:

Code: Alles auswählen

>>>p1.x = 18
>>>p1.y = 84
>>> print(p1)
x: 18 y: 84
so, und jetzt möchte ich wieder meinen alten gespeicherten Punkt p1 laden:

Code: Alles auswählen

>>>p1.open_point()     ###??? p1 wird eigtl. nicht benötigt...
und hier hänge ich völlig in der Luft, was ist denn "data" eigentlich?
wenn ich innerhalb(!) der open_point-Methode die Zeile "print (data) hinzufüge, dann kommt:

Code: Alles auswählen

<_io.BufferedReader name='test.bin'>
wie verwandle ich das wieder in meine Instanz?
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

Ich möchte einfach eine Instanz einer Klasse mit pickle abspeichern und später wieder laden.
Dann machs doch so:

Code: Alles auswählen

class Husten(object): pass

husten = Husten()
# 1. dump
pickle.dump(husten, <fileobj>)
...
# 2. load
husten2 = pickle.load(<fileobj>)
Vorausgesetzt die Klassendefinition von Husten ist unter 2. bekannt, kannst Du mit husten2 wie mit husten weiterarbeiten.
Hat das einen bestimmten Grund, warum Du die pickle-Logik als Methode haben willst?
Zuletzt geändert von jerch am Sonntag 2. August 2009, 14:49, insgesamt 1-mal geändert.
BlackJack

@AntagonisT: Aus der Datei musst Du mit `pickle.load()` wieder laden.

Der Name der Methode ist falsch, denn damit wird ja kein Punkt "geöffnet", sondern Du willst einen *laden*. Eben das Gegenstück zum `save_point()`. Das `self` ist natürlich überflüssig, weil zu dem Zeitpunkt, wo Du den Punkt laden willst gibt es das Objekt ja noch gar nicht. Da könnte man also eine statische Methode draus machen. Ungetestet:

Code: Alles auswählen

class Point():
    def __init__(self, x=0, y=0):
        self.x = x
        self.y = y

    def __str__(self):
        return 'x: %s y: %s' % (self.x, self.y)

    def save_point(self, filename):
        with open(filename, 'wb') as point_file:
            pickle.dump(self, point_file)

    @staticmethod
    def load_point(filename):
        with open(filename, 'rb') as point_file:
            return pickle.load(point_file)
Allerdings ist es IMHO keine so gute Idee Punkte sich selbst picklen zu lassen. Insbesondere weil die Gegenrichtung alles mögliche entpicklen kann und nicht nur Punkte.
Karl
User
Beiträge: 252
Registriert: Freitag 29. Juni 2007, 17:49

Also als erstes solltest du file nicht überschreiben.
2. data ist einfach das Objekt, das open() zurückgibt.
3. Benutze doch einfach pickle.load() um das Objekt wieder zu laden.
4. Ich würde das nicht in der Klasse selbst machen sondern außerhalb. Es ergibt zB keinen Sinn, wenn ein Punkt sich selbst abspeichert oder ein Punkt andere Punkte läd.
Wenn ein Koordinatensystem einen Punkt speichert (oder läd), wäre das schon viel sinnvoller.
Edit:
5. Außerdem ist es ja auch sinnlos, save_point so zu benennen, save würde reichen, weil es ja klar ist, worauf sich das save dann bezieht.
AntagonisT
User
Beiträge: 42
Registriert: Samstag 11. Juli 2009, 16:36

jerch hat geschrieben:Dann machs doch so:

Code: Alles auswählen

class Husten(object): pass

husten = Husten()
# 1. dump
pickle.dump(husten, <fileobj>)
...
# 2. load
husten2 = pickle.load(<fileobj>)
Vorausgesetzt die Klassendefinition von Husten ist unter 2. bekannt, kannst Du mit husten2 wie mit husten weiterarbeiten.
kann ich nicht, weil husten2 ja nur innerhalb der "open_point" Methode bekannt ist und ich krieg den da nicht "raus".

nach dem Laden sollte 'husten' sozusagen komplett durch 'husten2' ausgetauscht sein.
Hat das einen bestimmten Grund, warum Du die pickle-Logik als Methode haben willst?
Nö, warum? Sollte ich die nicht nehmen?
AntagonisT
User
Beiträge: 42
Registriert: Samstag 11. Juli 2009, 16:36

BlackJack hat geschrieben:@AntagonisT: Aus der Datei musst Du mit `pickle.load()` wieder laden.
:oops: hatte ich drin und ist wohl beim kopieren/editieren irgendwie rausgefallen
BlackJack hat geschrieben:Der Name der Methode ist falsch, denn damit wird ja kein Punkt "geöffnet", sondern Du willst einen *laden*. Eben das Gegenstück zum `save_point()`. Das `self` ist natürlich überflüssig, weil zu dem Zeitpunkt, wo Du den Punkt laden willst gibt es das Objekt ja noch gar nicht. Da könnte man also eine statische Methode draus machen. Ungetestet:

Code: Alles auswählen

class Point():
    def __init__(self, x=0, y=0):
        self.x = x
        self.y = y

    def __str__(self):
        return 'x: %s y: %s' % (self.x, self.y)

    def save_point(self, filename):
        with open(filename, 'wb') as point_file:
            pickle.dump(self, point_file)

    @staticmethod
    def load_point(filename):
        with open(filename, 'rb') as point_file:
            return pickle.load(point_file)


"statische Methode", mal schauen, was das denn schon wieder macht...:wink:
AntagonisT
User
Beiträge: 42
Registriert: Samstag 11. Juli 2009, 16:36

Karl hat geschrieben:Also als erstes solltest du file nicht überschreiben.
2. data ist einfach das Objekt, das open() zurückgibt.
3. Benutze doch einfach pickle.load() um das Objekt wieder zu laden.
4. Ich würde das nicht in der Klasse selbst machen sondern außerhalb. Es ergibt zB keinen Sinn, wenn ein Punkt sich selbst abspeichert oder ein Punkt andere Punkte läd.
1. wie gesagt, vereinfacht zu Testzwecken, in echt kann man Namen etc eingeben
4. es erschien mir inkonsequent, wieder eine Methode rauszunehmen
Karl hat geschrieben: Wenn ein Koordinatensystem einen Punkt speichert (oder läd), wäre das schon viel sinnvoller.

sowas in der Art soll es dann werden. :wink:
BlackJack

@AntagonisT: Bei jerch's Vorschlag sind sowohl `husten` als auch `husten2` ausserhalb von Methoden von `Point`. Dass dass das jetzt jeder hier angemerkt hat, sollte Dir zu denken geben.
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

kann ich nicht, weil husten2 ja nur innerhalb der "open_point" Methode bekannt ist und ich krieg den da nicht "raus".

nach dem Laden sollte 'husten' sozusagen komplett durch 'husten2' ausgetauscht sein.
Ich sehe in meinem Codebsp. weit und breit keine open_point-Methode. :?:

husten gegen husten2?
Dann halt (husten2 gegen husten austauschen):

Code: Alles auswählen

husten = pickle.load(fileobj)
Ich glaube, wir reden an einander vorbei. Zumindest zielt die Frage nach der pickle-Logik als Methode darauf ab, daß Du die offensichtlich an der Klasse dranhaben willst. Das hast Du verneint, jetzt argumentierst Du aber mit einer dieser Methoden. :?:

Danke BlackJack!
Hier liegt wohl die Verständnisschwierigkeit. Meine Husten-Klasse sollte exemplarisch für irgendwas (picklebares) stehen, z.B. Deine Point-Klasse. Das Gepickle findet dann außerhalb statt, also nicht als Methode der Klasse selbst. Ist für mich transparenter, es sei denn, es gibt für Dich einen wichtigen Grund, daß an die Klasse zu flanschen, daher die Frage.
AntagonisT
User
Beiträge: 42
Registriert: Samstag 11. Juli 2009, 16:36

Ich versuche mal, mein eigentliches Vorhaben in Worten zu erklären, vielleicht wird dann auch klarer, worauf ich mit meinem Punktbeispiel hinauswollte:

Ich möche einen Koordinatensystem-Generator basteln:

1. Ich habe eine Klasse "Koordinatensystem", diese Koordinatenysteme haben Eigenschaften, die in der __init__-Methode der Klasse erst einmal auf Standardwerte gesetzt werden sollen:
- 2 Achsen, die eine Länge besitzen
- die Eigenschaft logarithmisch/linear
- Werte für Haupt- und Nebenintervalle
- eine Achsenbeschriftung
- Gitterlinien
- evtl. noch Linienstärken, Farben usw.. etc...


2. der User kann dann über Klassenmethoden:
- a) diese Eigenschaften oder Werte verändern (über ein noch nicht erstelltes GUI)
- b) diese Instanz dann in einer Datei speichern
- c) die Werte vielleicht weiterverändern
- d) zu einem späteren Zeitpunkt wieder die alten Werte aus einer Datei laden
- e) eine Postscript-Datei erstellen, also auch in eine Datei exportieren
- usw usw...


das ist so die grobe Skizze, bzw. ich habe da schon auch was gemacht und bin dann eben auf mein obiges Problem gestossen: Ich möchte meine Instanz durch das Laden sozusagen "austauschen".

Ich frage mich, ob es da jetzt notwendig ist, überhaupt Klassen einzusetzen, da in dem Programm sowieso immer nur eine Instanz existieren soll (zumindest stelle ich mir das so vor, ich wüsste nicht, wozu es mehrere Instanzen geben muss)

ps: Ich werde immer verwirrter, dieses Klassengedöns ist schon ein toller Hirnverrenker.. :oops:
BlackJack

@AntagonisT: Es gibt auf jeden Fall Elemente, von denen Du mehr als ein Exemplar hast. Punkte und Achsen zum Beispiel.

*Notwendig* ist es natürlich nicht mit Klassen zu arbeiten, denn es ging und geht in anderen Sprachen ja auch ohne, allerdings ist Python eine objektorientierte Programmiersprache und da sollte man schon überlegen Klassen zu verwenden, wo sie Sinn machen. Und ich denke hier machen sie durchaus Sinn. Man kann dann die Verantwortlichkeiten schön auf verschiedene Objekte verteilen.

Auch wenn es vom gesamten Koordinatensystem nur ein Exemplar zur gleichen Zeit geben sollte, macht es Sinn alle nötigen Daten und Funktionen zu einer Klasse zusammen zu fassen.

Bei 2. meintest Du übrigens sicher keine Klassenmethoden sondern nur Methoden, was im Allgemeinen die "Abkürzung" für Instanzmethoden ist. Klassenmethoden sind etwas anderes, die werden mit `classmethod()` erstellt.
AntagonisT
User
Beiträge: 42
Registriert: Samstag 11. Juli 2009, 16:36

BlackJack hat geschrieben:Auch wenn es vom gesamten Koordinatensystem nur ein Exemplar zur gleichen Zeit geben sollte, macht es Sinn alle nötigen Daten und Funktionen zu einer Klasse zusammen zu fassen.
Danke für deine Bestätigung, wenigstens der Ansatz schein dann ok zu sein?
BlackJack hat geschrieben:Bei 2. meintest Du übrigens sicher keine Klassenmethoden sondern nur Methoden, was im Allgemeinen die "Abkürzung" für Instanzmethoden ist. Klassenmethoden sind etwas anderes, die werden mit `classmethod()` erstellt.
Ja, genau, danke
Instanzen, Methoden, Funktionen, Module, Parameter, Variablen, Klassen, da kann man sich schon mal selbst verwirren :wink:
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

Da hast Du Dir ja einiges vorgenommen. Nur um Dich vor einem Anti-Pattern zu bewahren, es gibt matplotlib und Konsorten. ;) Als gutes Lernobjekt dient diese Aufgabe aber allemal.
Ich werde immer verwirrter, dieses Klassengedöns ist schon ein toller Hirnverrenker
OOP läßt sich intuitiv viel leichter verstehen, als es von den meisten Büchern hierzu suggeriert.

Mal eine kleine Analogie zu Deinem obigen Bsp:
Ein Auto(das Objekt) kann in einer Garage stehen. Aber schließt es sich selber dort ein (analog zum Punkt oben)? Nein. Gehört irgendwie nicht zur Zuständigkeit eines Autos. Gleichwohl könnte man es so bauen, mit Autopilot, Garagentürsteuerung etc.

Ebenso wenig macht es Sinn, jedem einzelnen Punkt diese Fähigkeit zu geben, zumal Du sehr viele dann "sich selber parken fahren" lassen müßtest (jeder Punkt picklet sich selbst). Intuitiv riecht das nach einer magischen dritten Hand, die das übernimmt, z.B. der Autoboy vor dem Hotel, der sich um das Wegbringen und Zurückholen des Autos kümmert. Oder in Deinem Falle eine zusätzliche Logik, die sich nur hierum kümmert. Wie komplex diese dann wiederum zu Werke geht, hängt davon ab, was sie alles können soll (Da ist von "einfachen" Hole-und-Bringe-Funktionen bis zu Modelklassen alles drin.)

Hoffentlich war das jetzt nicht zu viel Geschwafel ;)
AntagonisT
User
Beiträge: 42
Registriert: Samstag 11. Juli 2009, 16:36

jerch hat geschrieben:Als gutes Lernobjekt dient diese Aufgabe aber allemal.
Das hoffe ich auch. :wink:
Antworten