7. Eingabe und Ausgabe¶
Es gibt verschiedene Arten, die Ausgabe eines Programmes darzustellen: Daten können in menschenlesbarer Form ausgegeben werden oder in eine Datei für die spätere Verwendung geschrieben werden. Dieses Kapitel beschreibt einige Möglichkeiten.
7.1. Ausgefallenere Ausgabeformatierung¶
Bis jetzt sind uns zwei Arten der Ausgabe von Werten begegnet:
Ausdrucksanweisungen (expression statements) und die print()
-Funktion.
(Eine dritte Möglichkeit ist die write()
-Methode von Dateiobjekten; die
Standardausgabedatei kann als sys.stdout
referenziert werden. In der
Bibliotheksreferenz gibt es dazu weitere Informationen.)
Oft will man mehr Kontrolle über die Formatierung der Ausgabe haben als nur
Leerzeichen-getrennte Werte auszugeben. Es gibt zwei Arten die Ausgabe zu
formatieren: Die erste Möglichkeit ist, dass man die gesamte Verarbeitung der
Zeichenketten selbst übernimmt; indem man Slicing- und Verknüpfungsoperationen
benutzt, kann man jede denkbare Anordnung zusammenstellen. Der Typ string
hat einige Methoden, die ein paar nützliche Operationen ausführen, um
Zeichenketten auf eine bestimmte Länge aufzufüllen; diese werden wir in Kürze
behandeln. Die zweite Möglichkeit ist die Benutzung der
format()
-Methode.
Das string
-Modul enthält eine Klasse Template
, die noch
einen Weg bietet, Werte in Zeichenketten zu ersetzen.
Eine Frage bleibt natürlich: Wie konvertiert man Werte zu Zeichenketten?
Glücklicherweise kennt Python Wege, um jeden Wert in eine Zeichenkette
umzuwandeln: Man übergibt den Wert an die repr()
- oder
str()
-Funktion.
Die str()
-Funktion ist dazu gedacht eine möglichst menschenlesbare
Repräsentation des Wertes zurückzugeben, während repr()
dazu gedacht ist,
vom Interpreter lesbar zu sein (oder einen SyntaxError
erzwingt, wenn es
keine äquivalente Syntax gibt). Für Objekte, die keine besondere menschenlesbare
Repräsentation haben, gibt str()
denselben Wert wie repr()
zurück.
Viele Werte wie Nummern oder Strukturen wie Listen und Dictionaries benutzen für
beide Funktionen dieselbe Repräsentation. Besonders Zeichenketten haben zwei
verschiedene Repräsentationen.
Ein paar 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 y ist 40000...
>>> #repr() bei einer Zeichenkette benutzt Anführungszeichen und Backslashes:
... hello = 'Hallo Welt\n'
>>> hellos = repr(hello)
>>> print(hellos)
'Hallo Welt\n'
>>> # Das Argument für repr() kann jedes Pythonobjekt sein:
... repr((x, y, ('spam', 'eggs')))
"(32.5, 40000, ('spam', 'eggs'))"
Hier zwei Möglichkeiten, eine Tabelle von Quadrat- und Kubikzahlen zu erstellen:
>>> for x in range(1, 11):
... print(repr(x).rjust(2), repr(x*x).rjust(3), end=' ')
... # Achte auf die Benutzung 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
>>> 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
(Beachte, dass im ersten Beispiel ein Leerzeichen pro Spalte durch die
Funktionsweise von print()
hinzugefügt wird: Sie trennt ihre Argumente mit
Leerzeichen.)
Dieses Beispiel hat die rjust()
-Methode von Zeichenkettenobjekten
gezeigt, die eine Zeichenkette in einem Feld der gegebenen Breite rechtsbündig
macht, indem sie diese links mit Leerzeichen auffüllt. Es gibt die ähnlichen
Methoden ljust()
und center()
. Diese Methoden schreiben
nichts, sondern geben eine neue Zeichenkette zurück. Ist die gegebene
Zeichenkette zu lang, schneiden sie nichts, sondern geben diese unverändert
zurück; dies wird die Anordnung durcheinanderbringen, aber ist meistens besser
als die Alternative, dass der Wert verfälscht wird. (Will man wirklich
abschneiden, kann man immer noch eine Slicing-Operation hinzufügen, zum Beispiel
x.ljust(n)[:n]
.)
Es gibt noch eine weitere Methode, zfill()
, die eine numerische
Zeichenkette mit Nullen auffüllt. Sie versteht auch Plus- und Minuszeichen:
>>> '12'.zfill(5)
'00012'
>>> '-3.14'.zfill(7)
'-003.14'
>>> '3.14159265359'.zfill(5)
'3.14159265359'
Die einfachste Benutzung der format()
-Methode sieht so aus:
>>> print('Wir sind die {}, die "{}!" sagen.'.format('Ritter', 'Ni'))
Wir sind die Ritter, die "Ni!" sagen.
Die Klammern und die Zeichen darin (genannt Formatfelder - format fields)
werden mit den Objekten ersetzt, die der format()
-Methode übergeben
werden. Eine Nummer in den Klammern bezieht sich auf die Position des Objektes,
die der format()
-Methode übergeben werden.
>>> print('{0} and {1}'.format('spam', 'eggs'))
spam and eggs
>>> print('{1} and {0}'.format('spam', 'eggs'))
eggs and spam
Werden Schlüsselwortargumente in der format()
-Methode benutzt, können
deren Werte durch die Benutzung des Argumentnamens referenziert werden.
>>>print('Dieses {Speise} ist {Adjektiv}.'.format(Speise='Spam',
Adjektiv='absolut schrecklich'))
Dieses Spam ist absolut schrecklich.
Positionsabhängige und Schlüsselwortargumente können willkürlich kombiniert werden:
>>>print('Die Geschichte von {0}, {1} und {anderer}.'.format('Bill',
'Manfred', anderer='Georg'))
Die Geschichte von Bill, Manfred und Georg.
'!a'
(wendet ascii()
an), '!s'
(wendet str()
an) und
'!r'
(wendet repr()
an) können dazu benutzt werden den übergebenen
Wert zu konvertieren bevor er formatiert wird:
>>> import math
>>> print('Der Wert von PI ist ungefähr {}.'.format(math.pi))
Der Wert von PI ist ungefähr 3.14159265359.
>>> print('Der Wert von PI ist ungefähr {!r}.'.format(math.pi))
Der Wert von PI ist ungefähr 3.141592653589793.
Ein optionales ':'
mit Formatspezifizierer (format specifier) können auf
den Namen des Feldes folgen. Dies erlaubt einem eine größere Kontrolle darüber,
wie der Wert formatiert wird. Das folgende Beispiel rundet Pi auf drei Stellen
nach dem Komma.
>>> import math
>>> print('Der Wert von Pi ist ungefähr {0:.3f}.'.format(math.pi))
Der Wert von Pi ist ungefähr 3.142.
Übergibt man einen Integer nach dem ':'
, so legt man eine minimale Breite
für dieses Feld an. Das ist nützlich um Tabellen schön aussehen zu lassen.
>>> table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 7678}
>>> for name, phone in table.items():
... print('{0:10} ==> {1:10d}'.format(name, phone))
...
Jack ==> 4098
Dcab ==> 7678
Sjoerd ==> 4127
Hat man einen wirklich langen Formatstring, den man nicht aufteilen will, wäre
es nett, wenn man die zu formatierenden Variablen durch den Namen statt durch
die Position referenzieren könnte. Dies kann einfach bewerkstelligt werden,
indem man das Dictionary übergibt und auf die Schlüssel über eckige Klammern
'[]'
zugreift
>>> 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
Das könnte auch genauso erreicht werden, indem man die Tabelle als Schlüsselwortargumente mit der ‘**’-Notation übergibt.
>>> 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
Das ist besonders nützlich in Verbindung mit der eingebauten Funktion
vars()
, die ein Dictionary mit allen lokalen Variablen zurückgibt.
Format String Syntax gibt eine komplette
Übersicht zur Zeichenkettenformatierung mit format()
.
7.1.1. Alte Zeichenkettenformatierung¶
Der %
-Operator kann auch zur Zeichenkettenformatierung genutzt werden. Er
interpretiert das linke Argument genauso wie einen sprintf()
-artigen
Formatstring, der auf das rechte Argument angewendet werden soll und gibt die
resultierende Zeichenkette dieser Formatierungsoperation zurück. Zum Beispiel:
>>> import math
>>> print('Der Wert von Pi ist ungefähr %5.3f.' % math.pi)
Der Wert von Pi ist ungefähr 3.142.
Da format()
ziemlich neu ist, benutzt viel Pythoncode noch den
%
-Operator. Jedoch sollte format()
hauptsächlich benutzt werden,
da die alte Art der Formatierung irgendwann aus der Sprache entfernt werden
wird.
Mehr Informationen dazu gibt es in dem Abschnitt Old String Formatting Operations.
7.2. Lesen und Schreiben von Dateien¶
open()
gibt ein Dateiobjekt (file object) zurück und wird meistens
mit zwei Argumenten aufgerufen: open(filename, mode)
>>> f = open('/tmp/workfile', 'w')
>>> print(f)
<open file '/tmp/workfile', mode 'w' at 80a0960>
Das erste Argument ist eine Zeichenkette, die den Dateinamen enthält. Das zweite
Argument ist eine andere Zeichenkette mit ein paar Zeichen, die die Art der
Benutzung der Datei beschreibt. mode kann 'r'
sein, wenn die Datei nur
gelesen wird, 'w'
, wenn sie nur geschrieben wird (eine existierende Datei
mit demselben Namen wird gelöscht) und 'a'
öffnet die Datei zum Anhängen;
alle Daten, die in die Datei geschrieben werden, werden automatisch ans Ende
angehängt. 'r+'
öffnet die Datei zum Lesen und Schreiben. Das
mode-Argument ist optional, fehlt es, so wird 'r'
angenommen.
Normalerweise werden Dateien im Textmodus (text mode) geöffnet, das
heisst, dass man Zeichenketten von ihr liest beziehungsweise in sie schreibt,
die in einer bestimmten Kodierung kodiert werden (der Standard ist UTF-8).
Wird 'b'
an das mode-Argument angehängt, so öffnet man die Datei im
Binärmodus (binary mode); in ihm werden Daten als Byteobjekte gelesen
und geschrieben. Dieser Modus sollte für alle Dateien genutzt werden, die keinen
Text enthalten.
Im Textmodus wird beim Lesen standardmäßig das plattformspezifische Zeilenende
(\n
unter Unixen, \r\n
unter Windows) zu einem einfachen \n
konvertiert und beim Schreiben \n
zurück zum plattformspezifischen
Zeilenende. Diese versteckte Modifikation ist klasse für Textdateien, wird aber
binäre Dateiformate, wie JPEG
- oder EXE
-Dateien, beschädigen.
Achte sehr sorgfältig darauf, dass Du den Binärmodus benutzt, wenn Du solche
Dateien schreibst oder liest.
7.2.1. Methoden von Dateiobjekten¶
Die übrigen Beispiele in diesem Abschnitt nehmen an, dass ein Dateiobjekt namens
f
schon erstellt wurde.
Um den Inhalt einer Datei zu lesen, kann man f.read(size)
aufrufen, was
einen Teil der Daten ausliest und diese als Zeichenketten- oder Byteobjekt
zurückgibt. size ist ein optionales, numerisches Argument. Wird es ausgelassen
oder ist es negativ, so wird der gesamte Inhalt der Datei ausgelesen und
zurückgegeben, falls die Datei doppelt so groß wie der Speicher Deiner Maschine
ist, so ist das Dein Problem. Andernfalls werden höchstens size Byte
ausgelesen und zurückgegeben. Ist das Ende der Datei erreicht, so gibt
f.read()
eine leere Zeichenkette (''
) zurück.
>>> f.read()
'Das ist die ganze Datei.\n'
>>> f.read()
''
f.readline()
liest eine einzelne Zeile aus einer Datei; ein
Zeilenumbruchszeichen (\n
) bleibt am Ende der Zeichenkette und wird nur
ausgelassen, falls die letzte Zeile nicht in einem Zeilenumbruch endet. Dies
macht den Rückgabewert eindeutig: Falls f.readline()
eine leere Zeichenkette
zurückgibt, so ist das Ende der Datei erreicht, während eine Leerzeile durch
'\n'
, eine Zeichenkette, die nur einen einzelnen Zeilenumbruch enthält,
dargestellt wird.
>>> f.readline()
'Dies ist die erste Zeile der Datei\n'
>>> f.readline()
'Zweite Zeile der Datei\n'
>>> f.readline()
''
f.readlines()
gibt eine Liste zurück die alle Zeilen der Datei enthält. Wird
ein optionaler Paramenter sizehint übergeben, liest es mindestens so viele
Bytes aus der Datei und zusätzlich noch so viele, dass die nächste Zeile
komplett ist und gibt diese Zeilen zurück. Dies wird oft benutzt, um ein
effizientes Einlesen der Datei anhand der Zeilen zu ermöglichen, ohne die
gesamte Datei in den Speicher laden zu müssen. Nur komplette Zeilen werden
zurückgegeben.
>>> f.readlines()
['Dies ist die erste Zeile der Datei\n', 'Zweite Zeile der Datei\n']
Ein alternativer Ansatz Zeilen auszulesen ist, über das Dateiobjekt zu iterieren. Das ist speichereffizient, schnell und führt zu einfacherem Code:
>>> for line in f:
... print(line, end='')
...
Dies ist die erste Zeile der Datei.
Zweite Zeile der Datei
Der alternative Ansatz ist einfacher, bietet aber keine feinkörnige Kontrolle. Da beide Ansätze die Pufferung von Zeilen unterschiedlich handhaben, sollten sie nicht vermischt werden.
f.write(string)
schreibt den Inhalt von string in die Datei und gibt die
Anzahl der Zeichen, die geschrieben wurden, zurück.
>>> f.write('Dies ist ein Test\n')
18
Um etwas anderes als eine Zeichenkette zu schreiben, muss es erst in eine Zeichenkette konvertiert werden:
>>> value = ('Die Antwort', 42)
>>> s = str(value)
>>> f.write(s)
19
f.tell()
gibt eine Ganzzahl zurück, die die aktuelle Position des
Dateiobjektes innerhalb der Datei angibt, gemessen in Bytes vom Anfang der
Datei. Um die Position des Dateiobjektes zu ändern, gibt es f.seek(offset,
from_what)
. Die Position wird berechnet indem offset zu einem Referenzpunkt
addiert wird, dieser wird durch das Argument from_what festgelegt. Bei einem
from_what des Wertes 0, wird von Beginn der Datei gemessen, bei 1 von der
aktuellen Position, bei 2 vom Ende der Datei. from_what kann ausgelassen
werden und hat den Standardwert 0, das den Anfang der Datei als Referenzpunkt
benutzt.
>>> f = open('/tmp/workfile', 'rb+')
>>> f.write(b'0123456789abcdef')
16
>>> f.seek(5) # Gehe zum 6. Byte der Datei
5
>>> f.read(1)
b'5'
>>> f.seek(-3, 2) # Gehe zum drittletzten Byte
13
>>> f.read(1)
b'd'
In Textdateien (die, die ohne ein b
im Modus geöffnet werden) sind nur
Positionierungen vom Anfang der Datei aus erlaubt (mit der Ausnahme, dass mit
f.seek(0, 2)
zum Ende der Datei gesprungen werden kann).
Wenn man mit einer Datei fertig ist, ruft man f.close()
auf, um sie zu
schließen und jegliche Systemressource freizugeben, die von der offenen Datei
belegt wird. Nach dem Aufruf von f.close()
schlägt automatisch jeder Versuch
fehl das Objekt zu benutzen.
>>> f.close()
>>> f.read()
Traceback (most recent call last):
File "<stdin>", line 1, in ?
ValueError: I/O operation on closed file
Die optimale Vorgehensweise ist es, das Schlüsselwort with
zu
benutzen, wenn man mit Dateiobjekten arbeitet. Das hat den Vorteil, dass die
Datei richtig geschlossen wird, sobald die Befehle des Blocks abgearbeitet sind,
auch wenn innerhalb eine Ausnahme verursacht wird. Das ist auch viel kürzer als
einen äquivalenten try
-finally
-Block zu schreiben:
>>> with open('/tmp/workfile', 'r') as f:
... read_data = f.read()
>>> f.closed
True
Dateiobjekte haben noch ein paar zusätzliche Methoden, wie isatty()
und truncate()
, die weniger häufig genutzt werden. Ein komplettes
Handbuch zu Dateiobjekten kann in der Bibliotheksreferenz gefunden werden.
7.2.2. Das pickle
-Modul¶
Zeichenketten können einfach in eine Datei geschrieben und aus ihr gelesen
werden. Zahlen sind ein bisschen aufwändiger, da die read()
-Methode nur
Zeichenketten zurückgibt. Diese müssen an eine Funktion wie int()
übergeben werden, die eine Zeichenkette wie '123'
nimmt und deren
numerischen Wert 123 zurückgibt. Wenn man jedoch komplexere Datentypen wie
Listen, Dictionaries oder Klasseninstanzen speichern will, wird die
Angelegenheit viel komplizierter.
Anstatt die Benutzer ständig Code schreiben und debuggen zu lassen, um
komplexere Datentypen zu speichern, stellt Python ein Standardmodul namens
pickle
bereit. Dies ist ein fantastisches Modul, das fast jedes
Pythonobjekt (sogar ein paar Formen von Pythoncode!) nehmen kann und es in eine
Zeichenkettenrepräsentation konvertieren kann; dieser Prozess wird
pickling (“einwecken”) genannt. Das Objekt aus der
Zeichenkettenrepräsentation zu rekonstruieren wird unpickling genannt.
Zwischen pickling und unpickling, kann die Zeichenkettenrepräsentation in Daten
oder Dateien gespeichert werden oder über ein Netzwerk an eine entfernte
Maschine geschickt werden.
Hat man ein Objekt x
und ein Dateiobjekt f
, das zum Schreiben geöffnet
wurde, benötigt der einfachste Weg das Objekt zu picklen nur eine Zeile Code:
pickle.dump(x, f)
Um das Objekt wieder zu unpicklen reicht, wenn f
ein Dateiobjekt ist, das
zum Lesen geöffnet wurde:
x = pickle.load(f)
(Es gibt auch andere Varianten, die benutzt werden, wenn man viele Objekte
pickled oder falls man gepicklete Daten nicht in einer Datei speichern will;
siehe pickle
in der Python Bibliotheksreferenz.)
pickle
ist der normale Weg ein Pythonobjekt zu erzeugen, das gespeichert
und von anderen Programmen oder demselben Programm wiederbenutzt werden kann;
der Fachbegriff für so etwas ist ein persistentes Objekt. Weil
pickle
so weitläufig benutzt wird, stellen viele Programmierer, die
Pythonerweiterungen schreiben sicher, dass neue Datentypen, wie Matrizen,
richtig gepickled und unpickled werden können.