7. Eingabe und Ausgabe
**********************

Es gibt verschiedene Möglichkeiten, die Ausgabe eines Programms
darzustellen; Daten können in einer für Menschen lesbaren Form
ausgegeben oder zur späteren Verwendung in eine Datei geschrieben
werden. In diesem Kapitel werden einige dieser Möglichkeiten
behandelt.


7.1. Erweiterte Formatierungsmöglichkeiten für die Ausgabe
==========================================================

Bisher haben wir zwei Möglichkeiten kennengelernt, Werte zu schreiben:
*Ausdrucksanweisungen* und die Funktion "print()".  (Eine dritte
Möglichkeit ist die Verwendung der Methode "write()" von Datei-
Objekten. Auf die Standardausgabedatei kann als "sys.stdout" verwiesen
werden. Weitere Informationen hierzu findest du in der
Bibliotheksreferenz.)

Oftmals möchtest du mehr Kontrolle über die Formatierung Ihrer Ausgabe
haben, als nur durch Leerzeichen getrennte Werte auszugeben. Es gibt
verschiedene Möglichkeiten, die Ausgabe zu formatieren.

* Um formatierte Zeichenfolgenliterale zu verwenden, beginne eine
  Zeichenfolge mit "f" oder "F" vor dem öffnenden Anführungszeichen
  oder dem dreifachen Anführungszeichen. Innerhalb dieser Zeichenfolge
  kannst du einen Python-Ausdruck zwischen den Zeichen "{" und "}"
  schreiben, der sich auf Variablen oder Literalwerte beziehen kann.

     >>> year = 2016
     >>> event = 'Referendum'
     >>> f'Ergebnisse des {year} {event} '
     'Ergebnisse des Referendums von 2016'

* Die Methode "str.format()" für Zeichenketten erfordert mehr
  manuellen Aufwand. Verwende weiterhin  "{" und "}", um zu
  kennzeichnen, an welcher Stelle eine Variable eingefügt werden soll,
  und können detaillierte Formatierungsanweisungen angeben, müssen
  jedoch zusätzlich die zu formatierenden Informationen bereitstellen.
  Im folgenden Codeblock findest du zwei Beispiele für die
  Formatierung von Variablen:

     >>> yes_votes = 42_572_654
     >>> total_votes = 85_705_149
     >>> prozent = Ja-Stimmen / Gesamtstimmen
     >>> '{:-9} Ja-Stimmen  {:2,2 %}'.format(Ja-Stimmen, prozent)
     ' 42.572.654 Ja-Stimmen  49,67 %'

  Beachte, dass die  "yes_votes" nur bei negativen Zahlen mit
  Leerzeichen und einem Minuszeichen aufgefüllt werden. Das Beispiel
  gibt außerdem  "percentage" multipliziert mit 100 aus, mit zwei
  Dezimalstellen und einem Prozentzeichen am Ende (weitere
  Informationen findest du unter Formatspezifikation Mini-Sprache ).

* Schließlich kannst du die gesamte Zeichenfolgenbearbeitung selbst
  übernehmen, indem du Zeichenfolgenausschnitte und
  Verkettungsoperationen nutzen tust, um jedes erdenkliche Layout zu
  erstellen. Der Typ "Zeichenfolge" verfügt über einige Methoden, mit
  denen sich Zeichenfolgen auf eine bestimmte Spaltenbreite auffüllen
  lassen.

Wenn du keine aufwendige Ausgabe benötigst, sondern lediglich eine
schnelle Anzeige einiger Variablen zu Debugging-Zwecken wünschen,
kannst du jeden Wert mit den Funktionen  "repr()" oder "str()" in eine
Zeichenkette umwandeln.

