Daten verloren. © Bernd Klein

Daten verloren. © Bernd Klein

Was passiert, wenn man ein Programm beendet, sei es, dass dies gewollt ist, oder dass es durch einen Fehler abgebrochen wird?

Das Programmende bedeutet auch das Ende der Lebenszeit für die verwendeten Variablen, d.h. die Objekte, die von den Variablen referenziert werden, sind eigentlich für immer verloren. Wir sagen „eigentlich“, denn vielleicht hatte sie ja jemand gedumpt, d.h. in einer Datei gepeichert. Damit stehen sie bei einem erneuten Programmlauf wieder zur Verfügung, denn man kann sie ja wieder einlesen.

 

Daten, die auch über einen Programmlauf hinaus weiter bestehen können, bezeichnet man übrigens als persistente Daten im Gegensatz zu den transienten oder flüchtigen Daten eines Programmes.

Um unsere Daten vor einem Programmende vor Verlust zu retten, gibt es prinzipiell drei Möglichkeiten:

  • Manuelle Speicherung in einer Datei
    Wenn die im Programm verwendeten Datenstrukturen komplex sind, könnte sich dies als schwierig und fehlerträchtig erweisen. Das Problem liegt darin, dass man sich selbst eine Strategie überlegen muss, wie man die Daten abspeichern und später wieder einlesen kann. ie das pickle-Modul. Wir werden dieses Modul jedoch nicht behandeln, da allgemein aus verschiedenen Gründen empfohlen wird pickle zu verwenden.
  • pickle-Modul
    Wir werden im folgenden näher auf dieses Modul eingehen.
  • shelve-Modul
    Shelve baut auf pickle auf und bietet eine Dictionary-ähnliche Struktur

Pickle-Modul

Mixed-Pickles, manchmal auch nur Pickles genannt, sind eine Beilage, die sauer eingelegt ist. Doch was haben kleine Gurken, Zwiebeln, Maiskölbchen, Blumenkohl und so weiter mit Python und der Programmierung in Python zu tun? MAn ahnt es: So wie man diverse Gemüseteile für die Zukunft einlegt oder pökelt, so dient das pickle-Modul dazu, verschiedenste Sorten von Daten für erneuten späteren Gebrauch über Programmgrenzen und Programme hinweg abzuspeichern. Objekte werden mit Pickle in einer Form serialisiert abgespeichern, dass sie später wieder deserialisiert werden können.
Zum Abspeichern dient die Methode dump, die in ihrer allgemeinen Syntax wie folgt aussieht:

pickle.dump(obj, file[,protocol, *, fix_imports=True])

dump() schreibt eine gepickelte Darstellung des Objectes obj in das Dateiobjekt file. Das optionale Argument für das Protokollargument steuert die Art der Ausgabe:

  • Protokollversion 0 ist die originale Ablageart von Python, d.h. vor Python3. Dabei handelt es sich um ein für Menchen lesbares Format. Dieser Modus ist abwärts kompatibel mit früheren Pythonversionen.
  • Protokollversion 1 benutzt das alte Binärformat. Dieser Modus ist ebenfalls abwärts kompatibel mit früheren Pythonversionen.
  • Protokollversion 2 wurde mit 2.3. eingeführt. Es stellt ein effizienteres „Pickling“ zur Verfügung.
  • Protokollversion 3 wurde mit Python 3.0 eingeführt. Dieser Modus bietet einen hervorragenden Modus Bytes. Dateien, die in diesem Modus erzeugt werden, können nicht mehr in Python 2.x mit dem zugehörigen Pickle-Modul bearbeitet werden. Es handelt sich hierbei um den von Python 3.x empfohlenen Modus. Er stellt auch den Defaultwert dar.

Ein einfaches Beispiel:

>>> import pickle
>>> programmiersprachen = ["Python", "Perl", "C++", "Java"]
>>> fh = open("sprachen.pkl","bw")
>>> pickle.dump(programmiersprachen,fh)
>>> fh.close()
>>> 

