Anwendung von pickle und classmethods

Code-Stücke können hier veröffentlicht werden.
Antworten
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

Hi

hier mal etwas Pseudo-Code einer Anwendung des Pickleprotokols, weil schon einige Male Zweifel an dessen Nützlichkeit aufgetaucht sind ;-). Dies hier beschreibt das Pickling und Unpickling von Klasseninstanzen in Verbindung mit classmethod, was u.U. einige Zeilen Code sparen und mancher grauen Zelle das Leben retten kann (wie kann ich verdammtnochmal meine Klasse einfach initialisieren??? :?):

Code: Alles auswählen

import pickle

class SomeClass:
	def __init__(self, name, somedata):
		self.name = name
		self.somedata = somedata
		#und was man sonst noch so in __init__ machen kann

	def toFile(self, fname):
		pickle.dump(self, open(fname, 'wb'), 2) #binäres Picklingprotokoll

	#Python2.4 Syntax:
	@classmethod
        # oder @staticmethod , wenn 'cls' nicht gebraucht wird
	def fromFile(cls, fname):
		return pickle.load(open(fname, 'rb'))
              #das zurückgegebene Objekt entspricht der Klasseninstanz
              #weiteres Rummachen mit cls ist also unnötig
	#oder classmethod-Definition in Python2.3 Syntax
	#fromFile = classmethod(fromFile)
kann man folgendermaßen nutzen:

Code: Alles auswählen

from SomeClass import SomeClass
x = SomeClass(name="somename",somedata="somedata")
x.toFile('test.dat')
#raus aus Python und wieder rein
from SomeClass import SomeClass
x = SomeClass.fromFile('test.dat')
Na gut, das ist nicht die einzige Anwendung und es geht auch anders - aber es ist eine mögliche Anwendung, die, wie ich finde sehr, sehr praktisch ist. Für irgendjemanden hier vielleicht auch. Würde mich freuen.

Gruß,
Christian

edit: Vorschläge vom 27.10.07 eingearbeitet.
edit: nochmals korrigiert am 28.10.07
edit: Korregiert am 29.10.07
Zuletzt geändert von CM am Montag 29. Oktober 2007, 18:42, insgesamt 3-mal geändert.
gecko
User
Beiträge: 47
Registriert: Samstag 9. Juni 2007, 10:48

Danke für das Snippet.

Bei mir tauchte das Problem auf, dass das File im Binary mode geöffnet werden muss. Die Openbefehle sollten also mit 'wb' bzw. rb ergänzt werden.
BlackJack

Und wenn die `classmethod` mit dem ersten Argument nichts anstellt kann man es auch weglassen und eine `staticmethod` daraus machen.
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

BlackJack hat geschrieben:Und wenn die `classmethod` mit dem ersten Argument nichts anstellt kann man es auch weglassen und eine `staticmethod` daraus machen.
Das ist korrekt. Aber cls kann ganz praktisch sein, will man z. B. eine Versionskontrolle machen oder einen Signalhandler zufügen (siehe http://www.python-forum.de/viewtopic.php?p=76785 ). Da ich solche Snippets nur meist mit copy & paste hier reinsetze und Dinge selektiv rauskicke zur Vereinfachung, kann es zu solchen Unstimmigkeiten kommen. Das nur als Erklärung. Danke für die Klarstellung BlackJack.

@gecko: copy & paste ist wohl auch der Grund, warum da kein 'rb' stand. Sorry. Ich habe das mal für die Zukunft, damit nicht jeder alle Anmerkungen im Thread lesen muß, angepasst ...

Gruß,
Christian
gecko
User
Beiträge: 47
Registriert: Samstag 9. Juni 2007, 10:48

write bytes auch, oder nicht?
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

Nein, die gegenwärtige Version sollte jetzt aber stimmig sein *hust* ...
Siehe auch http://docs.python.org/lib/node317.html
BlackJack

Du musst sowohl beim Lesen als auch beim Schreiben mit 'rb' bzw. 'wb' öffnen! Egal bei welcher Protokollversion.
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

Das ist nicht korrekt. Das oben stehende Skript ist mittlerweile (tja, ...) getestet. Wichtig ist - und das steht auch so in der Doku - das das Fileobjekt eine write-Methode hat. Mehr ist nicht von Nöten. Protokoll 2 ist genauso zu verwenden, wie ich es gezeigt habe (jedenfalls ab Python > 2.4). Probiert es aus.

Konkret gemacht habe ich:

Code: Alles auswählen

import pickle

class Someclass:
    def __init__(self, name, somedata):
        self.name = name
        self.somedata = somedata
        #und was man sonst noch so in __init__ machen kann

    def tofile(self, fname):
        pickle.dump(self, open(fname, 'w'), 2) 

    @staticmethod
    def fromfile(fname):
        return pickle.load(open(fname))

x = Someclass('bla', 3)
x.tofile('dump.dat')
aufgerufen und dann

Code: Alles auswählen

In [1]: from test import Someclass

In [2]: x = Someclass.fromfile('dump.dat')

In [3]: x
Out[3]: <test.Someclass instance at 0xb78e3f6c>

In [4]: x.name
Out[4]: 'bla'

In [5]: x.somedata
Out[5]: 3
Unter Python 2.5.1 (Linux). Ist zwar redundant, aber ...
BlackJack

Binäre Dateien müssen immer mit 'b' geöffnet werden sonst kann Müll dabei herauskommen. Das Skript würde ich nicht als getestet ansehen. Insbesondere nicht wenn Linux verwendet wurde, weil hier 'b' oder nicht 'b' keinen Unterschied macht. Speicher mal ohne das 'b' unter Windows und versuch dass dann unter Linux zu laden oder umgekehrt. Oder wahlweise auch mit MacOS.

Auch das Pickle-"Textprotokoll" ist ein Binärprotokoll, das eben nur Bytewerte im ASCII-Bereich enthält. Es erwartet aber genau ein "Trennbyte" mit dem Wert 10, egal unter welchem Betriebssystem! Wenn es etwas anderes bekommt, funktioniert das entpicklen nicht mehr.
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

Jepp, irgendwie erinnere ich mich denselben Fehler schon einmal gemacht zu haben. Peinlich das.

Danke nochmals,
Christian
Antworten