Die Funktion  "str()" soll Darstellungen von Werten zurückgeben, die
für Menschen gut lesbar sind, während  "repr()" dazu dient,
Darstellungen zu generieren, die vom Interpreter gelesen werden können
(oder einen "SyntaxError" auslösen, falls keine entsprechende Syntax
vorhanden ist).  Bei Objekten, für die es keine spezielle Darstellung
für Menschen gibt, gibt "str()" denselben Wert zurück wie "repr()".
Viele Werte, wie beispielsweise Zahlen oder Strukturen wie Listen und
Wörterbücher, werden von beiden Funktionen auf dieselbe Weise
dargestellt. Insbesondere Zeichenketten haben zwei unterschiedliche
Darstellungen.

Einige Beispiele:

   >>> s = 'Hallo, Welt.'
   >>> str(s)
   'Hallo, Welt.'
   >>> repr(s)
   "'Hallo, Welt.'"
   >>> str(1/7)
   '0,14285714285714285'
   >>> x = 10 * 3,25
   >>> y = 200 * 200
   >>> s = 'Der Wert von x ist ' + repr(x) + ', und y ist ' + repr(y) + '...'
   >>> print(s)
   Der Wert von x ist 32,5 und der von y ist 40000...
   >>> # Die Funktion repr() fügt bei einer Zeichenkette Anführungszeichen und Backslashes hinzu:
   >>> hello = 'hello, world\n'
   >>> hellos = repr(hello)
   >>> print(hellos)
   'hello, world\n'
   >>> # Das Argument für repr() kann ein beliebiges Python-Objekt sein:
   >>> repr((x, y, ('spam', 'eggs')))
   "(32.5, 40000, ('spam', 'eggs'))"

The "string" module contains a "Template" class that offers yet
another way to substitute values into strings, using placeholders like
"$x" and replacing them with values from a dictionary, but offers much
less control of the formatting.


7.1.1. Formatierte Zeichenfolgenliterale
----------------------------------------

Mit formatierten String-Literalen (kurz auch als f-Strings bezeichnet)
kannst du den Wert von Python-Ausdrücken in einen String einfügen,
indem du dem String die Präfixe "f" oder "F" voranstellen und
Ausdrücke in der Form "{expression}" schreibst.

Dem Ausdruck kann ein optionaler Formatbezeichner folgen. Dies
ermöglicht eine genauere Steuerung der Formatierung des Werts. Im
folgenden Beispiel wird Pi auf drei Stellen nach dem Komma gerundet:

   >>> import math
   >>> print(f'Der Wert von Pi beträgt ungefähr {math.pi:.3f}.')
   Der Wert von Pi beträgt ungefähr 3,142.

Wird nach dem Befehl  "':'" eine Ganzzahl übergeben, wird die Breite
dieses Feldes auf die angegebene Mindestanzahl an Zeichen festgelegt.
Dies ist nützlich, um Spalten auszurichten.

   >>> table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 7678}
   >>> for name, phone in table.items():
   ...     print(f'{name:10} ==> {phone:10d}')
   ...
   Sjoerd     ==>       4127
   Jack       ==>       4098
   Dcab       ==>       7678

Es können weitere Modifikatoren verwendet werden, um den Wert vor der
Formatierung umzuwandeln.  "'!a'" wendet "ascii()" an,  "'!s'" wendet
"str()" an und "'!r'" wendet "repr()" an

   >>> animals = 'Aale'
   >>> print(f'Mein Luftkissenfahrzeug ist voll mit {animals}.')
   Mein Luftkissenfahrzeug ist voll mit Aalen.
   >>> print(f'Mein Luftkissenfahrzeug ist voll mit {animals!r}.')
   Mein Luftkissenfahrzeug ist voll mit 'Aalen'.

Mit dem Bezeichner  "=" lässt sich ein Ausdruck so erweitern, dass er
zunächst den Text des Ausdrucks, dann ein Gleichheitszeichen und
anschließend die Darstellung des ausgewerteten Ausdrucks enthält:

>>> bugs = 'roaches'
>>> count = 13
>>> area = 'living room'
>>> print(f'Debugging {bugs=} {count=} {area=}')
Debugging bugs='roaches' count=13 area='living room'