Objekte, die mit pickle.dump() in eine Datei geschrieben worden sind, können mit der Pickle-Methode pickle.load(file) wieder eingelesen werden. pickle.load erkennt automatisch in welchem Format eine Datei erstellt worden ist. Die Datei data.pkl kann wieder in Python eingelesen werden. Dies kann in der gleichen Session oder dem gleichen Programm geschehen, aber meistens werden die Daten natürlich in einem erneuten Programmlauf oder in einem anderen Programm eingelen. Wir zeigen die Funktionsweise des Einlesens im folgenden Beispiel, einer neu gestarteten interaktiven Python-Session:

$ python3
Python 3.4.0 (default, Apr 11 2014, 13:05:11) 
[GCC 4.8.2] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import pickle
>>> f = open("sprachen.pkl","rb")
>>> sprachen = pickle.load(f)
>>> print(sprachen)
['Python', 'Perl', 'C++', 'Java']
>>> 

Da nur die Objekte selbst und nicht ihre Namen gespeichert worden sind, müssen wir das Ergebnis von pickle.load() wieder einer Variablen, – wie in obigem Beispiel gezeigt, – zuweisen.

Es stellt sich die Frage, welche Art von Daten man eigentlich pickeln kann? Im Prinzip alles, was man sich vorstellen kann:

  • Alle Python-Basistypen, wie Booleans, Integers, Floats, Komplexe Zahlen, Strings, Byte-Arrays und Byte-Objekte und natürlich None.
  • Listen und Tupels, Mengen und Dictionaries
  • Beliebige Kombinationen der beiden vorigen Gruppen
  • Außerdem können auch Funktionen, Klassen und Instanzen unter gewissen Bedingungen gepickelt werden.
  • file-Objekte lassen sich nicht pickeln.

In unserem einfachen Beispiel haben wir ein Objekt gepickelt, d.h. eine Liste von Programmiersprachen. Wie sieht es aber aus, wenn wir mehrere Objekte gleichzeitig pickeln wollen? Ganz einfach: Man verpackt sie in ein Objekt, meistens ein Tupel. Im folgenden Beispiel erzeugen wir zwei Listen „programmiersprachen“ und „python_dialekte“:

>>> import pickle
>>> fh = open("data.pkl","bw")
>>> programmiersprachen = ["Python", "Perl", "C++", "Java", "Lisp"]
>>> python_dialekte = ["Jython", "IronPython", "CPython"]
>>> pickle_object = (programmiersprachen, python_dialekte)
>>> pickle.dump(pickle_object,fh)
>>> fh.close()

Obige gepickelte Datei können wir so einlesen, dass wir die beiden Listen wieder trennen können:

>>> import pickle
>>> f = open("data.pkl","rb")
>>> (sprachen, dialekte) = pickle.load(f)
>>> print(sprachen, dialekte)
['Python', 'Perl', 'C++', 'Java', 'Lisp'] ['Jython', 'IronPython', 'CPython']
>>> 

shelve-Modul

Hat man das shelve-Modul importiert und beginnt die englische Beschreibung unter help(shelve) zu lesen, wundert man sich vielleicht über die Rechtschreibung. Dort steht: ,,A ’shelf‘ is a persistent, dictionary-like object.“ Wir haben also ein Modul, was sie ,,shelve“ nennt und ein dictionary-ähnliches Objekt, was ,,shelf“ geschrieben wird. Es handelt sich nicht um einen Verschreiber. Das Wort ,,shelf“ ist ein Substantiv und bedeutet,,Regal“ oder ,,Ablage“, während ,,shelve“ als Verb benutzt wird und meistens bedeutet, dass man etwas in ein Regal stellt. Nach dieser kurzen englischen Grammatikauffrischung sind wir auch schon der Aufgabe des shelve-Modules näher gekommen. shelve funktioniert wie ein Bücherregal, nur dass wir statt ,,Büchern“ Daten haben und unser Regal eine Datei ist.