Weitere Informationen zum Spezifizierer  "=" findest du unter
selbstdokumentierende Ausdrücke. Eine Übersicht über diese
Formatvorgaben findest du im Referenzhandbuch unter
Formatspezifikation Mini-Sprache.


7.1.2. Die String-Methode format()
----------------------------------

Die grundlegende Verwendung der Methode  "str.format()" sieht wie
folgt aus:

   >>> print('Wir sind die {}, die "{}!" sagen'.format('Ritter', 'Ni'))
   Wir sind die Ritter, die "Ni!" sagen

Die Klammern und die darin enthaltenen Zeichen (sogenannte
Formatfelder) werden durch die an die Methode  "str.format()"
übergebenen Objekte ersetzt. Eine Zahl in den Klammern kann verwendet
werden, um auf die Position des an die Methode  "str.format()"
übergebenen Objekts zu verweisen.

   >>> print('{0} und {1}'.format('Spam', 'Eier'))
   Spam und Eier
   >>> print('{1} und {0}'.format('Spam', 'Eier'))
   Eier und Spam

Werden in der Methode  "str.format()" Schlüsselwortargumente
verwendet, wird auf deren Werte über den Namen des Arguments
verwiesen.

   >>> print('Diese {food} lautet {adjective}.'.format(
   ...       food='Spam', adjective='absolut schrecklich'))
   Dieser Spam ist absolut schrecklich.

Positions- und Schlüsselwortargumente können beliebig kombiniert
werden:

   >>> print('Die Geschichte von {0}, {1} und {other}.'.format('Bill', 'Manfred',
   ...                                                    other='Georg'))
   Die Geschichte von Bill, Manfred und Georg.

Wenn du eine wirklich lange Formatzeichenfolge hast, die du nicht
aufteilen möchtest, wäre es praktisch, wenn du die zu formatierenden
Variablen nicht anhand ihrer Position, sondern anhand ihres Namens
referenzieren könntest. Dies lässt sich erreichen, indem du einfach
das Wörterbuch übergibst und eckige Klammern "'[]'" verwendest, um auf
die Schlüssel zuzugreifen.

   >>> table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 8637678}
   >>> print('Jack: {0[Jack]:d}; Sjoerd: {0[Sjoerd]:d}; '
   ...       'Dcab: {0[Dcab]:d}'.format(table))
   Jack: 4098; Sjoerd: 4127; Dcab: 8637678

Dies könnte auch erreicht werden, indem das Wörterbuch  "table" als
Schlüsselwortargumente mit der Notation  "**" übergeben wird.

   >>> table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 8637678}
   >>> print('Jack: {Jack:d}; Sjoerd: {Sjoerd:d}; Dcab: {Dcab:d}'.format(**table))
   Jack: 4098; Sjoerd: 4127; Dcab: 8637678

Dies ist besonders nützlich in Kombination mit der integrierten
Funktion "vars()", die ein Wörterbuch zurückgibt, das alle lokalen
Variablen enthält:

   >>> table = {k: str(v) for k, v in vars().items()}
   >>> message = " ".join([f'{k}: ' + '{' + k + '};' for k in table.keys()])
   >>> print(message.format(**table))
   __name__: __main__; __doc__: None; __package__: None; __loader__: ...

Die folgenden Zeilen erzeugen beispielsweise eine übersichtlich
ausgerichtete Spaltenreihe, in der Ganzzahlen sowie deren Quadrate und
Kubikzahlen aufgeführt sind:

   >>> for x in range(1, 11):
   ...     print('{0:2d} {1:3d} {2:4d}'.format(x, x*x, x*x*x))
   ...
    1   1    1
    2   4    8
    3   9   27
    4  16   64
    5  25  125
    6  36  216
    7  49  343
    8  64  512
    9  81  729
   10 100 1000

Einen vollständigen Überblick über die Zeichenfolgenformatierung mit
"str.format()" findest du unter Syntax für Formatzeichenketten.


7.1.3. Manuelle Formatierung von Zeichenfolgen
----------------------------------------------

Hier ist dieselbe Tabelle mit Quadraten und Würfeln, die manuell
formatiert wurde:

   >>> for x in range(1, 11):
   ...     print(repr(x).rjust(2), repr(x*x).rjust(3), end=' ')
   ...     # Beachte die Verwendung von 'end' in der vorherigen Zeile
   ...     print(repr(x*x*x).rjust(4))
   ...
    1   1    1
    2   4    8
    3   9   27
    4  16   64
    5  25  125
    6  36  216
    7  49  343
    8  64  512
    9  81  729
   10 100 1000

(Beachte, dass das einzelne Leerzeichen zwischen den einzelnen Spalten
durch die Funktionsweise von  "print()" hinzugefügt wurde: Diese
Funktion fügt immer Leerzeichen zwischen ihren Argumenten ein.)

Die Methode  "str.rjust()" von String-Objekten richtet eine
Zeichenkette in einem Feld mit einer bestimmten Breite rechtsbündig
aus, indem sie links mit Leerzeichen aufgefüllt wird. Es gibt ähnliche
Methoden:  "str.ljust()" und "str.center()". Diese Methoden schreiben
nichts, sondern geben lediglich eine neue Zeichenkette zurück. Ist die
Eingabezeichenkette zu lang, wird sie nicht gekürzt, sondern
unverändert zurückgegeben; dies bringt zwar das Spaltenlayout
durcheinander, ist aber in der Regel besser als die Alternative, bei
der ein Wert falsch dargestellt würde. (Wenn du wirklich eine Kürzung
wünschst, kannst du jederzeit eine Slice-Operation hinzufügen, wie in
"x.ljust(n)[:n]" beschrieben.)

Es gibt noch eine weitere Methode, "str.zfill()", die eine numerische
Zeichenkette links mit Nullen auffüllt. Sie erkennt Plus- und
Minuszeichen:

   >>> '12'.zfill(5)
   '00012'
   >>> '-3.14'.zfill(7)
   '-003,14'
   >>> '3,14159265359'.zfill(5)
   '3,14159265359'


7.1.4. Alte Zeichenfolgenformatierung
-------------------------------------

Der %-Operator (Modulo) kann auch zur Formatierung von Zeichenketten
verwendet werden. Bei der Angabe von  "format % values" (wobei
*format* eine Zeichenkette ist) werden die
Konvertierungsspezifikationen  "%" in *format* durch null oder mehr
Elemente aus *values* ersetzt. Dieser Vorgang wird allgemein als
Zeichenketteninterpolation bezeichnet. Zum Beispiel:

   >>> import math
   >>> print('Der Wert von Pi beträgt ungefähr %5.3f.' % math.pi)
   Der Wert von Pi beträgt ungefähr 3,142.

Weitere Informationen findest du im Abschnitt  String-Formatierung im
printf-Stil.


7.2. Dateien lesen und schreiben
================================

"open()" gibt ein *Dateiobjekt* zurück und wird meist mit zwei
Positionsargumenten und einem Schlüsselwortargument verwendet:
"open(filename, mode, encoding=None)"

   >>> f = open('workfile', 'w', encoding="utf-8")

Das erste Argument ist eine Zeichenkette, die den Dateinamen enthält.
Das zweite Argument ist eine weitere Zeichenkette, die einige Zeichen
enthält, die beschreiben, wie die Datei verwendet wird.  *mode* kann
"'r'" lauten, wenn die Datei nur gelesen wird,  "'w'" für das reine
Schreiben (eine bereits vorhandene Datei mit demselben Namen wird
dabei gelöscht) und "'a'" öffnet die Datei zum Anhängen; alle in die
Datei geschriebenen Daten werden automatisch am Ende angehängt. "'r+'"
öffnet die Datei sowohl zum Lesen als auch zum Schreiben. Das Argument
*mode* ist optional; wird es weggelassen, wird "'r'" angenommen.