Prinzipiell verhält sich ein shelf-Objekt wie ein Python-Dictionary. Allerdings gibt es zwei wesentliche Unterschiede. Bevor man ein shelf benutzen kann, muss man es öffnen, d.h. man öffnet die Datei, und nach der Benutzung muss man die Datei wieder schließen. Außerdem gibt es eine Einschränkung bzgl. der Schlüssel, die man verwenden kann. Während ein Python-Dictionary beliebige unveränderliche Datentypen als Keys verwenden kann, also beispielsweise Integer, Floats, Strings und Tupel, kann ein shelf nur Strings als Keys verwenden. Dies ist allerdings nur eine geringe Beschränkung der Nutzbarkeit. Für die Werte gibt es allerdings keine Einschränkungen.

Das Shelve-Modul lässt sich sehr einfach benutzen. Man importiert zuerst das Modul selbst, und dann öffnet man eine Datei mit dem zu verwendenden Shelf-Objekt unter Verwendung der shelve-Methode ,,open“:

>>> import shelve
>>> s = shelve.open("MyShelve")

Falls bereits eine Datei ,,MyShelve“ existiert, versucht die open-Methode diese zu öffnen. Falls es sich um eine Datei handelt, die nicht unter Verwendung des shelve-Modules angelegt worden ist, erfolgt eine Fehlermeldung, da es sich dann nicht um Daten im korrekten Format handelt. Falls die Datei noch nicht existiert, wird eine Datei im korrekten Format angelegt.

Jetzt kann man mit s arbeiten wie mit einem Dictionary:

>>> s["street"] = "Fleet Str"
>>> s["city"] = "London"
>>> for key in s:
...     print(key)
... 
city
street

Bevor man das Programm verlässt, in dem man mit einem shelf-Objekt arbeitet, sollte man das shelf-Objekt wieder schließen.

>>> s.close()

Wir können nun die Shelf-Datei in einem beliebigen Python-Programm oder in der interaktiven Shell wieder benutzen und die alten Daten stehen sofort zur Verfügung:

$ python3
Python 3.2.3 (default, Feb 28 2014, 00:22:33) 
[GCC 4.7.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import shelve
>>> s = shelve.open("MyShelve")
>>> s["street"]
'Fleet Str'
>>> s["city"]
'London'
>>> 

Man kann ein shelve-Objekt auch einfach mit dem dict-Casting-Operator in ein Dictionary wandeln:

>>> s
≤shelve.DbfilenameShelf object at 0xb7133dcc>
>>> dict(s)
{'city': 'London', 'street': 'Fleet Str'}
>>> 

Im folgenden Beispiel demonstrieren wir, dass wir in einem Shelf-Eintrag auch komplexe Objekte, wie beispielsweise Dictionaries abspeichern können:

>>> import shelve
>>> tele = shelve.open("MyPhoneBook")
>>> tele["Mike"] = {"first":"Mike", "last":"Miller", "phone":"4689"}
>>> tele["Steve"] = {"first":"Stephan", "last":"Burns", "phone":"8745"}
>>> tele["Eve"] = {"first":"Eve", "last":"Naomi", "phone":"9069"}
>>> tele["Eve"]["phone"]
'9069'

Die Daten sind auch in diesem Fall persistent gespeichert, d.h. man kann Sie auch wieder in einem späteren Programmlauf benutzen. Alles was man tun muss, ist die Datei MyPhoneBook zu öffnen:

$ python3
Python 3.2.3 (default, Feb 28 2014, 00:22:33) 
[GCC 4.7.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import shelve
>>> tele = shelve.open("MyPhoneBook")
>>> tele["Steve"]["phone"]
'8745'
>>> 

 

Ich freue mich über Ihre Kommentare & Fragen!

 

Klein, Einführung in Python 3, 2.A.Mehr zu diesem und anderen Themen zu Python 3 finden Sie in der neuen Ausgabe von Bernd Klein’s Buch Einführung in Python 3.

Mehr zum Thema Dateien lesen und schreiben finden Sie auch auf der Webseite von Bernd Klein python-kurs.eu.