Normalerweise werden Dateien im *Textmodus* geöffnet, das heißt, du
liest Zeichenketten aus der Datei aus und schreibst Zeichenketten in
die Datei, die in einer bestimmten *Kodierung* kodiert sind. Wenn
keine *Kodierung* angegeben ist, ist die Standardeinstellung
plattformabhängig (siehe "open()"). Da UTF-8 der moderne De-facto-
Standard ist, wird "encoding="utf-8"" empfohlen, es sei denn, du
weisst, dass du eine andere Kodierung verwenden musst. Durch
Hinzufügen eines "'b'" zum Modus wird die Datei im *Binärmodus*
geöffnet. Daten im Binärmodus werden als "bytes" -Objekte gelesen und
geschrieben. Beim Öffnen einer Datei im Binärmodus kannst du keine
*Kodierung* angeben.

Im Textmodus werden beim Lesen standardmäßig plattformspezifische
Zeilenenden ("\n" unter Unix, "\r\n" unter Windows) in "\n"
umgewandelt. Beim Schreiben im Textmodus werden "\n" standardmäßig
wieder in plattformspezifische Zeilenenden umgewandelt.  Diese im
Hintergrund vorgenommene Änderung der Dateidaten ist für Textdateien
unbedenklich, führt jedoch bei Binärdateien wie den in den Formaten
"JPEG" oder "EXE" zu Datenverlusten.  Achte daher unbedingt darauf,
beim Lesen und Schreiben solcher Dateien den Binärmodus zu verwenden.

Es hat sich bewährt, beim Umgang mit Datei-Objekten das Schlüsselwort
"with" zu verwenden. Der Vorteil besteht darin, dass die Datei nach
Abschluss der Suite ordnungsgemäß geschlossen wird, selbst wenn an
irgendeiner Stelle eine Ausnahme ausgelöst wird. Die Verwendung von
"with" ist zudem wesentlich kürzer als das Schreiben der
entsprechenden "try"-"finally"-Blöcke:

   >>> with open('workfile', encoding="utf-8") as f:
   ...     read_data = f.read()

   >>> # Wir können überprüfen, ob die Datei automatisch geschlossen wurde.
   >>> f.closed
   True

Wenn du das Schlüsselwort  "with" nicht verwendest, solltest du die
Funktion "f.close()" aufrufen, um die Datei zu schließen und die von
ihr belegten Systemressourcen sofort freizugeben.

Warnung:

  Der Aufruf von "f.write()" ohne das Schlüsselwort "with" ` oder der
  Aufruf von "f.close()" **kann** dazu führen, dass die Argumente von
  "f.write()" nicht vollständig auf die Festplatte geschrieben werden,
  selbst wenn das Programm erfolgreich beendet wird.

Nachdem ein Dateiobjekt geschlossen wurde – entweder durch eine
"with"-Anweisung oder durch den Aufruf von "f.close()"– schlagen alle
Versuche, das Dateiobjekt zu verwenden, automatisch fehl.

   >>> f.close()
   >>> f.read()
   Traceback (letzter Aufruf zuletzt):
     Datei "<stdin>", Zeile 1, in <module>
   ValueError: E/A-Vorgang an einer geschlossenen Datei.


7.2.1. Methoden von Datei-Objekten
----------------------------------

In den übrigen Beispielen dieses Abschnitts wird davon ausgegangen,
dass bereits ein Dateiobjekt namens  "f" angelegt wurde.

Um den Inhalt einer Datei zu lesen, rufe "f.read(size)" auf. Diese
Funktion liest eine bestimmte Datenmenge ein und gibt sie als
Zeichenkette (im Textmodus) oder als Byte-Objekt (im Binärmodus)
zurück. *size* ist ein optionales numerisches Argument. Wenn *size*
weggelassen wird oder negativ ist, wird der gesamte Inhalt der Datei
gelesen und zurückgegeben; es ist Ihr Problem, wenn die Datei doppelt
so groß ist wie der Arbeitsspeicher Ihres Rechners. Andernfalls werden
höchstens *size* Zeichen (im Textmodus) oder *size* Bytes (im
Binärmodus) gelesen und zurückgegeben. Wenn das Ende der Datei
erreicht wurde, gibt "f.read()" eine leere Zeichenkette zurück ("''").

   >>> f.read()
   'Das ist der gesamte Inhalt der Datei.\n'
   >>> f.read()
   ''

"f.readline()" Liest eine einzelne Zeile aus der Datei; am Ende der
Zeichenkette wird ein Zeilenumbruchzeichen ("\n") belassen, das nur in
der letzten Zeile der Datei weggelassen wird, wenn die Datei nicht mit
einem Zeilenumbruch endet.  Dadurch ist der Rückgabewert eindeutig:
Wenn  "f.readline()" eine leere Zeichenkette zurückgibt, ist das Ende
der Datei erreicht, während eine Leerzeile durch  "'\n'" dargestellt
wird, eine Zeichenkette, die nur ein einziges Zeilenendezeichen
enthält.

   >>> f.readline()
   'Dies ist die erste Zeile der Datei.\n'
   >>> f.readline()
   'Zweite Zeile der Datei\n'
   >>> f.readline()
   ''

Um Zeilen aus einer Datei zu lesen, kannst du das Dateiobjekt in einer
Schleife durchlaufen. Dies ist speichereffizient, schnell und führt zu
einfachem Code:

   >>> for line in f:
   ...     print(line, end='')
   ...
   Dies ist die erste Zeile der Datei.
   Zweite Zeile der Datei

Wenn du alle Zeilen einer Datei in eine Liste einlesen möchtest,
kannst du auch "list(f)" oder "f.readlines()" verwenden.

"f.write(string)" Schreibt den Inhalt von *string* in die Datei und
gibt die Anzahl der geschriebenen Zeichen zurück.

   >>> f.write('Das ist ein Test\n')
   15

Andere Objekttypen müssen vor dem Schreiben konvertiert werden –
entweder in eine Zeichenkette (im Textmodus) oder in ein Byte-Objekt
(im Binärmodus):

   >>> value = ('die Antwort', 42)
   >>> s = str(value)  # das Tupel in eine Zeichenkette umwandeln
   >>> f.write(s)
   18

"f.tell()" Gibt eine Ganzzahl zurück, die die aktuelle Position des
Dateiobjekts in der Datei angibt – im Binärmodus als Anzahl der Bytes
ab dem Dateianfang und im Textmodus als undurchsichtige Zahl.

Um die Position des Dateiobjekts zu ändern, verwende "f.seek(offset,
whence)". Die Position wird berechnet, indem *offset* zu einem
Referenzpunkt addiert wird; der Referenzpunkt wird durch das Argument
*whence* festgelegt.  Ein *whence*-Wert von 0 misst vom Anfang der
Datei aus, 1 verwendet die aktuelle Dateiposition und 2 verwendet das
Ende der Datei als Bezugspunkt. *whence* kann weggelassen werden und
ist standardmäßig auf 0 gesetzt, wobei der Anfang der Datei als
Bezugspunkt verwendet wird.

   >>> f = open('workfile', 'rb+')
   >>> f.write(b'0123456789abcdef')
   16
   >>> f.seek(5)      # Zum 6. Byte in der Datei springen
   5
   >>> f.read(1)
   b'5'
   >>> f.seek(-3, 2)  # Zum dritten Byte vor dem Ende springen
   13
   >>> f.read(1)
   b'd'

In Textdateien (die ohne  "b" in der Moduszeichenfolge geöffnet
wurden) sind nur relative Sprünge zum Dateianfang zulässig (mit
Ausnahme des Sprungs zum Dateiende mit  "seek(0, 2)"), und die
einzigen gültigen *offset*-Werte sind diejenigen, die von "f.tell()"
zurückgegeben werden, oder Null. Jeder andere *offset*-Wert führt zu
undefiniertem Verhalten.

Dateiobjekte verfügen über einige zusätzliche Methoden, wie
beispielsweise  "isatty()" und "truncate()", die jedoch seltener
verwendet werden; eine vollständige Anleitung zu Dateiobjekten findest
du in der Bibliotheksreferenz.


7.2.2. Strukturierte Daten speichern mit "json"
-----------------------------------------------

Zeichenketten lassen sich problemlos in eine Datei schreiben und aus
einer Datei lesen.  Bei Zahlen ist der Aufwand etwas größer, da die
Methode "read()" nur Zeichenketten zurückgibt, die an eine Funktion
wie "int()" übergeben werden müssen, die eine Zeichenkette wie "'123'"
entgegennimmt und deren numerischen Wert 123 zurückgibt. Wenn du
komplexere Datentypen wie verschachtelte Listen und Wörterbücher
speichern möchtest, wird das manuelle Parsen und Serialisieren
kompliziert.

Anstatt dass Benutzer ständig Code schreiben und debuggen müssen, um
komplizierte Datentypen in Dateien zu speichern, ermöglicht Python die
Verwendung des beliebten Datenaustauschformats namens JSON (JavaScript
Object Notation).  Das Standardmodul namens "json" kann Python-
Datenhierarchien übernehmen und in Zeichenfolgen-Darstellungen
umwandeln; dieser Vorgang wird als *Serialisierung* bezeichnet.  Das
Rekonstruieren der Daten aus der Zeichenfolgenrepräsentation wird als
*Deserialisierung* bezeichnet. Zwischen der Serialisierung und der
Deserialisierung kann die das Objekt repräsentierende Zeichenfolge in
einer Datei oder einem Datenspeicher abgelegt oder über eine
Netzwerkverbindung an einen entfernten Rechner gesendet worden sein.

Bemerkung:

  Das JSON-Format wird häufig von modernen Anwendungen für den
  Datenaustausch verwendet. Viele Programmierer sind bereits damit
  vertraut, was es zu einer guten Wahl für die Interoperabilität
  macht.

Wenn du ein Objekt vom Typ  "x" hast, kannst du dessen JSON-
Zeichenkettendarstellung mit einer einzigen Codezeile anzeigen:

   >>> import json
   >>> x = [1, 'simple', 'list']
   >>> json.dumps(x)
   '[1, "simple", "list"]'

Eine weitere Variante der Funktion "dumps()", die den Namen "dump()"
trägt, serialisiert das Objekt einfach in eine *Textdatei*. Wenn also
"f" ein zum Schreiben geöffnetes *Textdatei*-Objekt ist, können wir
Folgendes tun:

   json.dump(x, f)

Um das Objekt erneut zu dekodieren, wenn "f" ein *Binärdatei*- oder
*Textdatei*-Objekt ist, das zum Lesen geöffnet wurde:

   x = json.load(f)

Bemerkung:

  JSON-Dateien müssen in UTF-8 kodiert sein. Verwende die Option
  "encoding="utf-8"", wenn du eine JSON-Datei als *Textdatei* öffnest,
  sowohl beim Lesen als auch beim Schreiben.

Diese einfache Serialisierungstechnik eignet sich für Listen und
Wörterbücher, doch die Serialisierung beliebiger Klasseninstanzen in
JSON erfordert etwas mehr Aufwand. Die Dokumentation zum Modul  "json"
enthält eine Erläuterung dazu.

Siehe auch:

  "pickle" - das Pickle-Modul

  Im Gegensatz zu JSON ist *pickle* ein Protokoll, das die
  Serialisierung beliebig komplexer Python-Objekte ermöglicht.  Als
  solches ist es Python-spezifisch und kann nicht zur Kommunikation
  mit Anwendungen verwendet werden, die in anderen Sprachen
  geschrieben sind. Außerdem ist es standardmäßig unsicher: Die
  Deserialisierung von *pickle*-Daten aus einer nicht
  vertrauenswürdigen Quelle kann zur Ausführung von beliebigem Code
  führen, wenn die Daten von einem erfahrenen Angreifer manipuliert
  wurden.
