Download - 3446435476_Pytho

Transcript
Page 1: 3446435476_Pytho

IN EINER WOCHE PROGRAMMIEREN LERNEN

Im Internet: Musterlösungen zu den Übungen

Bernd KLEIN

EINFÜHRUNG IN

PYTHON3

Page 2: 3446435476_Pytho

Klein

Einführung in Python 3

Bleiben Sie auf dem Laufenden!

Der Hanser Computerbuch-Newsletter informiert Sie regelmäßig über neue Bücher und Termine aus den verschiedenen Bereichen der IT. Profitieren Sie auch von Gewinnspielen und exklusiven Leseproben. Gleich anmelden unterwww.hanser-fachbuch.de/newsletter

CHV Newsletterhinweis Computer

Page 3: 3446435476_Pytho

  III

Page 4: 3446435476_Pytho

  III

Bernd Klein

Einführung in Python 3 In einer Woche programmieren lernen

Page 5: 3446435476_Pytho

IV   <U1_num> <U1>  MF

Der Autor:Bernd Klein, [email protected]

Alle in diesem Buch enthaltenen Informationen, Verfahren und Darstellungen wurden nach bes-tem Wissen zusammengestellt und mit Sorgfalt getestet. Dennoch sind Fehler nicht ganz auszu-schließen. Aus diesem Grund sind die im vorliegenden Buch enthaltenen Informationen mit kei-ner Verpflichtung oder Garantie irgendeiner Art verbunden. Autor und Verlag übernehmen infolgedessen keine juristische Verantwortung und werden keine daraus folgende oder sonstige Haftung übernehmen, die auf irgendeine Art aus der Benutzung dieser Informationen – oder Teilen davon – entsteht.

Ebenso übernehmen Autor und Verlag keine Gewähr dafür, dass beschriebene Verfahren usw. frei von Schutzrechten Dritter sind. Die Wiedergabe von Gebrauchsnamen, Handelsnamen, Waren- be zeich nungen usw. in diesem Buch berechtigt deshalb auch ohne besondere Kennzeichnung nicht zu der Annahme, dass solche Namen im Sinne der Warenzeichen- und Markenschutz-Gesetzgebung als frei zu betrachten wären und daher von jedermann benutzt werden dürften.

Bibliografische Information der Deutschen Nationalbibliothek:

Die Deutsche Nationalbibliothek verzeichnet diese Publikation in der Deutschen National biblio-grafie; detaillierte bibliografische Daten sind im Internet über http://dnb.d-nb.de abrufbar.

Dieses Werk ist urheberrechtlich geschützt. Alle Rechte, auch die der Übersetzung, des Nachdruckes und der Vervielfältigung des Bu-ches, oder Teilen daraus, vorbehalten. Kein Teil des Werkes darf ohne schriftliche Genehmigung des Verlages in irgendeiner Form (Fotokopie, Mikrofilm oder ein anderes Verfahren) – auch nicht für Zwecke der Unterrichtsgestaltung – reproduziert oder unter Verwendung elektronischer Sys-teme verarbeitet, vervielfältigt oder verbreitet werden.

© 2013 Carl Hanser Verlag München, www.hanser-fachbuch.de Lektorat: Brigitte Bauer-Schiewek Herstellung: Irene Weilhart Fachlektorat: Stefan Günther, Hanau Copy editing: Jürgen Dubau, Freiburg/Elbe Layout: le-tex publishing services, Leipzig Umschlagdesign: Marc Müller-Bremer, www.rebranding.de, München Umschlagrealisation: Stephan Rönigk Druck und Bindung: Kösel, Krugzell Ausstattung patentrechtlich geschützt. Kösel FD 351, Patent-Nr. 0748702 Printed in Germany

print-ISBN: 978-3-446-43547-6 e-book-ISBN: 978-3-446-43717-3

Page 6: 3446435476_Pytho

Inhalt

Vorwort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . XVII

Danksagung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . XIX

1 Einleitung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11.1 Programmieren lernen in einer Woche . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1

1.2 Aufbau des Buches. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2

1.3 Zielgruppe des Buches . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3

1.4 Programmieren lernen „interaktiv” . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4

1.5 Download der Beispiele und Hilfe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4

1.6 Anregungen und Kritik . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5

Teil I Einführung in Python 3 – Für Ein- und Umsteiger. . . . . . . . . . . . . . . . . . . . . . 7

2 Kommandos und Programme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92.1 Erste Schritte mit Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9

2.1.1 Linux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9

2.1.2 Windows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10

2.2 Herkunft und Bedeutung des Begriffes interaktive Shell . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11

2.2.1 Erste Schritte in der interaktiven Shell . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11

2.3 Verlassen der Python-Shell . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13

2.4 Benutzung von Variablen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13

2.5 Mehrzeilige Anweisungen in der interaktiven Shell . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14

2.6 Programme schreiben oder schnell mal der Welt “Hallo” sagen . . . . . . . . . . . . . . . . . . . 14

3 Bytecode und Maschinencode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173.1 Einführung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17

3.2 Unterschied zwischen Programmier- und Skriptsprachen . . . . . . . . . . . . . . . . . . . . . . . . . 17

3.3 Interpreter- oder Compilersprache. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17

Page 7: 3446435476_Pytho

VI Inhalt

4 Datentypen und Variablen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214.1 Einführung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21

4.2 Statische und dynamische Typdeklaration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22

4.3 Typumwandlung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24

4.4 Datentyp ermitteln . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25

5 Sequentielle Datentypen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 275.1 Übersicht . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27

5.1.1 Zeichenketten oder Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27

5.1.2 Listen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28

5.1.3 Tupel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28

5.1.4 Sequenz von Binärdaten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28

5.2 Indizierung von sequentiellen Datentypen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28

5.3 Slicing oder Ausschneiden. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30

5.4 Aufgaben . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31

6 Dictionaries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 336.1 Dictionaries und assoziative Felder . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33

6.2 Definition und Benutzung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34

6.3 Fehlerfreie Zugriffe auf Dictionaries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36

6.4 Zulässige Typen für Schlüssel und Werte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37

6.5 Verschachtelte Dictionaries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37

6.6 Methoden auf Dictionaries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38

6.7 Operatoren. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41

6.8 Zip. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42

6.9 Dictionaries aus Listen erzeugen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43

6.10 Aufgaben . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44

7 Mengen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 477.1 Übersicht . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47

7.2 Mengen in Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47

7.2.1 Sets erzeugen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48

7.2.2 Mengen von unveränderlichen Elementen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48

7.3 Frozensets. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49

7.4 Operationen auf „set”-Objekten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49

7.4.1 add(element). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49

7.4.2 clear() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50

7.4.3 copy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50

7.4.4 difference() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50

Page 8: 3446435476_Pytho

Inhalt VII

7.4.5 difference_update() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51

7.4.6 discard(el) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51

7.4.7 remove(el) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52

7.4.8 intersection(s) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52

7.4.9 isdisjoint() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52

7.4.10 issubset() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53

7.4.11 issuperset() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53

7.4.12 pop() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53

8 Verzweigungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 558.1 Verzweigungen im Allgemeinen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55

8.2 Bedingte Anweisungen in Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55

8.3 Beispiel Hundejahre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56

8.4 Wahr oder falsch: Bedingungen in Verzweigungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57

8.5 Aufgaben . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58

9 Schleifen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 599.1 Übersicht . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59

9.2 while-Schleife . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60

9.3 die Alternative im Erfolgsfall: else . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61

9.4 For-Schleife . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62

9.5 Aufgaben . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65

10 Dateien lesen und schreiben . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6910.1 Dateien . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69

10.2 Text aus einer Datei lesen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69

10.3 Schreiben in eine Datei . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71

10.4 In einem Rutsch lesen: readlines und read . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71

10.5 Aufgaben . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72

11 Listen und Tupel im Detail . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7511.1 Stapelspeicher . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75

11.2 Stapelverarbeitung in Python: pop und append. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76

11.3 extend . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77

11.4 Entfernen eines Wertes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77

11.5 Prüfen, ob ein Element in Liste enthalten ist . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78

11.6 Finden der Position eines Elementes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78

11.7 Einfügen von Elementen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79

11.8 Besonderheiten bei Tupel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79

Page 9: 3446435476_Pytho

VIII Inhalt

11.8.1 Leere Tupel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80

11.8.2 1-Tupel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80

11.8.3 Mehrfachzuweisungen, Packing und Unpacking . . . . . . . . . . . . . . . . . . . . . . . . . . . 81

11.9 Die veränderliche Unveränderliche . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81

11.10 Sortieren von Listen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82

11.10.1 „sort” und „sorted” . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82

11.10.2 Umkehrung der Sortierreihenfolge . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83

11.10.3 Eigene Sortierfunktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83

11.11 Aufgaben . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86

12 Modularisierung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8912.1 Module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89

12.1.1 Namensräume von Modulen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90

12.1.2 Namensräume umbenennen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91

12.1.3 Modularten. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91

12.1.4 Suchpfad für Module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92

12.1.5 Inhalt eines Modules. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93

12.1.6 Eigene Module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93

12.1.7 Dokumentation für eigene Module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94

12.2 Pakete. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95

13 Flaches und tiefes Kopieren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9713.1 Einführung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97

13.2 Kopieren einer Liste . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99

13.3 Kopie mit Teilbereichsoperator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100

13.4 Kopieren mit deepcopy aus dem Modul copy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101

13.5 Deepcopy für Dictionaries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101

14 Funktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10314.1 Allgemein . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103

14.2 Funktionen in Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104

14.3 Optionale Parameter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105

14.4 Docstring. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106

14.5 Schlüsselwortparameter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106

14.6 Rückgabewerte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106

14.7 Mehrere Rückgabewerte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107

14.8 Lokale und globale Variablen in Funktionen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108

14.9 Beliebige Anzahl von Parametern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109

14.10 Parameterübergabe. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110

Page 10: 3446435476_Pytho

Inhalt IX

14.11 Nebeneffekte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112

14.12 Kommandozeilenparameter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113

14.13 Variable Anzahl von Parametern. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113

14.14 * in Funktionsaufrufen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115

14.15 Beliebige Schlüsselwortparameter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116

14.16 Doppeltes Sternchen im Funktionsaufruf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116

15 Rekursive Funktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11715.1 Definition und Herkunft des Begriffs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117

15.2 Definition der Rekursion. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118

15.3 rekursive Funktionen in Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118

15.4 Die Tücken der Rekursion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119

15.5 Fibonacci-Folge in Python. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120

15.6 Aufgaben . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124

16 Globale und lokale Variablen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12716.1 Einführung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127

16.2 Globale und lokale Variablen in Funktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128

17 Alles über Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13117.1 ... fast alles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131

17.2 Aufspalten von Zeichenketten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132

17.2.1 split . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132

17.2.2 Standardverhalten und „maxsplit” . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134

17.2.3 rsplit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136

17.2.4 Folge von Trennzeichen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137

17.2.5 splitlines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138

17.2.6 partition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139

17.3 Zusammenfügen von Stringlisten mit join . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139

17.4 Suchen von Teilstrings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140

17.4.1 „in” oder „not in” . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140

17.4.2 s.find(substring[, start[, end]]) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140

17.4.3 s.rfind(substring[, start[, end]]). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141

17.4.4 s.index(substring[, start[, end]]) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141

17.4.5 s.rindex(substring[, start[, end]]) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141

17.4.6 s.count(substring[, start[, end]]) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142

17.5 Suchen und Ersetzen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142

17.6 Nur noch Kleinbuchstaben oder Großbuchstaben . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143

17.7 capitalize und title . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143

Page 11: 3446435476_Pytho

X Inhalt

17.8 Stripping Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144

17.9 Strings ausrichten. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144

17.10 String-Tests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145

17.11 Aufgaben . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147

18 Ausnahmebehandlung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14918.1 Die optionale else-Klausel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150

18.2 Fehlerinformationen über sys.exc_info . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151

18.3 Abfangen mehrerer Exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151

18.4 except mit mehrfachen Ausnahmen .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152

18.5 Exceptions generieren. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152

18.6 Finalisierungsaktion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152

19 Objektorientierte Programmierung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15519.1 Einführung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155

19.2 Die Kuchenklasse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156

19.3 Objekte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156

19.4 Klasse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156

19.5 Kapselung von Daten. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157

19.6 Vererbung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158

19.7 Klassen in Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158

19.8 Methoden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159

19.9 Konstruktor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160

19.10 Destruktor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160

19.11 Lauffähige Version der Kontoklasse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161

19.12 „Öffentlicher Schönheitsfehler” oder Public-Attribute . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162

19.13 Datenkapselung und die Benutzung von Public- Protected- und Private-Attributen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163

19.14 Statische Member . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164

19.14.1 __del__ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165

19.15 Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166

19.16 Dynamische und statische Attribute . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168

19.17 Vererbung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169

19.17.1 Oberbegriffe und Oberklassen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169

19.17.2 Vererbung in Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170

19.18 Mehrfachvererbung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174

19.18.1 Theorie. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174

19.18.2 Diamand-Problem oder „deadly diamond of death” . . . . . . . . . . . . . . . . . . . . . . . 176

19.18.3 Beispiel: CalendarClock . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177

Page 12: 3446435476_Pytho

Inhalt XI

19.19 Polymorphie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185

19.20 Operator-Überladung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187

19.21 Standardklassen als Basisklassen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188

19.22 Aufgaben . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189

Teil II Weiterführende Themen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193

20 Tests und Fehler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19520.1 Einführung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195

20.2 Modultests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197

20.3 Modultests unter Benutzung von __name__ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197

20.4 doctest-Modul . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200

20.5 Testgetriebene Entwicklung oder „Im Anfang war der Test” . . . . . . . . . . . . . . . . . . . . . . . . 202

20.6 unittest . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204

20.7 Methoden der Klasse TestCase . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 206

20.8 Aufgaben . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209

21 Systemprogrammierung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21121.1 Systemprogrammierung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211

21.2 Häufig falsch verstanden: Shell . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211

21.3 os-Modul . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212

21.3.1 Vorbemerkungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 213

21.3.2 Umgebungsvariablen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 213

21.3.3 Dateiverarbeitung auf niedrigerer Ebene . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215

21.3.4 Die exec-„Familie” . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221

21.3.5 Weitere Funktionen im Überblick. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 226

21.3.6 os.path - Arbeiten mit Pfaden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 240

21.4 shutil-Modul . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 249

22 Forks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25522.1 Fork . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 255

22.2 Fork in Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 255

23 Das Modul „pickle”. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25923.1 Daten „einpökeln” mit pickle.dump .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 259

23.2 pickle.load . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 260

Page 13: 3446435476_Pytho

XII Inhalt

24 Reguläre Ausdrücke . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26124.1 Ursprünge und Verbreitung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 261

24.2 Stringvergleiche . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 261

24.3 Überlappungen und Teilstrings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 263

24.4 Das re-Modul . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 263

24.5 Matching-Problem.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 264

24.6 Syntax der regulären Ausdrücke . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 266

24.6.1 Beliebiges Zeichen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 266

24.7 Zeichenauswahl . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 266

24.8 Endliche Automaten. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 267

24.9 Vordefinierte Zeichenklassen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 268

24.10 Anfang und Ende eines Strings. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 269

24.11 Optionale Teile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 271

24.12 Quantoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 272

24.13 Gruppierungen und Rückwärtsreferenzen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 274

24.13.1 Match-Objekte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 274

24.14 Umfangreiche Übung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 276

24.15 Alles finden mit findall . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 278

24.16 Alternativen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 279

24.17 Kompilierung von regulären Ausdrücken . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 280

24.18 Aufspalten eines Strings mit oder ohne regulären Ausdruck . . . . . . . . . . . . . . . . . . . . . . . 282

24.18.1 split-Methode der String-Klasse. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 282

24.18.2 split-Methode des re-Moduls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 283

24.18.3 Wörter filtern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 285

24.19 Suchen und Ersetzen mit sub . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 286

24.20 Aufgaben . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 286

25 lambda, map, filter und reduce . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28925.1 lambda . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 289

25.2 map . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 292

25.3 Filtern von sequentiellen Datentypen mittels „filter” . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 294

25.4 reduce . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 294

25.5 Aufgaben . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 296

26 Listen-Abstraktion/List Comprehension . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29726.1 Die Alternative zu Lambda und Co. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 297

26.2 Einführung in die Listen-Abstraktion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 297

26.3 Syntax. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 298

26.4 Weitere Beispiele . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 299

Page 14: 3446435476_Pytho

Inhalt XIII

26.5 Die zugrunde liegende Idee . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 299

26.6 Anspruchsvolleres Beispiel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 300

26.7 Mengen-Abstraktion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 301

26.8 Rekursive Funktion zur Berechnung der Primzahlen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 301

26.9 Generator Comprehension/ Generatoren-Abstraktion. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 302

26.10 Aufgaben . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 303

27 Generatoren und Iteratoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30527.1 Einführung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 305

27.2 Iteration in for-Schleifen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 305

27.3 Generatoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 307

27.4 Beispiele . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 309

27.4.1 Permutationen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 309

27.4.2 Variationen und Kombinationen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 310

27.5 Generatoren zähmen mit firstn und islice. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 312

27.6 send-Methode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 312

27.7 Generator-Ausdrücke . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 313

27.8 Aufgaben . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 314

28 Memoisation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31728.1 Bedeutung und Herkunft des Begriffes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 317

28.2 Memoisation mit Dekorateur-Funktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 318

28.3 Memoize in einer Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 319

28.4 Dekorateure in Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 319

28.5 Überprüfung von Argumenten durch Dekorateure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 321

29 NumPy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32329.1 Übersicht . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 323

29.2 Arrays in NumPy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 325

29.3 Arrays flach machen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 327

29.4 Arrays umdimensionieren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 328

29.5 Arrays konkatenieren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 330

29.6 Array, neue Dimension hinzufügen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 332

29.7 Array mit Nullen und Einsen initialisieren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 332

29.8 Matrizenarithmetik . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 333

29.9 Vektoraddition und Vektorsubtraktion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 334

29.10 Matrix-Klasse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 336

29.10.1 Matrix-Produkt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 337

29.10.2 Eine einfache praktische Anwendung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 338

Page 15: 3446435476_Pytho

XIV Inhalt

29.11 Inverse Matrix . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 339

29.12 Kreuzprodukt / Vektorprodukt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 340

29.13 Lineare Gleichungssysteme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 340

29.14 Polynome .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 342

29.15 Aufgaben . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 343

Teil III Umfassende Beispiele . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 345

30 Bruchklasse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34730.1 Brüche à la 1001 Nacht . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 347

30.2 Zurück in die Gegenwart. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 348

30.3 Rechenregeln . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 351

30.3.1 Multiplikation von Brüchen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 351

30.3.2 Division von Brüchen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 352

30.3.3 Addition von Brüchen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 353

30.3.4 Subtraktion von Brüchen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 353

30.3.5 Die Bruchklasse im Überblick . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 354

31 Mastermind . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35731.1 Die Ursprünge des Spiels . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 357

31.2 Die Spielregeln . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 358

31.2.1 „Bulls and Cows” . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 358

31.2.2 Mastermind . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 358

31.3 Kombinatorikmodul . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 359

31.4 Mastermind in Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 360

32 Textklassifikation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36532.1 Einführung in die Textklassifikation. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 365

32.2 Textklassifikation: Aufgabe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 367

32.3 Naive-Bayes-Klassifikator. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 367

32.3.1 Definition. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 367

32.3.2 Bayes-Theorem .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 367

32.4 Formale Herleitung der Naive-Bayes-Klassifikation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 368

32.5 Textklassifikation in Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 370

32.5.1 BagOfWords-Klasse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 370

32.5.2 Document-Klasse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 371

32.5.3 DocumentClass-Klasse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 373

32.5.4 Pool-Klasse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 374

Page 16: 3446435476_Pytho

Inhalt XV

33 Lösungen zu den Aufgaben . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37933.1 Lösungen zu Kapitel 5 (Sequentielle Datentypen) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 379

33.2 Lösungen zu Kapitel 6 (Dictionaries) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 381

33.3 Lösungen zu Kapitel 8 (Verzweigungen) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 383

33.4 Lösungen zu Kapitel 9 (Schleifen) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 385

33.5 Lösungen zu Kapitel 10 (Dateien lesen und schreiben) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 388

33.6 Lösungen zu Kapitel 11 (Listen und Tupel im Detail) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 390

33.7 Lösungen zu Kapitel 15 (Rekursive Funktionen) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 393

33.8 Lösungen zu Kapitel 17 (Alles über Strings . . . ) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 397

33.9 Lösungen zu Kapitel 19 (Objektorientierte Programmierung) . . . . . . . . . . . . . . . . . . . . . 400

33.10 Lösungen zu Kapitel 20 (Tests und Fehler) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 405

33.11 Lösungen zu Kapitel 24 (Reguläre Ausdrücke) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 405

33.12 Lösungen zu Kapitel 25 (lambda, map, filter und reduce) . . . . . . . . . . . . . . . . . . . . . . . . . . 410

33.13 Lösungen zu Kapitel 26 (Listen-Abstraktion/List Comprehension) . . . . . . . . . . . . . . . 411

33.14 Lösungen zu Kapitel 27 (Generatoren und Iteratoren) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 412

33.15 Lösungen zu Kapitel 29 (NumPy) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 415

Stichwortverzeichnis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 417

Page 17: 3446435476_Pytho
Page 18: 3446435476_Pytho

Vorwort

Bedingt durch den traumhaften Anstieg der Bedeutung von Python in der Wissenschaftund Wirtschaft in den letzten Jahren gibt es auch ein wachsendes Interesse an geeigne-ten Python-Büchern. Gerade für Programmieranfänger besteht die Schwierigkeit darin, eingeeignetes Buch zu finden. Der unüberlegte Griff ins Bücherregal führt schnell zum Kaufeines Buches, was zwar viele Vorzüge haben mag, aber für Anfängerinnen und Anfängervöllig ungeeignet ist.

Dies ist besonders schade, da es sich bei Python ja um eine einfache Programmiersprachehandelt. Das war bereits beim anfänglichen Design der Sprache eines der wesentlichen Zie-le ihres „Erfinders” Guido van Rossum, als er Python Anfang der 1990er Jahre am Zentrumfür Mathematik und Informatik (Centrum voor Wiskunde en Informatica) in Amsterdamentwarf. Python ist einfach, weil es mit erstaunlich wenigen Schlüsselwörtern auskommtund seine Syntax, also der Aufbau der Sprache, auf Übersichtlichkeit optimiert ist.

Auch im Bereich objektorientierte Programmierung ist Python sehr konsequent, in man-chen Dingen sogar konsequenter als Java. So sind in Python alles Objekte, d.h. es gibt keineprimitiven Typen. Selbst Integer und Float-Zahlen sind in Python als Klassen realisiert. Einweiterer Unterschied zu Java besteht darin, dass in Python Mehrfachvererbung möglich ist.

Auch Programme in anderen Sprachen lassen sich einfach als Module in Python einbetten.So kann man beispielsweise zeitkritische Algorithmen in C programmieren und sie dannin Python einbinden.

Zusammenfassend kann man sagen, dass es sich bei Python um eine Programmiersprachehandelt, die sich bestens zum Einstieg in die Programmierung eignet, aber auch die opti-male Lösung für Spezialisten aus zahlreichen Problemfelder ist. In diesem Sinne finden Siein diesem Buch den idealen Einstieg.

Brigitte Bauer-Schiewek, Lektorin

Page 19: 3446435476_Pytho
Page 20: 3446435476_Pytho

Danksagung

Zum Schreiben eines Buches benötigt es neben der nötigen Erfahrung und Kompetenz imFachgebiet vor allem viel Zeit. Zeit außerhalb des üblichen Rahmens. Zeit, die vor allem dieFamilie mitzutragen hat. Deshalb gilt mein besonderer Dank meiner Frau Karola, die michwährend dieser Zeit tatkräftig unterstützt hatte.

Außerdem danke ich den zahlreichen Teilnehmern an meinen Python-Kursen, die mirgeholfen haben, meine didaktischen und fachlichen Kenntnisse kontinuierlich zu ver-bessern. Ebenso möchte ich den Besuchern meiner Online-Tutorials unter www.python-kurs.eu und www.python-course.eu danken, vor allem denjenigen, die sich mit konstruk-tiven Anmerkungen bei mir gemeldet haben. Allen voran Stefan Günther, der mir vielekleine und große Ungenauigkeiten übermittelte und auch dieses Buch nochmals kritischgelesen hat.

Zuletzt danke ich auch ganz herzlich dem Hanser Verlag, der dieses Buch ermöglicht hat.Vor allem danke ich Frau Brigitte Bauer-Schiewek, Programmplanung Computerbuch, undFrau Sarah Merz, Lektorat und Online-Marketing, für die kontinuierliche ausgezeichneteUnterstützung. Für die technische Unterstützung bei LaTex-Problemen danke ich HerrnUwe Hartmann und Herrn Stephan Korell. Herrn Jürgen Dubau danke ich fürs Lektorat.

Bernd Klein, Singen

Page 21: 3446435476_Pytho

1 Einleitung

1.1 Programmieren lernen in einer WocheKann man in einer Woche programmieren lernen, so wie es der Titel des Buches verspricht?Genauer gesagt: Kann man in einer Woche mit Python programmieren lernen? Was mei-nen wir überhaupt mit einer Woche? „Acht Tage” – und gemeint sind sieben – oder denkenwir an eine Arbeitswoche mit fünf Tagen? Wie viel Stunden sollte man dann pro Tag an demBuch arbeiten? Jeweils ein voller Arbeitstag oder ein paar Minuten oder Stunden zwischen-durch? Die Liste der Fragen könnten wir noch beliebig fortsetzen.

Formulieren wir die Hauptfrage einfach um: Kann man in wenigen Tagen programmierenlernen? Das ist möglich, definitiv! Wir erfahren es mehrmals im Monat in unseren meistfünftägigen Python-Kursen. Sie werden zum einen von totalen Programmieranfängern undzum anderen von Programmierern mit Erfahrungen in anderen Programmiersprachen wiebeispielsweise C, C++, Java, Perl und nicht zu vergessen Shell-Programmierung besucht.Manche haben auch schon vorher mehr oder weniger viel Erfahrung in Python gesam-melt. Aber eines war in allen Gruppen gleich: Wir haben es immer geschafft, dass unsereTeilnehmerinnen und Teilnehmer programmieren gelernt haben, und vor allen Dingen –was uns am wichtigsten ist – konnten wir immer die Begeisterung für die Sprache Pythonentfachen.

Nun mögen Sie fragen, was bedeutet es, dass Sie „programmieren gelernt haben”? KönnenSie nun selbständig und problemlos auch komplexeste Probleme lösen? Lassen Sie es michmit natürlichen Sprachen vergleichen. Man kann in wenigen Stunden soviel Englisch odereine andere Sprache lernen, dass man bis zehn oder zwanzig zählen kann, dass man Leutebegrüßen kann, dass man sich bedanken, entschuldigen oder nach dem Weg fragen kannusw., aber auch nach einem Jahr wird man selten in der Lage sein, Shakespeare oder Ja-mes Joyce im englischen Original zu lesen. Will man jedoch überhaupt Shakespeare oderJoyce im Original lesen oder genügt es einem schon, wenn man einfache Konversationenmit Muttersprachlern führen, Nachrichten verstehen und Zeitungsartikel lesen kann? Soähnlich verhält es sich auch mit Programmiersprachen und der Programmierung im All-gemeinen. Was hat man sich zum Ziel gesetzt? Will man am Ende komplexe Aufgaben wieRoutenplaner, Webcrawler, Suchalgorithmen oder Ähnliches programmieren können odermöchte man vielmehr kleine oder mittlere praktische Probleme lösen?

Page 22: 3446435476_Pytho

2 1 Einleitung

Apropos „am Ende”: Wie auch beim Sprachen lernen gibt es kein Ende. Programmierenlernen ist eine lebenslange Angelegenheit. Man kann sich immer wieder (selbst als aner-kannter Experte) an kleinen neu gefundenen Details erfreuen.

1.2 Aufbau des BuchesDieses Buch besteht aus vier Teilen:

Teil I: In diesem Teil behandeln wir die Grundlagen der Sprache. Dies ist der ei-gentliche Stoff, den man durcharbeiten sollte, um Python zu beherrschen.Dieser Teil wird auch in unseren fünftägigen Kursen komplett behandelt.An die Bearbeitung dieses Teiles denken wir auch, wenn wir von „Program-mieren lernen in einer Woche” sprechen.

Teil II: Im nächsten Teil behandeln wir weiterführende Themen. Dies sind zumeinen weitere Sprachkonstrukte, die unmittelbar zum Sprachumfang vonPython gehören und um wichtige Module und Programmierkonzepte.Während es im ersten Teil prinzipiell keine Gebiete gibt, die man weglas-sen kann, wenn man Python lernen will, so sieht es in diesem Teil andersaus. Die Kapitel sind sehr wichtig für Python und die Benutzung von Py-thon, aber sie sind von unterschiedlicher Wichtigkeit für verschiedeneAnwender und Anwendungszwecke.

So ist beispielsweise das Kapitel zur Systemprogrammierung von besonde-rer Wichtigkeit für Systemprogrammierer, die ihre Systemprogramme zu-künftig unter Python und nicht mehr mit Shell-Skripting schreiben wollen.

NumPy ist insbesondere für Mathematiker und Ingenieure von besonde-rem Interesse, die gerne numerische Probleme effizient mit Python bear-beiten wollen, also beispielsweise „numerisches Lösen von Gleichungssys-temen”, „Matrizenmultiplikation” oder „Nullstellen von Polynomen”.

Die Listen-Abstraktionen, ebenso wie die lambda-, map-, filter- und reduce-Funktionen, bieten eine faszinierende Möglichkeit, die Programmierungauf ein abstrakteres Level zu bringen. Dadurch kann man komplexere Pro-bleme mit geringerem Programmieraufwand lösen. Dennoch kann mandie gleichen Programme auch ohne diese Techniken schreiben.

Aber das einführende Kapitel über Testverfahren und Debugging ist vonallgemeinem Interesse. Im Prinzip hätten wir dieses Kapitel ebenso gut inden ersten Teil nehmen können.

Außerdem behandeln wir in einem Kapitel „Reguläre Ausdrücke”, die na-hezu unerlässlich sind, wenn man Textverarbeitung betreibt. Man brauchtsie auch, wenn man aus Log- oder Parameterdateien bestimmte Informa-tionen herausfiltern will.

Teil III: In diesem Teil wenden wir das im 1. Teil gelernte Wissen in umfassendenBeispielen an. Dabei verwenden wir viele kleine Beispiele und Übungsauf-gaben, die wir bereits im 1. Teil behandelt haben. Sie werden sehen, wie

Page 23: 3446435476_Pytho

1.3 Zielgruppe des Buches 3

man Funktionen, wenn man sie gut konzipiert hat, in anderen Program-men wiederverwenden kann.

Teil IV: Programmieren lernen ist vor allen Dingen eine aktive Tätigkeit. Nur einBuch zu lesen und Beispiele nachzuvollziehen genügt nicht. Deshalb fin-den Sie zu den meisten Kapitel interessante und lehrreiche Übungsaufga-ben, die den Stoff vertiefen. In diesem Teil des Buches finden Sie nun dieausführlichen Musterlösungen mit Erläuterungen zu diesen Aufgaben.

1.3 Zielgruppe des BuchesBeim Schreiben eines Buches hat man ein Gegenüber vor sich, eine Person, die das Ge-schriebene liest, mögen und verstehen soll. Diese Person kann sowohl eine Frau als auchein Mann sein, dennoch werden wir im Text meistens zum Beispiel nur vom „Benutzer”1

sprechen. Wörter wie „BesucherInnen” sehen unserer Meinung nach hässlich aus, und ob-jektiv betrachtet erschweren sie die Lesbarkeit eines Textes. Ebenso hemmen Formulie-rungen der Art „Als Benutzerin oder Benutzer gilt .... er oder sie .... sollten sein oder ihr ...”den Lesefluss und lenken vom eigentlichen Ziel ab, also Python schnell und problemlos zuerlernen.

Natürlich hatten wir beim Schreiben aber nicht nur eine Person im Blickfeld, sondern eineganze Leserschar. Da sind zum einen die totalen Programmieranfänger, die Sachverhalteerklärt haben wollen, die erfahrene Programmierer anderer Sprachen vielleicht als „trivi-al” oder „selbstverständlich” bezeichnen würden. Aber hier ist ein Buch wohl dem Prä-senzunterricht, also wenn Lehrer und Lernende am gleichen Ort zusammen sind, deutlichüberlegen: Ist einem der Stoff eines Abschnittes oder sogar Kapitels bereits vertraut, kannman es einfach überspringen, bevor man sich zu langweilen beginnt. Das Gleiche könnendann auch die totalen Anfänger tun, wenn wir auf Ähnlichkeiten, aber auch generelle Un-terschiede in der Vorgehensweise von Python im Vergleich zu anderen Sprachen eingehen.Damit können wir also im ersten Teil des Buches sowohl totale Programmieranfänger alsauch erfahrenere Programmierer anderer Sprachen zufrieden stellen.

In der obigen Aufzählung fehlt aber noch eine wichtige Gruppe, nämlich diejenigen, dieschon Erfahrungen mit Python haben. Dies ist eine Gruppe mit einer breiten Streuung:Angefangen mit denjenigen, die bereits ein wenig reingeschnuppert haben, gefolgt vonsolchen, die bereits kleine oder auch größere Programme geschrieben haben, bis hin zujenen, die sich als Experten bezeichnen.

Experten, die alles wissen oder zu wissen glauben, können dieses Buch als Nachschlage-werk benutzen, wenn sie mal etwas vergessen haben sollten oder sich in einer Sache maldoch nicht so ganz sicher sind. Der umfangreiche Index in diesem Buch macht das Auffin-den besonders einfach und erlaubt es damit, dieses Buch außerdem als Referenz zu ver-wenden, auch wenn es keinesfalls unter diesem Aspekt geschrieben worden ist. Insbeson-dere im zweiten Teil befinden sich viele Themen, die auch Anwendern von Python nochunbekannt sein könnten.

1 als Übersetzung des englischen Fachbegriffs „user”

Page 24: 3446435476_Pytho

4 1 Einleitung

Wenn Programmieranfänger oder auch Programmierumsteiger von anderen Program-miersprachen erfolgreich den ersten Teil des Buches bearbeitet haben, sollten sie in derLage sein, auch den zweiten Teil zu verstehen. Vielleicht mit Ausnahme des Kapitels überdie Systemprogrammierung, weil dieses Kapitel Vorkenntnisse über Betriebssysteme, vorallem Linux und Unix, voraussetzt.

1.4 Programmieren lernen „interaktiv”Wie bereits erwähnt, floss in dieses Buch die jahrelange Erfahrung sowohl in der Theorieund Praxis des Programmierens allgemein, aber vor allem auch die Vermittlung des Stof-fes in zahlreichen kleinen und großen Kursen mit unterschiedlichsten Besuchertypen ein.Aber ein Buch zu schreiben, stellt dennoch eine neue Herausforderung dar. Beim Buchfehlt leider die direkte Interaktion zwischen dem, der das Wissen vermittelt, und dem Ler-nenden. Vor Ort kann man sofort sehen, wenn sich bei einem Teilnehmer die Stirn run-zelt, große Fragezeichen erscheinen oder wenn sich jemand aus Stress das Ohrläppchenzu reiben beginnt. Dann weiß man als erfahrener Dozent, dass es höchste Zeit ist, ein paarzusätzliche Beispiele und Analogien hinzuzunehmen oder dass man den Stoff nochmalsin anderen – möglicherweise auch einfacheren – Worten erklären sollte. Beim Schreibeneines Buches muss man diese möglichen Klippen vorhersehen und die nötigen zusätzli-chen Übungen und Beispiele an den entsprechenden Stellen bereitstellen. Aber was beivielen Lesern nun hilft, diese Klippen zu umgehen, führt bei anderen vielleicht zu Lange-weile und Ungeduld, denn sie empfinden diese zusätzlichen Erklärungen, Übungen oderBeispiele möglicherweise als Zeit- oder Platzvergeudung.

Das Grundproblem ist halt, dass es sich bei einem Buch nicht um ein interaktives Mediumhandelt. Aber dank des Internets können wir Ihnen diese Interaktivität dennoch bieten. Imnächsten Abschnitt finden Sie die Adressen, wo Sie Hilfe und zusätzliche Informationenzum Buch finden.

1.5 Download der Beispiele und HilfeAlle im Buch verwendeten Beispiele finden Sie auf zum Download unter

http://www.python-kurs.eu/buch/beispiele/

Auch wenn wir das Buch so geschrieben haben, dass Sie ohne zusätzliche Hilfe auskom-men sollten, wird es dennoch hier und da mal ein Problem geben, wo Sie sich vielleichtfest gebissen haben. Wenn Sie das Glück haben, in einer Umgebung zu arbeiten, in der esandere Python-Programmierer gibt, haben Sie es natürlich gut. Aber viele Leserinnen undLeser haben nicht diesen Vorteil. Dann könnte sich ein Besuch unserer Webseite beson-ders lohnen: http://www.python-kurs.eu/buch/ Dort finden Sie ein Korrekturverzeichnis,zusätzliche bzw. aktualisierte Übungen und sonstige Hilfestellungen. Außerdem finden Siehier auch ein Forum, in dem Sie Ihr Problem schildern können, sodass andere Ihnen hel-

Page 25: 3446435476_Pytho

1.6 Anregungen und Kritik 5

fen können. Ansonsten ist natürlich Google, wie in so vielen anderen Situationen auch, einwilliger Helfer.

Falls Sie glauben, eine Ungenauigkeit oder einen Fehler im Buch gefunden zu haben, kön-nen Sie auch gerne eine E-Mail direkt an den Autor schicken: [email protected].

Natürlich gilt dies auch, wenn Sie Anregungen zum Buch geben wollen. Leider können wirjedoch – so gerne wir es auch tun würden – keine individuellen Hilfen zu speziellen Proble-men geben. Dazu soll unser Forum dienen.

1.6 Anregungen und KritikTrotz großer Sorgfalt können wir sicherlich nicht verhindern, dass sich der eine oder andereFehler eingeschlichen hat. Wenn Sie also Kritik, Anmerkungen oder auch Wünsche haben,senden Sie uns einfach eine E-Mail an [email protected]. Wir werden versuchen, diesalles in kommenden Auflagen zu berücksichtigen. Die jeweils aktuellsten Ergänzungen undweitere Informationen können Sie unter http://www.python-kurs.eu/buch/ finden.

Bernd Klein

im Mai 2013

Page 26: 3446435476_Pytho

TEIL IEinführung in Python 3 –Für Ein- und Umsteiger

,

Page 27: 3446435476_Pytho
Page 28: 3446435476_Pytho

2 Kommandos undProgramme

2.1 Erste Schritte

2.1.1 Linux

Bei den meisten Linux-Distributionen ist Python bereits vorinstalliert, und damit ist auchdie interaktive Python-Shell direkt verfügbar. Der Interpreter wird üblicherweise im Ver-zeichnis /usr/bin/ (aber manchmal auch in /usr/local/bin) installiert, aber diese Informa-tion benötigen wir jetzt noch nicht.

Der einfachste Weg, um mit Python unter Linux zu arbeiten, besteht darin, dass man zuerstein Terminal startet wie beispielsweise ein „xterm” oder ein „gnome-terminal”.

BILD 2.1 Gnome-Terminal

In diesem Terminal – im Bild sehen Sie ein Gnome-Terminal – tippen Sie „python3” einund drücken die Eingabetaste. Damit starten Sie die interaktive Python-Shell. Python mel-det sich mit Informationen über die installierte Version. Der Interpreter steht nun mit demsogenannten Eingabeprompt (»>) für Eingaben bereit. Man kann jetzt beliebigen Python-Code eingeben, der nach dem Quittieren mit der Eingabetaste (Return-Taste) sofort ausge-führt wird.

$ python3Python 3.2.3 (default, Oct 19 2012, 19:53:57)

Page 29: 3446435476_Pytho

10 2 Kommandos und Programme

[GCC 4.7.2] on linux2Type "help", "copyright", "credits" or "license" for more information.>>>

Bitte beachten Sie, dass Sie keinesfalls die Ziffer 3 hinter „python” vergessen dürfen. WennSie nur „python” eingeben, starten Sie Python in einer 2er-Version.

$ pythonPython 2.7.3 (default, Sep 26 2012, 21:53:58)[GCC 4.7.2] on linux2Type "help", "copyright", "credits" or "license" for more information.>>>

Um die Beispiele in unserem Buch nachvollziehen zu können benötigen Sie jedoch Pythonin einer 3er-Version.1

2.1.2 Windows

Unter Windows muss Python erst installiert werden. Die neuesten Versionen findet manunter www.python.org. Wir haben die Version 3.2.2 von ActiveState installiert.

Python starten unter Windows

1 Also eine Version 3.x, d.h. Python 3.0, 3.1, 3.2, 3.3 usw.

Page 30: 3446435476_Pytho

2.2 Herkunft und Bedeutung des Begriffes interaktive Shell 11

Nachdem Python installiert worden ist, findet man unter „Alle Programme” einen Eintrag„Python 3.2” oder „Python 3.3”, unter dem sich die Untereinträge „IDLE (Python GUI)” und„Python (command line)” bzw. „Python Interactive Shell” befinden.

Um die nachfolgenden Beispiele und Übungen nachvollziehen zu können, starten Sie bit-te die Python-Kommandozeile, also „Python (command line)” bzw. „Python InteractiveShell”. Sie erhalten ein schwarzes Terminal-Fenster.

Alternativ kann man auch mit IDLE arbeiten. Dabei handelt es sich um eine Mini-Entwick-lungsumgebung, auf die wir hier jedoch nicht eingehen wollen.

2.2 Interaktive ShellWenn die Aktivitäten des vorigen Kapitels erfolgreich waren, sollten Sie sich nun sowohlunter Linux als auch unter Windows in der interaktiven Python-Shell befinden.

Der Begriff „interaktiv” kommt vom lateinischen „inter agere”. Das Verb „agere” bedeutetunter anderem „tun”, „treiben”, „betreiben”, „handeln”, und „inter” bezeichnet die zeitli-che und örtliche Position zu Dingen und Ereignissen, also „zwischen” zwei oder „inmitten”(oder „unter”) vielen Objekten, Personen oder Ereignissen sein. Also heißt „inter agere” da-zwischen handeln. In diesem Sinne steht die interaktive Shell zwischen dem Anwender unddem Betriebssystem (Linux, Unix, Windows oder anderen) bzw. dem zu interpretierendenProgramm (z.B. Python). Die interaktive Shell steht aber auch zeitlich zwischen den einzel-nen Aktionen, d.h. zwischen den einzelnen Befehlen und Kommandos. Der Benutzer gibteinen Befehl ein, die Shell führt diesen unverzüglich aus, liefert sofort das Ergebnis undwartet dann auf den nächsten Befehl.

In Englisch steht das Wort „shell” für eine Schale, einen Panzer oder ganz allgemein ei-ne Hülle oder Schutzhülle. „shell” bezeichnet auch ein Schneckenhaus und das Gehäuse,das eine Muschel umschließt. Ebenso liegt eine Shell auch zwischen einem Betriebssys-tem und dem Benutzer. Wie eine Muschelschale schützt sie einerseits das Betriebssystemvor dem Benutzer, und andererseits erspart sie dem Benutzer die Benutzung der „primi-tiven” und schwer verständlichen Basisfunktionen, indem es ihm komfortable Befehle zurKommunikation mit dem Computer zur Verfügung stellt.

Auch die Programmiersprache Python bietet dem Anwender eine komfortable Kom-mandozeilenschnittstelle, die sogenannte Python-Shell, manchmal auch als interaktivePython-Shell bezeichnet. Man könnte meinen, dass es sich bei dem Begriff „interaktiveShell” um eine Tautologie handelt, da ja, so wie oben beschrieben, Shells immer interaktivsind. Dies ist jedoch nicht so: Es gibt auch vor allem im Umfeld von Linux und Unix Shells,die als Subshell aufgerufen werden und nicht interaktiv ausgeführt werden.

2.2.1 Erste Schritte in der interaktiven Shell

Wir nehmen nun an, dass Sie entweder unter Linux oder unter Windows vor einer lauffähi-gen Python-Shell sitzen, die mit dem blinkendem Cursor hinter dem Prompt „»>” auf IhreEingabe wartet.

Page 31: 3446435476_Pytho

12 2 Kommandos und Programme

Unsere Experimente mit der Python-Shell starten wir mit der Eingabe eines beliebigenarithmetischen Ausdrucks wie z.B. „4 * 5.2” oder „4 * 12 / 3”. Ein Ausdruck wird mit demDrücken der Eingabetaste (return) sofort ausgeführt, und das Ergebnis wird in der nächs-ten Zeile ausgegeben:

>>> 4 * 5.321.2>>> 4 * 12 / 316.0>>>

Die interaktive Shell erlaubt eine Bequemlichkeit, die innerhalb eines Python-Skripts nichtzulässig ist. In einem Programm hätten wir das Ergebnis des obigen Ausdrucks nur mittelseiner print-Anweisung ausgeben können:

>>> print(4 * 5.3)21.2

Python kennt wie die meisten anderen Programmiersprachen die Regel „Punktrechnunggeht vor Strichrechnung”, wie wir im folgenden Beispiel sehen können:

>>> 1 + (42 * 2)85>>> 1 + 42 * 285>>>>>> (1 + 42) * 286

Der jeweils letzte Ausgabewert wird vom Interpreter in einer speziellen Variablen automa-tisch gespeichert. Der Name der Variable ist einfach ein Unterstrich, also „_”. Das Ergebnisder letzten Berechnung kann man sich also damit wieder ausgeben lassen:

>>> _

85>>>

Der Unterstrich kann im Prinzip wie eine normale Variable benutzt werden:

>>> _ * 3255>>>

Dies gilt allerdings nicht in einem Python-Skript oder Python-Programm! In einem Python-Programm hat der Unterstrich nicht diese Bedeutung.

Auch Strings lassen sich mit oder ohne print in der interaktiven Shell ausgeben:

>>> print("Hello World")Hello World>>> "Hello World"'Hello World'>>>

Page 32: 3446435476_Pytho

2.3 Verlassen der Python-Shell 13

Die Python-Shell bietet noch einen Komfort, den man von den Linux-Shells gewöhnt ist.Es gibt einen Befehlsstack, in dem alle Befehle der aktuellen Sitzung gespeichert werden.Mit den Tasten „↑” und „↓” kann man sich in diesem Stack nach unten („↑”), also zu älterenBefehlen, und nach oben („↓”), also wieder in Richtung neuester Befehl, bewegen.

2.3 Verlassen der Python-Shell

BILD 2.2 Emergency Exit

Wir haben zwar gerade erst damit begonnen, inder Python-Shell „herumzuspielen”, dennoch wol-len sicherlich einige das Programm bereits verlas-sen. Ctrl-C, – was vielen Linux- und Unix-Benutzernhäufig als Erstes einfällt, – funktioniert nicht! DenPython-Interpreter kann man per Kontrollsequenznur mit Ctrl-D wieder verlassen. Alternativ dazukann man die Python-Shell mittels Eingabe derFunktion exit() verlassen. Die Klammern hinter exitsind ab Python 3 zwingend notwendig.

Alle Einstellungen wie beispielsweise Variablen oder eingegebene Funktionsdefinitionengehen beim Verlassen der Python-Shell verloren und sind beim nächsten Start nicht mehrvorhanden.

2.4 Benutzung von VariablenIn der Python-Shell kann man auch ganz einfach Variablen benutzen. Wenn Sie genau wis-sen wollen, was es mit Variablen und Datentypen auf sich hat, empfehlen wir, in Kapitel 4(Datentypen und Variablen) weiterzulesen.

Das folgende Beispiel wendet sich an Benutzer, die bereits ein wenig Erfahrung mit Pro-grammierung haben. Man kann also ganz einfach Werte in Variablen speichern. Variablen-namen bedürfen keiner besonderen Kennzeichnung wie in anderen Sprachen wie zum Bei-spiel das Dollarzeichen in Perl:

>>> maximal = 124>>> breite = 94>>> print(maximal - breite)30>>>

Page 33: 3446435476_Pytho

14 2 Kommandos und Programme

2.5 Mehrzeilige AnweisungenBis jetzt haben wir noch nicht über mehrzeilige Anweisungen gesprochen. Anfänger wer-den hier vielleicht Probleme haben, da noch einige Grundlagen zum Verständnis fehlen,und sollten deshalb besser mit dem folgenden Kapitel weitermachen.

Wir demonstrieren, wie die interaktive Shell mit mehrzeiligen Anweisungen wie zum Bei-spiel For-Schleifen umgeht.

>>> l = ["A",42,78,"Just a String"]>>> for character in l:... print(character)...A4278Just a String>>>

Achtung: Nachdem man die Zeile „for character in l:” eingetippt hat, wartet die interak-tive Shell im Folgenden auf eingerückte Zeilen. Dies teilt uns die Shell mit einem neuenPrompt mit: Statt dem gewohnten „>>> ” erhalten wir nun eine Folge von drei Punkten„... ”. Wir müssen also alle Anweisungen, die mit der Schleife ausgeführt werden sollen, umdie gleiche Anzahl von Leerzeichen einrücken, mindestens jedoch ein Leerzeichen! Wennwir fertig sind, müssen wir nach der letzten Code-Zeile zweimal die Eingabetaste drücken,bevor die Schleife wirklich ausgeführt wird.

2.6 Programme schreiben

BILD 2.3 Hallo Welt

Wir haben bisher ein wenig mit der inter-aktiven Python-Shell herumprobiert, abernoch kein richtiges Programm geschrie-ben. Bei der Programmierung ist es gene-rell wichtig, möglichst in kleinen Schrit-ten vorzugehen. Deshalb wird unser ers-tes Programm in Python auch nur sehr we-nig tun. Das Programm soll lediglich einenkurzen Text ausgeben, nachdem es gestar-tet worden ist. Für diesen Text wird in fastallen Einführungen zu Programmierspra-chen die Zeichenkette2 „Hallo Welt” oderauf Englisch „Hello World” verwendet. Wirwollen dieser Tradition folgen.

2 Eine Folge von Zeichen wird als „Zeichenkette” oder meistens als „String” bezeichnet.

Page 34: 3446435476_Pytho

2.6 Programme schreiben oder schnell mal der Welt “Hallo” sagen 15

Im Folgenden wollen wir zeigen, was in der Programmiersprache C++ nötig ist, um ein sokleines Programm zum Laufen zu bringen. Sie können diesen kurzen Exkurs in die Pro-grammiersprache C++ bedenkenlos überspringen. Wir bringen dieses Beispiel nur, damitman später erkennen kann, wie leicht es ist, in Python im Vergleich zu C++ zu program-mieren.

Folgender Programm-Code ist in C++ nötig, um ein Programm zu schreiben, was nach demStart nur den Text „Hello World” in einer Zeile ausgibt:

#include <iostream>using namespace std;

int main(){

cout << "Hello World!\n";return 0;

}

Wenn man dieses Programm in einer Datei gespeichert hat, also z.B. „hello.cpp”, muss mandas Programm kompilieren, um ein lauffähiges Programm zu erhalten.3 Dies geschieht mitfolgendem Befehl, der ein lauffähiges Programm unter dem Namen „hello” erzeugt:

bernd@saturn:~/bodenseo/python/beispiele$ g++ -o hello hello.cpp

Im Folgenden starten wir das Programm „hello” und erhalten die gewünschte Ausgabe:

bernd@saturn:~/bodenseo/python/beispiele$ ./helloHello World!bernd@saturn:~/bodenseo/python/beispiele$

Deutlich einfacher können wir ein solches Programm unter Python erstellen.

Schreiben Sie in einem Editor4 Ihrer Wahl die folgende Zeile und speichern Sie sie unterdem Namen „hello.py” ab:

print("Hello World")

Unter Linux oder Unix starten wir dieses Programm am einfachsten, wenn wir uns in einemTerminal in das Verzeichnis begeben, in dem wir die Datei hello.py abgespeichert haben.Dort können wir dann das Programm mit Kommando „python3 hello.py” ausführen.Im Folgenden sehen wir dieses Vorgehen inklusive Ergebnis. Dabei nehmen wir an, dassSie die Datei in einem Unterverzeichnis python_beispiele Ihres Homeverzeichnissesabgespeichert hatten:

bernd@venus:~$ cd ~/python_beispiele/bernd@venus:~/python_beispiele$ python3 hello.pyHallo Welt

3 Näheres zu Compiler, Interpreter, Maschinen und Skriptsprachen erfahren Sie in Kapitel 3 (Bytecode undMaschinencode).

4 Unter Windows könnte das beispielsweise „Notepad” sein, unter Linux oder einem anderem Unix-Systemempfiehlt sich der vi, emacs, kate oder gedit.

Page 35: 3446435476_Pytho

16 2 Kommandos und Programme

Unter Windows können wir ähnlich vorgehen. Wir starten erst das Programm „Eingabe-aufforderung”. Dort bewegen wir uns in unser Verzeichnis mit den Python-Programmen,in das wir unser hello.py abgespeichert haben.

Hello World unter Python in Windows-Eingabeaufforderung

Wir haben gesehen, dass die nötige Syntax5 unter Python deutlich geringer ist als unterC++, d.h. man muss lediglich den Text mit einer print-Funktion aufrufen. Anschließend istauch kein explizites Kompilieren6 nötig. Man kann das Programm einfach so starten.

Fragen zum Verständnis:

■ Wie kann man ein Python-Programm schreiben?■ Wie startet man ein Python-Programm?■ Wie kann man das Produkt aus 3.5 und 7.8 am einfachsten mit Python bezeichnen?

5 Etymologisch leitet sich das Wort Syntax aus dem altgriechischen Wort „syntaksis” ab. „syn” bedeutet „zu-sammen”, und „taksis” steht für „Reihenfolge” oder „Ordnung”. Unter Syntax versteht man in der Linguistikdie Lehre vom Satzbau, also sozusagen die „zusammen”- oder „gesamt”-Reihenfolge der einzelnen Satztei-le. In der Informatik versteht man unter der Syntax einer Programmiersprache die Regeln oder das Systemvon Regeln, mit denen man gültige Programme der Sprache beschreiben kann.

6 Man beachte das Wort „explizit”. Beim Start des Programmes wird nämlich implizit gegebenenfalls eineKompilierung vorgenommen. Auf diese gehen wir im nächsten Kapitel näher ein.

Page 36: 3446435476_Pytho

3 Bytecode undMaschinencode

3.1 EinführungDieses Kapitel können Sie gerne beim Einstieg in die Materie auch überspringen, da dasVerständnis für die folgenden Kapitel nicht dringend notwendig ist. Hier geht es um allge-meine Fragen: Handelt es sich bei Python um eine Programmier- oder eine Skriptsprache?Wird ein Python-Programm übersetzt oder interpretiert? Kann der gleiche Python-Codeauf verschiedenen Rechnern oder Betriebssystemen laufen? Worin liegt der Zusammen-hang zwischen Python, Jython oder Java?

3.2 Unterschied zwischen Programmier-und Skriptsprachen

Die Frage, worin der Unterschied zwischen Programmier- und Skriptsprachen liegt, lässtsich nicht einfach beantworten. Sehr häufig werden die Begriffe Skript und Programm na-hezu synonym verwendet. Es gibt Fälle, in denen es eindeutig ist: Hat man beispielsweiseein Problem in C gelöst, so wird allgemein das Ergebnis als „Programm” bezeichnet. Kaumjemand würde ein C-Programm als Skript bezeichnen. Bei Shells wie z.B. der Bourne- oderder Bash-Shell spricht man nahezu immer von Skripten und nicht von Programmen. Wür-de man von einem Bourne-Shell-Programm sprechen, so klingt das mindestens so falsch,als bezeichnete man ein Fahrrad als Moped. Doch worin liegt der Unterschied zwischeneinem Shell-Skript und einem C-Programm?

3.3 Interpreter- oder CompilerspracheCompilersprache ist eine Programmiersprache, deren Programme vor ihrer Ausführungvollständig in Maschinencode, also Binärcode, übersetzt werden.1

1 Manchmal bezeichnet man auch eine Sprache, in der ein Compiler geschrieben wird, als Compilersprache

Page 37: 3446435476_Pytho

18 3 Bytecode und Maschinencode

TABELLE 3.1 Unterschiede zwischen Skripten und Programmen

Skripte ProgrammeEin Skript besteht typischerweisenur aus „wenigen” Zeilen Code,häufig weniger als 100 Zeilen.

Programme können klein sein, also beispielsweiseweniger als Hundert Zeilen Code, aber sie können auchaus mehreren Tausend oder gar Millionen Codezeilenbestehen.

Skripte werden unmittelbarausgeführt. Sie werden nichtübersetzt (bzw. compiliert),sondern interpretiert.

Programme in Programmiersprachen wie C und C++müssen immer in Maschinencode übersetzt werden,bevor sie ausgeführt werden können.

Bei einer Interpretersprache werden die Programme Befehl für Befehl interpretiert undausgeführt.

Die Programmiersprachen C und C++ sind reine Compilersprachen, d.h. jedes Programmmuss zuerst in Maschinencode übersetzt werden, bevor es ausgeführt werden kann. Spra-chen wie Basic und VB werden interpretiert.

Man liest leider allzu häufig, dass es sich bei Python um eine interpretierte Sprache han-delt. Dies ist nicht richtig. Aber bei Python handelt es sich auch nicht um eine Compiler-sprache im „klassischen” Sinne. Python-Programme werden sowohl übersetzt als auch in-terpretiert. Startet man ein Python-Programm, wird zuerst der Programmcode in Bytecodeübersetzt. Bei Bytecode handelt es sich um Befehle für eine virtuelle Maschine. Bei Pythonspricht man von der PVM („Python Virtual Machine).2 Dieser Code ist unabhängig vonrealer Hardware, aber kommt dem Code von Maschinensprachen schon relativ nahe. Die-ser Bytecode wird dann für einen bestimmten Maschinencode interpretiert. Das bedeutet,dass der Bytecode plattformunabhängig ist

BILD 3.1 Python: Compiler und Interpreter

Wenn Sie schon eine Weile mit Python gearbeitet haben, wird Ihnen vielleicht schon malaufgefallen sein, dass es „plötzlich” in dem Verzeichnis, in dem Ihre Python-Skripte stehen,Dateien gibt, die die Endung „pyc” haben. Viele wundern sich und fragen sich, woher die-se Dateien kommen. Wird eine Ihrer Dateien als Modul importiert, z.B. ein Programm mitdem Namen „beispiel.py”, so wird diese Datei in Bytecode übersetzt und der Code in ei-ner Datei „beispiel.pyc” gespeichert. Wenn Sie dies selbst testen wollen, erzeugen Sie docheinfach eine Datei „beispiel.py” mit einer Zeile als Inhalt:

print("Hallo ich komme von beispiel.py")

2 Dieses Konzept entspricht der Vorgehensweise bei Java. Auch dort wird der Code zuerst in Bytecode über-setzt, d.h. Code für die JVM (Java Virtual Machine).

Page 38: 3446435476_Pytho

3.3 Interpreter- oder Compilersprache 19

Geben Sie dann im Python-Interpreter folgende Zeile ein:

$ python3Python 3.2.3 (default, Oct 19 2012, 19:53:57)Type "help", "copyright", "credits" or "license" for more information.>>> import beispielHallo ich komme von beispiel.py>>>

In Ihrem Verzeichnis finden Sie anschließend ein Unterverzeichnis „__pycache__ „beispiel.cpython-32.pyc”

Page 39: 3446435476_Pytho

4 Datentypen und Variablen

4.1 Einführung

BILD 4.1 Variablen als Container

Haben Sie bereits in Sprachen wie Cund C++ programmiert? Deswegen glau-ben Sie, dass Sie bereits genug über Da-tentypen und Variablen wissen? Vorsichtist geboten! Sie wissen sicherlich viel, dasstimmt, aber wahrscheinlich nicht genug,wenn es um Python geht. Deshalb lohnt essich auf jeden Fall, hier weiterzulesen.

Es gibt gravierende Unterschiede in derArt, wie Python und C Variablen behan-deln. Vertraute Datentypen wie Ganzzah-len (Integer), Fließkommazahlen (floatingpoint numbers) und Strings sind zwar inPython vorhanden, aber auch hier gibt eswesentliche Unterschiede zu C und C++.

Möchte man Listen oder assoziative Arrays in C oder C++ verwenden, dann muss man siesich selbst mühsam von Grund auf inklusive Zugriffs- und Suchfunktionen programmierenoder schwer verständliche Libraries verwenden.

Dieses Kapitel ist also sowohl für Anfänger als auch für fortgeschrittene Programmierer zuempfehlen.

Eine Variable im allgemeinsten Sinne ist einfach ein Behälter (Container) zur Aufbewah-rung von bestimmten Werten, also z.B. Strings oder Zahlen. Wie der Name sagt, sind Varia-blen „variabel”, d.h. sie können ihren Wert ändern. Man kann im Verlauf des Programmsauf diese Variablen oder genauer: auf den Wert ihres Inhalts zugreifen, oder ihnen einenneuen Wert zuweisen.

Im Folgenden kreieren wir eine Variable i, indem wir ihr einfach den Wert 42 unter Verwen-dung eines Gleichheitszeichens zuweisen:

>>> i = 42

Man bezeichnet dies als Zuweisung oder auch als Definition einer Variablen.

Page 40: 3446435476_Pytho

22 4 Datentypen und Variablen

Anmerkung: Obige Anweisung darf man nicht als mathematisches Gleichheitszeichen se-hen, sondern als „der Variablen i wird der Wert 42 zugewiesen”, d.h. der Inhalt von i ist nachder Zuweisung 42. Man kann die Anweisung also wie folgt „lesen”: „i soll sein 42”.

Wir können nun diese Variable zum Beispiel benutzen, indem wir ihren Wert mit printausgeben lassen:

>>> print("Wert von i: ", i)Wert von i: 42>>>

Wir können Sie aber auch auf der rechten Seite einer Zuweisung in einem Rechenausdruckverwenden, dessen Ergebnis dann einer Variablen zugewiesen wird:

>>> x = i / 3.0 + 5.8>>> print(x)19.8>>>

Man kann eine Variable auch gleichzeitig auf der rechten und der linken Seite einer Zuwei-sung benutzen. Im Folgenden erhöhen wir den Wert der Variablen i um 1:

>>> i = i + 1>>> print(i)43>>>

4.2 Statische und dynamischeTypdeklaration

In Programmiersprachen unterscheidet man zwischen verschiedenen Datentypen. So be-zeichnet man Zahlen wie -2, -1, 0, 1, 2 als ganze Zahlen, Ganzzahlen oder meistens mit demenglischen Wort Integer. Zahlen wie 0.0676 oder 13.897 werden als Fließkommazahlen oderFloat-Zahlen bezeichnet.1

Wer Erfahrungen mit C, C++, Java oder ähnlichen Sprachen hat, hat dort gelernt, dass man,bevor man eine Variable verwenden darf, ihr einen Typ zuordnen muss.

Der Datentyp einer Variablen muss im Programm festgelegt werden und zwar bevor dieVariable benutzt oder definiert wird. Dies sind dann beispielsweise in einem C-Programmso aus:

int i, j;float x;

x = i / 3.0 +5.8;

1 Man beachte, dass in Programmiersprachen als Dezimaltrennzeichen immer ein Punkt anstelle eines Kom-mas verwendet wird.

Page 41: 3446435476_Pytho

4.2 Statische und dynamische Typdeklaration 23

Während des Programmlaufes können sich dann die Werte für i, j und x ändern, aber ihreTypen sind für die Dauer des Programmlaufes fest auf int im Falle von i und j und float fürdie Variable x festgelegt.

Dies bezeichnet man als „statische Typdeklaration”, da bereits der Compiler den Typ fest-legt und während des Programmablaufes keine Änderungen des Typs mehr vorgenommenwerden können.

In Python sieht dies anders aus. Zunächst einmal bezeichnen Variablen in Python keinenbestimmten Typ, und deshalb benötigt man in Python keine Typdeklaration. Benötigt manim Programm beispielsweise eine Variable i mit dem Wert 42 und dem Typ Integer, so er-reicht man dies einfach mit der folgenden Anweisung:

>>> i = 42

Damit hat man automatisch eine Variable vom Typ Integer deklariert und definiert. Mankann dann im weiteren Verlauf des Programmes der Variablen i auch beliebige Werte vonanderen Datentypen zuweisen:

>>> i = 42>>> i = "Hallo">>> i = [3,9,17]>>>

Dennoch ordnet Python in jedem Fall einen speziellen Typ oder genauer gesagt eine spe-zielle Klasse der Variablen zu. Dieser Datentyp wird aber automatisch von Python erkannt.Diese automatische Typzuordnung, die auch während der Laufzeit erfolgen kann, bezeich-net man als „dynamische Typdeklaration”. Mit der Funktion type können wir uns den je-weiligen Typ ausgeben lassen:

>>> i = 42>>> type(i)<class 'int'>>>> i = "Hallo">>> type(i)<class 'str'>>>> i = [3,9,17]>>> type(i)<class 'list'>>>>

Während sich bei statischen Typdeklarationen, also bei Sprachen wie C und C++, nur derWert, aber nicht Typ einer Variablen während eines Laufes ändert, kann sich bei dynami-scher Typdeklaration, also in Python, sowohl der Wert als auch der Typ einer Variablenändern.

Aber Python achtet auf Typverletzungen. Man spricht von einer Typverletzung2, wenn Da-tentypen beispielsweise aufgrund fehlender Zuweisungskompatibilität nicht regelgemäßverwendet werden. In Python kann es nur bei Ausdrücken zu Problemen kommen, da man

2 engl. type conflict

Page 42: 3446435476_Pytho

24 4 Datentypen und Variablen

ja einer Variablen einen beliebigen Typ zuordnen kann. Eine Typverletzung liegt beispiels-weise vor, wenn man eine Variable vom Typ int zu einer Variablen vom Typ str addierenwill. Es wird in diesem Fall ein TypeError generiert:

>>> x = "Ich bin ein String">>> y = 42>>> z = x + yTraceback (most recent call last):File "<stdin>", line 1, in <module>

TypeError: Can't convert 'int' object to str implicitly>>>

Allerdings kann man Integer und Floats – auch wenn es sich um unterschiedliche Da-tentypen handelt – in einem Ausdruck mischen. Der Wert des Ausdrucks wird dann einFloat.

>>> x = 12>>> y = 3.5>>> z = x * y>>> z42.0>>> type(x)<class 'int'>>>> type(y)<class 'float'>>>> type(z)<class 'float'>>>>

4.3 TypumwandlungIn der Programmierung bezeichnet man die Umwandlung eines Datentyps in einen an-deren als Typumwandlung3. Man benötigt Typumwandlungen beispielsweise, wenn manStrings und numerische Werte zu einem Ausgabestring zusammenpacken will:

>>> first_name = "Henry">>> last_name = "Miller">>> age = 20>>> print(first_name + " " + last_name + ": " + str(age))Henry Miller: 20>>>

In dem Beispiel haben wir den Wert von „age” explizit mit der Funktion str in einen Stringgewandelt. Man bezeichnet dies als explizite Typumwandlung. Hätten wir in obigem Bei-

3 engl. type conversion oder cast

Page 43: 3446435476_Pytho

4.4 Datentyp ermitteln 25

spiel nicht den Integer-Wert age in einen String gewandelt, hätte Python einen TypeErrorgeneriert:

>>> print(first_name + " " + last_name + ": " + age)Traceback (most recent call last):File "<stdin>", line 1, in <module>

TypeError: Can't convert 'int' object to str implicitly>>>

Der Text besagt, dass Python keine implizite Typumwandlung von „int” nach „str” vorneh-men kann. Prinzipiell unterstützt Python keine impliziten Typumwandlungen, wie sie inPerl oder PHP möglich sind. Dennoch gibt es Ausnahmen so wie unser Beispiel, in dem wirInteger- und Floatwerte in einem Ausdruck gemischt hatten. Dort wurde der Integer-Wertimplizit in einen Float-Wert gewandelt.

4.4 Datentyp ermittelnIn vielen Anwendungen muss man für ein Objekt bestimmen, um welchen Typ bzw. umwelche Klasse es sich handelt. Dafür bietet sich meistens die eingebaute Funktion „type”an. Man übergibt ihr als Argument einen Variablennamen und erhält den Typ des Objektszurück, auf das die Variable zeigt:

>>> l = [3,5,4]>>> type(l)<class 'list'>>>> x = 4>>> type(x)<class 'int'>>>> x = 4.5>>> type(x)<class 'float'>>>> x = "Ein String">>> type(x)<class 'str'>>>>

Als Alternative zu der Funktion „type” gibt es noch die eingebaute Funktion „isinstance”,die einen Wahrheitswert „True” oder „False” zurückgibt.

isinstance(object, ct)

„object” ist das Objekt, das geprüft werden soll, und „ct” entspricht der Klasse oder demTyp, auf den geprüft werden soll. Im folgenden Beispiel prüfen wir, ob es sich bei dem Ob-jekt x um ein Tupel handelt:

>>> x = (3,89,67)>>> isinstance(x, tuple)True

Page 44: 3446435476_Pytho

26 4 Datentypen und Variablen

In Programmen kommt es häufig vor, dass wir für ein Objekt wissen wollen, ob es sich umeinen von vielen Typen handelt. Also zum Beispiel die Frage, ob eine Variable ein Integeroder ein Float ist. Dies könnte man mit der Verknüpfung „or” lösen:

>>> x = 4>>> isinstance(x,int) or isinstance(x,float)True>>> x = 4.8>>> isinstance(x,int) or isinstance(x,float)True>>>

Allerdings bietet isinstance hierfür eine bequemere Methode. Statt eines Typs gibt man alszweites Argument eine Liste von Typen an:

>>> x = 4.8>>> isinstance(x,(int, float))True>>> x = (89,123,898)>>> isinstance(x,(list,tuple))True>>> isinstance(x,(int, float))False>>>

Page 45: 3446435476_Pytho

5 SequentielleDatentypen

5.1 Übersicht

BILD 5.1 Fotos aus dem Film „The Kiss” von1896

Auch Perl kennt die Datentypen String undList, aber die Leistung von Python be-steht darin, dass man solche Datentypenzu einer logischen Basisklasse „Sequentiel-le Datentypen” zusammenfasst. Eines ha-ben nämlich Strings, Listen und Tupeln ge-meinsam: Es handelt sich jeweils um Da-tentypen, deren Elemente sequentiell an-geordnet sind. Bei Strings handelt es sichdabei um gleichartige Objekte, d.h. Zei-chen, und bei Listen oder Tupeln handeltes sich um beliebige Element.

In Python gibt es deshalb gleichnamigeMethoden oder Zugriffsmethoden für se-quentielle Datentypen, wenn eine solcheOperation für einen Typ erlaubt ist. Bei-spielsweise kann man mit obj[0] auf daserste Element eines sequentiellen Daten-typs zugreifen, egal ob es sich um einenString, eine Liste oder einen Tupel han-delt. Aber man kann obj[0] nicht verän-dern, falls „obj” ein String ist.

Python stellt die folgenden sequentiellen Datentypen zur Verfügung:

5.1.1 Zeichenketten oder Strings

Strings bestehen aus einer Folge – also Sequenz – von beliebigen Zeichen. Um einen Stringin Python zu generieren, werden diese Zeichen in einfache oder doppelte Hochkommatagestellt, also z.B. ’Ich bin ein String’ und “Ich bin auch ein String”.

Page 46: 3446435476_Pytho

28 5 Sequentielle Datentypen

5.1.2 Listen

Listen stellen wie Strings eine sequentielle Anordnung dar. Während Strings jedoch auseiner Sequenz von beliebigen Zeichen aufgebaut sind, bestehen Listen aus einer Sequenzvon beliebigen Objekten, also z.B. Integer-Zahlen, Float-Zahlen, Strings oder wieder Listen.

Beispiele:

x = [3,99,"Ein Text"]y = [ 42, 65 , [45, 89], 88 ]

Wie wir in dem Beispiel gesehen haben, werden Listen mit eckigen Klammern generiert.Die Elemente einer Liste werden durch Kommata getrennt. Im Beispiel können wir aucherkennen, dass nach und vor der eckigen Klammer auch Leerzeichen stehen können, eben-so wie vor und nach einem Komma. Listen können verschachtelt sein, das heißt, dass sieandere Listen als Unterlisten enthalten können.

5.1.3 Tupel

Rein äußerlich unterscheiden sich Listen und Tupel durch die Art der Klammerung, d.h.Listen werden von eckigen Klammern und Tupel von runden Klammern eingefasst. Dievorigen Listenbeispiele sehen in Tupelschreibweise wie folgt aus:

x = (3,99,"Ein Text")y = ( 42, 65 , [45, 89], 88 )

Der wesentliche Unterschied zwischen Listen und Tupel besteht jedoch darin, dass Tupelnicht mehr verändert werden können.

5.1.4 Sequenz von Binärdaten

Man unterscheidet unveränderliche (bytes) und veränderliche (bytearray) Binärdaten.

Auf diesen Datentyp werden wir in einem späteren Kapitel eingehen.

5.2 IndizierungBetrachten wir den String "Hello World": Man sieht, dass die Zeichen eines Strings von linksnach rechts mit 0 beginnend nummeriert sind. Von hinten (rechts) beginnt man mit -1 zuzählen. Jedes Zeichen eines Strings kann so eindeutig angesprochen werden. Dazu werdeneckige Klammern benutzt, wie man im folgenden Beispiel sehen kann:

BILD 5.2 Indizierung eines Strings

Page 47: 3446435476_Pytho

5.2 Indizierung von sequentiellen Datentypen 29

>>> text = "Hallo Kaa">>> print(text[0])H>>> print(text[6])K>>>

Mithilfe von negativen Indizes kann man die Zeichen auch von hinten aus ansprechen,also -1 für das letzte Zeichen, -2 für das vorletzte und so weiter.

>>> text = "Es geht auch von hinten!">>> text[-1]'!'>>> text[-2]'n'>>>

Dies funktioniert bei allen Objekten von sequentiellen Datentypen, also auch bei Listenund Tupeln:

>>> l = [ 42, 65 , [45, 89], 88 ]>>> l[0]42>>> l[2][45, 89]>>> l[2][1]89>>> l[0] = "Ein neuer Wert">>> l['Ein neuer Wert', 65, [45, 89], 88]>>>>>>>>> t = ( 42, 65 , (45, 89), 88 )>>> t[0]42>>> t[2][0]45>>> t[0] = "Tupel lassen sich nicht ändern!"Traceback (most recent call last):File "<stdin>", line 1, in <module>

TypeError: 'tuple' object does not support item assignment>>>

Im obigen Beispiel konnten wir sehen, dass wir nur Listen, aber keine Tupel ändern kön-nen.

Page 48: 3446435476_Pytho

30 5 Sequentielle Datentypen

5.3 Slicing oder AusschneidenMan kann auch Teile eines sequentiellen Datentyps ausschneiden. Im Falle eines Stringserhält man dann einen Teilstring oder bei Listen wieder eine Liste. Im Englischen wird die-ses Ausschneiden als „slicing"bezeichnet. In der Überschrift zu diesem Kapitel benutzenwir sowohl den Begriff „Slicing” als auch „Ausschneiden”, da wir uns nicht für die deutscheÜbersetzung eindeutig entscheiden wollten. Der Begriff „Slicing” wird in der deutschenPython-Literatur allzu häufig benutzt. Deshalb werden wir die beiden Begriffe im Folgen-den synonym benutzen.

Wie bei der Indizierung benutzt der Slicing-Operator eckige Klammer, aber nun werdenstatt einem Wert mindestens zwei Werte erwartet: Anfangswert und Endwert.

Man versteht dies am besten an einem Beispiel:

>>> txt = "Hello World">>> txt[1:5]'ello'>>> txt[0:5]'Hello'>>> txt = "Hello World">>> txt[0:5]'Hello'

Lässt man den Anfangswert weg (z.B. [:5] ), beginnt das Ausschneiden am Anfang desStrings (oder der Liste). Analog kann man auch den Endwert weglassen, um alles bis zumEnde zu übernehmen ( z.B. [6:] ) Lässt man Anfangs- und Endwert weg, erhält man denganzen String (oder entsprechend die ganze Liste oder Tupel) zurück:

'Hello'>>> txt[0:-6]'Hello'>>> txt[:5]'Hello'>>> txt[6:]'World'>>> txt[:]'Hello World'

Das folgende Beispiel zeigt, wie sich dies bei Listen auswirkt:

>>> colours = ['red', 'green', 'blue']>>> colours[1:3]['green', 'blue']>>> colours[2:]['blue']>>> colours[:2]['red', 'green']>>>>>> colours[-1]'blue'

Page 49: 3446435476_Pytho

5.4 Aufgaben 31

Der Slicing-Operator funktioniert auch mit drei Argumenten. Das dritte Argument gibtdann an, das wievielte Argument jedes Mal genommen werden soll, d.h. s[begin, end, step].Ausgegeben werden dann die folgenden Elemente von s: s[begin], s[begin + 1 * step], ...s[begin + i * step], solange (begin + i * step) < end ist. txt[::3] gibt jeden dritten Buchstabeneines Strings aus.

Beispiel:

>>> txt = "Python ist ganz toll">>> txt[2:15:3]'tnsgz'>>> txt[::3]'Ph ta l'

5.4 Aufgaben

1. Aufgabe:Welche sequentiellen Datentypen haben Sie kennengelernt?Lösung: Lösungen zu Kapitel 5 (Sequentielle Datentypen), Seite 379

2. Aufgabe:Wie erhält man das erste Element einer Liste?Lösung: Lösungen zu Kapitel 5 (Sequentielle Datentypen), Seite 379

3. Aufgabe:Wie kann man am einfachsten das letzte Zeichen in einem String ausgeben?Lösung: Lösungen zu Kapitel 5 (Sequentielle Datentypen), Seite 379

4. Aufgabe:Überlegen Sie sich, welche der folgenden Anweisungen korrekt sind. Wir haben dieErgebnisse, die von der interaktiven Python-Shell ausgeben wurden, gelöscht:

>>> t = (4,7,9)>>> s = "Ich bin ein String">>> l = [45,98,"787",[3,4]]>>> t2 = (4,8,[45,98])>>> t[0]>>> t[3]>>> t(3)>>> s[4]>>> s[4] = "x"

Page 50: 3446435476_Pytho

32 5 Sequentielle Datentypen

>>> l[2][0] = "g">>> l[3][0] = "g">>> l>>> t2[2][0] = 42

Lösung: Lösungen zu Kapitel 5 (Sequentielle Datentypen), Seite 380

5. Aufgabe:Welche zwei Sätze verbergen sich hinter dem folgenden scheinbaren Buchstaben-salat:’DIenr diesmt Sdienrn eb eisstte HLielhfree rz,u rd eSre lsbiscthh inlafceh iumnmde rnaucnhs eürbee rofbleürssstieg Mmaaxcihmte..’Hinweis: Benutzen Sie Slicing!Lösung: Lösungen zu Kapitel 5 (Sequentielle Datentypen), Seite 381

Page 51: 3446435476_Pytho

6 Dictionaries

6.1 Dictionaries und assoziative Felder

BILD 6.1 Dictionary

In den vorigen Kapiteln BIS HIER habenwir die sequentiellen Datentypen wie Lis-ten, Strings und Tupel eingeführt. Nunwollen wir eine weitere Kategorie von Da-tentypen genauer untersuchen, den Da-tentyp „Mapping”. In dieser Kategorie gibtes allerdings zur Zeit nur einen imple-mentierten Typ, das Dictionary. Beim Dic-tionary handelt es sich um ein assoziati-ves Feld. Assoziative Felder werden wer-den in verschiedenen Programmierspra-chen mit verschiedenen Namen versehen.So spricht man in Java und C++ von einer Map, in Perl und Ruby von einem Hash und wie inPython bezeichnen auch Smalltalk, Objective-C und C# assoziative Arrays als Dictionaries.

Ein Dictionary besteht aus Schlüssel-Objekt-Paaren. Zu einem bestimmten Schlüssel ge-hört immer ein Objekt. Man kann also die Schlüssel auf die Objekte „abbilden”, daher derKategorienname Mapping.

Dictionaries gehören zu den wichtigsten Datentypen von Python. Kaum ein Programmoder Skript kommt ohne Dictionaries aus. Wie Listen können Dictionaries leicht verändertwerden, außerdem können sie während der Laufzeit beliebig wachsen und schrumpfen.

In diesem Kapitel geht es aber nicht nur um Dictionaries, sondern am Ende beschäftigenwir uns auch noch mit dem Zusammenhang zwischen Listen und Dictionaries, d.h. wirzeigen, wie man aus Dictionaries Listen erzeugt und umgekehrt aus bestimmten ListenDictionaries.

Page 52: 3446435476_Pytho

34 6 Dictionaries

6.2 Definition und BenutzungWir beginnen mit dem wohl einfachsten Fall eines Dictionarys, dem leeren:

>>> empty = {}>>> empty{}

Zu Ehren des Schutzheiligen von Python, "Monty Python", definieren wir nun ein spezi-elles kulinarisches Dictionary. Was wäre Python und auch das Internet ohne „ham”, „egg”und „spam”?

>>> food = {"ham" : "yes", "egg" : "yes", "spam" : "no"}>>> print(food){'egg': 'yes', 'ham': 'yes', 'spam': 'no'}>>> food["ham"]'yes'>>> food["spam"]'no'>>> food["spam"] = "yes">>> print(food){'egg': 'yes', 'ham': 'yes', 'spam': 'yes'}>>>

Im vorigen Beispiel können wir ein paar interessante Dinge über Dictionaries lernen. EinDictionary wird mit geschweiften Klammern eingegeben. Ein Schlüssel-Wert-Paar wird mitDoppelpunkt zwischen Schlüssel und Wert eingegeben. Die verschiedenen Paare werdenmit Komma getrennt eingegeben.

Man kann ein ganzes Directory mit print ausgeben lassen. Dabei ist es zunächst einmalirritierend, dass die Reihenfolge – so wie auch in unserem Beispiel – meist nicht mit derReihenfolge in der Definition des Directories entspricht. In unserer Definition stand alserstes „ham”, während unsere Ausgabe mit „egg” beginnt. Dies ist in Ordnung, da auf demDirectory keine Ordnung definiert ist. Python kann also die Schlüssel-Werte-Paare in einerbeliebigen Reihenfolge ausgeben.

Als Nächstes erkennen wir, dass wir zum Zugriff auf einen bestimmten Schlüsselwert ecki-ge Klammern benutzen. So liefert food["ham"] den Wert für den Schlüssel "ham" zurück,d.h. ’yes’.

Die Zuweisung eines neuen Wertes zu einem bestehenden Schlüssel erfolgt ähnlich wiebei den Listen, nur dass wir keinen Index, sondern einen Schlüssel benutzen. So weisenwir mit der Anweisung food["spam"] = "yes" dem Schlüssel "spam" den neuen Wert’yes’ zu.

Im nächsten Beispiel kreieren wir ein einfaches deutsch-englisches Wörterbuch als Dictio-nary:

>>> en_de = {"red" : "rot", "green" : "grün", "blue" : "blau", "yellow":"gelb"}

>>> print(en_de){'blue': 'blau', 'green': 'grün', 'yellow': 'gelb', 'red': 'rot'}

Page 53: 3446435476_Pytho

6.2 Definition und Benutzung 35

In obigem Dictionary können wir nur über die englischen Schlüssel zugreifen. Möchtenwir beispielsweise wissen, wie „grün” auf Englisch heißt, so geht dies nicht.1 Um nun auchüber deutsche Schlüssel zuzugreifen, müssen wir ein neues Dictionary definieren, in demdie deutschen Wörter als Schlüssel gespeichert sind und die englischen als Werte:

>>> de_en = {"rot" : "red", "grün" : "green", "blau" : "blue", "gelb": "yellow"}

Wie wäre es mit einem weiteren Dictionary, zum Beispiel in Deutsch-Französisch? Versu-chen Sie es doch einmal zur Übung. Falls Sie kein Französisch können: Die Farben lauten:„rouge”, „vert”, „bleu” und „jaune”.

Ein deutsch-französisches Dictionary sieht nun wie folgt aus:

>>> de_fr = {"rot" : "rouge", "grün" : "vert", "blau" : "bleu", "gelb":"jaune"}

Nun sind wir in der Lage, von Englisch nach Französisch zu übersetzen, obwohl wir gar keinEnglisch-Französisch-Wörterbuch haben. So gibt uns de_fr[en_de["red"]] das franzö-sische Wort für „red”, also „rouge”:

>>> en_de = {"red" : "rot", "green" : "grün", "blue" : "blau", "yellow":"gelb"}

>>> de_fr = {"rot" : "rouge", "grün" : "vert", "blau" : "bleu", "gelb":"jaune"}

>>> print("The French word for 'red' is " + de_fr[en_de['red']])The French word for 'red' is rouge>>>

Was passiert, wenn wir auf eine Farbe zugreifen wollen, die gar nicht existiert? Wir probie-ren dies im Folgenden:

>>> en_de = {"red" : "rot", "green" : "grün", "blue" : "blau", "yellow":"gelb"}

>>> en_de["brown"]Traceback (most recent call last):File "<stdin>", line 1, in <module>

KeyError: 'brown'>>>

Wir erzeugen also einen Fehler, einen KeyError, wenn wir auf einen nicht existierendenSchlüssel zuzugreifen versuchen.

Um solche Fehler zu vermeiden, werden häufig bedingte Anweisungen verwendet. Auf die-se werden wir erst in Kapitel 8 (NumPy) eingehen. Dennoch möchten wir Ihnen das typi-sche Vorgehen hier nicht vorenthalten. Anfänger, die noch nie programmiert haben, kön-nen die folgenden paar Abschnitte getrost übergehen, damit sie keine Verständnisproble-me haben.

1 Nicht ohne Programmierung jedenfalls

Page 54: 3446435476_Pytho

36 6 Dictionaries

6.3 Fehlerfreie Zugriffe auf DictionariesMit dem Schlüsselwort „in” kann geprüft werden, ob ein Index in einem Dictionary vor-kommt:

>>> en_de = {"red" : "rot", "green" : "grün", "blue" : "blau"}>>> "red" in en_deTrue>>> "brown" in en_deFalse>>>

Damit kann man mittels einer bedingten Anweisung prüfen, ob eine Farbe bereits alsSchlüssel enthalten ist oder nicht. In dem folgenden kleinen Programm benutzen wirnoch eine weitere Anweisung, auf die wir auch erst später wieder eingehen: die input-Anweisung. Das Programm bleibt bei einer input-Anweisung stehen und druckt den Textaus, der als Argument angegeben wird, also in unserem ersten Aufruf „Farbe?”. Dann kannder Benutzer des Programms eine Eingabe vornehmen, die von input als String zurückge-liefert wird. In unserem Beispiel speichern wir den String in der Variablen „colour”:

en_de = {"red" : "rot", "green" : "grün", "blue" : "blau"}

colour = input("Farbe? ")

if colour in en_de:print("Die Farbe " + colour + " ist ein Schlüssel")print("Der deutsche Wert für " + colour + " ist " + en_de[colour])

else:print("Die Farbe " + colour + " ist noch kein Schlüssel")colour_de = input("Deutsch für " + colour + "? ")en_de[colour] = "braun"print("Nun kennen wir auch " + colour + ":")print(en_de)

Wir starten dieses Programm zweimal: einmal mit einer Farbe, die bereits im Dictionaryenthalten ist, und einmal mit einer, die noch nicht enthalten ist:

$ python3 guarded_dictionary_access.pyFarbe? redDie Farbe red ist ein SchlüsselDer deutsche Wert für red ist rot$ python3 guarded_dictionary_access.pyFarbe? brownDie Farbe brown ist noch kein SchlüsselDeutsch für brown? braunNun kennen wir auch brown:{'blue': 'blau', 'brown': 'braun', 'green': 'grün', 'red': 'rot'}

Page 55: 3446435476_Pytho

6.4 Zulässige Typen für Schlüssel und Werte 37

6.4 Zulässige Typen für Schlüssel undWerte

In einem Dictionary können beliebige Typen als Werte verwendet werden. Bei den Schlüs-seln gilt jedoch die Einschränkung, dass nur Instanzen unveränderlicher (immutable) Da-tentypen verwendet werden können, also z.B. keine Listen und keine Dictionaries. Ansons-ten erhält man eine Fehlermeldung:

<

>>> dic = { [1,2,3]:"abc"}Traceback (most recent call last):File "<stdin>", line 1, in <module>

TypeError: unhashable type: 'list'

Tupel als Schlüssel sind in Ordnung, wie wir im folgenden Beispiel sehen:

>>> dic = { (1,2,3):"abc", 3.1415:"abc"}>>> dic{(1, 2, 3): 'abc'}

6.5 Verschachtelte DictionariesWir zeigen nun, wie man Dictionaries verschachtelt. Verschachtelte Dictionaries werdenhäufig auch mit ihrem englischen Begriff als „Nested Dictionaries” bezeichnet. Um die-ses Verfahren zu demonstrieren, peppen wir unsere Wörterbücher für natürliche Sprachennoch ein wenig auf. Dazu definieren wir ein Dictionary von Dictionaries:

>>> en_de = {"red" : "rot", "green" : "grün", "blue" : "blau", "yellow":"gelb"}

>>> de_fr = {"rot" : "rouge", "grün" : "vert", "blau" : "bleu", "gelb":"jaune"}

>>>>>> dictionaries = {"en_de" : en_de, "de_fr" : de_fr }>>>>>> print(dictionaries["de_fr"]["blau"])bleu>>>

Page 56: 3446435476_Pytho

38 6 Dictionaries

6.6 Methoden auf DictionariesIm Folgenden bezeichnet D ein Dictionary, wenn wir es nicht anders vermerkt haben.

clear(...) Löscht alle Einträge eines Dictionaries.

Beispiel:

>>> en_de = {"red" : "rot", "green" : "grün", "blue" : "blau", "yellow":"gelb"}

>>> en_de.clear()>>> en_de{}>>>

copy(...) D.copy() erzeugt eine flache Kopie von D.2 Anfänger machen häufig den Fehlerzu glauben, dass die Zuweisung eines Dictionarys an eine Variable bereits einer Kopieentspricht. Dass dies nicht so ist, demonstrieren wir im folgenden Beispiel:

>>> en_de = {"red" : "rot", "green" : "grün", "blue" : "blau", "yellow":"gelb"}

>>> d = en_de>>> d["yellow"] = "gelblich">>> d{'blue': 'blau', 'green': 'grün', 'yellow': 'gelblich', 'red': 'rot'}>>> en_de{'blue': 'blau', 'green': 'grün', 'yellow': 'gelblich', 'red': 'rot'}>>>

Wäre „d” in obigem Beispiel eine Kopie, hätte sich der Wert von „en_de” nicht änderndürfen.

Eine flache Kopie erhalten wir mit der Methode copy, wie wir im folgenden Beispielsehen:

>>> en_de = {"red" : "rot", "green" : "grün", "blue" : "blau", "yellow":"gelb"}

>>> d = en_de.copy()>>> d["yellow"] = "gelblich">>> d{'blue': 'blau', 'green': 'grün', 'red': 'rot', 'yellow': 'gelblich'}>>> en_de{'blue': 'blau', 'green': 'grün', 'yellow': 'gelb', 'red': 'rot'}>>>

Auf die besondere Problematik zwischen flacher und tiefer Kopie gehen wir in 13 (Fla-ches und tiefes Kopieren) ein.

fromkeys(...) dict.fromkeys(S[,v]) liefert ein Dictionary zurück mit den Werten des unver-änderlichen sequentiellen Datentyps S als Schlüssel und dem optionalen Wert „v”, der

2 Auf die Unterschiede zwischen flacher und tiefer Kopie gehen wir in Kapitel 13 (Flaches und tiefes Kopieren)ein.

Page 57: 3446435476_Pytho

6.6 Methoden auf Dictionaries 39

jedem Schlüssel automatisch zugeordnet wird. Wird „v” nicht angegeben, werden alleWerte auf None gesetzt:

Beispiel:

>>> food = ("ham", "eggs", "spam")>>> d = dict.fromkeys(food)>>> d{'eggs': None, 'ham': None, 'spam': None}>>> d = dict.fromkeys(food, "enjoy")>>> d{'eggs': 'enjoy', 'ham': 'enjoy', 'spam': 'enjoy'}>>>

get(...) D.get(k[,d]) liefert D[k] zurück, falls k in D ist, ansonsten d. Der Default-Wert istNone.

Beispiel:

>>> en_de = {"red" : "rot", "green" : "grün", "blue" : "blau", "yellow":"gelb"}

>>> en_de.get("red")'rot'>>> en_de.get("brown")>>> en_de.get("brown","spam")'spam'>>>

items(...) D.items() liefert ein Mengen-ähnliches Objekt vom Type „dict_item” zurück, waseiner View der Schlüssel-Werte-Paare entspricht.

Beispiel:

>>> en_de = {"red" : "rot", "green" : "grün", "blue" : "blau", "yellow":"gelb"}

>>> x = en_de.items()>>> type(x)<class 'dict_items'>>>> xdict_items([('blue', 'blau'), ('green', 'grün'), ('yellow', 'gelb'),

('red', 'rot')])>>>

keys(...) D.keys() liefert ein Mengen-ähnliches Objekt vom Type „dict_item” zurück, waseiner View der Schlüssel entspricht.

Beispiel:

>>> en_de = {"red" : "rot", "green" : "grün", "blue" : "blau", "yellow":"gelb"}

>>> x = en_de.keys()>>> type(x)<class 'dict_keys'>>>> xdict_keys(['blue', 'green', 'yellow', 'red'])>>>

Page 58: 3446435476_Pytho

40 6 Dictionaries

pop(...) D.pop(k[,d]) entfernt den Schlüssel „k” zusammen mit seinem Wert aus dem Dic-tionary. Die Methode liefert den Wert D[k] zurück. Entspricht „k” keinem Schlüssel,dann wird ein KeyError generiert, außer pop wurde mit dem optionalen Parameter „d”aufgerufen. In diesem Fall liefert D.pop(k,d) den Wert „d” zurück.

Beispiel:

>>> en_de = {"red" : "rot", "green" : "grün", "blue" : "blau", "yellow":"gelb"}

>>> x = en_de.pop("red")>>> x'rot'>>> en_de{'blue': 'blau', 'green': 'grün', 'yellow': 'gelb'}>>> x = en_de.pop("brown")Traceback (most recent call last):File "<stdin>", line 1, in <module>

KeyError: 'brown'>>> x = en_de.pop("brown","spam")>>> x'spam'>>>

popitem(...) D.popitem() wird ohne Parameter aufgerufen und liefert ein beliebigesSchlüssel-Wert-Paar (k, v) zurück, was dann aus dem Dictionary entfernt wird. Fallsdas Dictionary leer ist, wird ein KeyError generiert.

Beispiel:

>>> en_de = {"red" : "rot", "green" : "grün", "blue" : "blau", "yellow":"gelb"}

>>> en_de.popitem()('blue', 'blau')>>> en_de.popitem()('green', 'grün')>>> en_de.popitem()('yellow', 'gelb')>>> en_de.popitem()('red', 'rot')>>> en_de.popitem()Traceback (most recent call last):File "<stdin>", line 1, in <module>

KeyError: 'popitem(): dictionary is empty'>>>

setdefault(...) D.setdefault(k[,d]) setzt D[k] auf den Wert d, falls der Schlüssel k noch nichtin D enthalten ist. Falls k bereits in D enthalten ist, verändert diese Methode das Dictio-nary D nicht. Falls der optionale Parameter „d” nicht angeben wird, wird D[k] auf denWert None gesetzt, falls der Schlüssel k noch nicht in D enthalten ist. Die Methode liefertden Wert von D[k] zurück.

>>> en_de = {"red" : "rot", "green" : "grün", "blue" : "blau"}>>> en_de.setdefault("brown", "braun")

Page 59: 3446435476_Pytho

6.7 Operatoren 41

'braun'>>> en_de{'blue': 'blau', 'brown': 'braun', 'green': 'grün', 'red': 'rot'}>>> en_de.setdefault("green", "verde")'grün'>>> en_de{'blue': 'blau', 'brown': 'braun', 'green': 'grün', 'red': 'rot'}>>> en_de.setdefault("yellow")>>> en_de{'blue': 'blau', 'brown': 'braun', 'green': 'grün', 'yellow': None, '

red': 'rot'}>>>

update(...) Fügt ein Dictionary d2 zu d hinzu und überschreibt gegebenenfalls die Wertevon bereits vorhandenen Schlüsseln.

Beispiel:

>>> en_de = {"red" : "rot", "green" : "grün", "blue" : "blau"}>>>>>> en_de2 = {"yellow":"gelb", "red":"rötlich"}>>> en_de.update(en_de2)>>> en_de{'blue': 'blau', 'green': 'grün', 'yellow': 'gelb', 'red': 'rötlich'}>>> en_de = {"red" : "rot", "green" : "grün", "blue" : "blau"}>>> en_de2 = {"yellow":"gelb", "red":"rötlich"}>>> en_de2.update(en_de)>>> en_de2{'blue': 'blau', 'green': 'grün', 'red': 'rot', 'yellow': 'gelb'}>>>

6.7 Operatoren auf DictionariesAuch auf Dictionaries ist eine „Länge” definiert. Die Methode len() liefert die Anzahl derSchlüssel-Werte-Paare zurück, was der Anzahl der verschiedenen Schlüssel entspricht.

>>> en_de = {"red" : "rot", "green" : "grün", "blue" : "blau", "yellow":"gelb"}

>>> len(en_de)4>>>

Page 60: 3446435476_Pytho

42 6 Dictionaries

6.8 ZipDiese sehr nützliche Funktion gehört eigentlich nicht in die Dictionary-Klasse. Es ist eineFunktion, die Sequenzen als Ein- und Ausgaben hat. Dennoch behandeln wir sie an dieserStelle, weil wir sie im folgenden Abschnitt benötigen werden.

Mit Zip kann man eine Matrix transponieren.

>>> z1 = [11, 12, 13]>>> z2 = [21, 22, 23]>>> z3 = [31, 32, 33]>>>>>> T = zip(z1, z2, z3)>>> T<zip object at 0x911ffac>>>> list(T)[(11, 21, 31), (12, 22, 32), (13, 23, 33)]>>>>>> M = zip(*T)>>> list(M)[]>>> T = zip(z1, z2, z3)>>> M = zip(*T)>>> list(M)[(11, 12, 13), (21, 22, 23), (31, 32, 33)]>>>

Im obigen Beispiel hatten wir zip mit dem Argument *T aufgerufen. Der Sternchen-Operator kann auf ein Tupel, eine Liste oder ganz allgemein auf einen sequentiellen Da-tentyp bzw. Iterator angewendet werden, um diesen zu „entpacken”.3 Im obigen Beispielwird zip mit den drei Argumenten (11, 21, 31), (12, 22, 32) und (13, 23, 33) aufgerufen. Hät-te man T statt *T geschrieben, wäre zip mit einer Liste bestehend aus den vorigen Tupelnaufgerufen worden, d.h. mit [(11, 21, 31), (12, 22, 32), (13, 23, 33)].

Besonders interessant im Zusammenhang mit Dictionaries ist der Fall, in dem wir zweiflache Listen oder Tupel miteinander verknüpfen.

>>> l1 = [11, 12, 13, 14]>>> l2 = [21, 22, 23, 24]>>> list(zip(l1,l2))[(11, 21), (12, 22), (13, 23), (14, 24)]>>>

Sind die Argumente von zip verschieden lang, dann arbeitet zip nur auf den Argumentenbis zur kleinsten Länge. Alle darüber hinaus existierenden Elemente werden ignoriert.

>>> l1 = [11, 12, 13]>>> l2 = [21, 22, 23, 24]>>> list(zip(l1,l2))

3 Auf diesen Mechanismus gehen wir in Kapitel Funktionen, Seite 115 näher ein.

Page 61: 3446435476_Pytho

6.9 Dictionaries aus Listen erzeugen 43

[(11, 21), (12, 22), (13, 23)]>>>

6.9 Dictionaries aus Listen erzeugenBetrachten wir die beiden folgenden Listen:

>>> gerichte = ["Pizza", "Sauerkraut", "Paella", "Hamburger"]>>> laender = ["Italien","Deutschland","Spanien","USA"]

Auch wenn mittlerweile Hamburger weltweit verbreitet sind, wenn vielleicht mehr Pizzenin Deutschland gegessen werden und Sauerkraut nicht mehr zu den deutschen Lieblings-gerichten gehört, bilden wir dennoch ein Dictionary, was alte Stereotypen bedient: Bei derEingabe eines Landes als Schlüssel soll als Wert das landesspezifische Gericht erscheinen.

Schauen wir uns zunächst einmal an, was passiert, wenn wir obige Listen mit zip verknüp-fen:

>>> list(zip(laender, gerichte))[('Italien', 'Pizza'), ('Deutschland', 'Sauerkraut'), ('Spanien', 'Paella

'), ('USA', 'Hamburger')]>>>

Wenn Sie im vorigen Kapitel unter der Dictionary-Methode item nachschauen, werden Sieerkennen, dass die von item zurückgelieferte Liste von 2er-Tupel obiges Aussehen hat. Des-halb ist es nicht verwunderlich, dass ein dict-Casting auf das obige Ergebnis das gewünsch-te Dictionary zurückliefert:

>>> landesueblich = dict(zip(laender, gerichte))>>> landesueblich{'Spanien': 'Paella', 'Italien': 'Pizza', 'Deutschland': 'Sauerkraut', '

USA': 'Hamburger'}>>> landesueblich["Deutschland"]'Sauerkraut'>>> landesueblich["USA"]'Hamburger'>>>

Page 62: 3446435476_Pytho

44 6 Dictionaries

6.10 Aufgaben

1. Aufgabe:

BILD 6.2 Datei-Menü

Stellen Sie sich vor, wir wollen in einem Programm die Bezeichnungen für ein Datei-menü internationalisieren, also z.B. in Deutsch, Englisch, Französisch und Italienischanbieten.Wir wollen ein verschachteltes Dictionary verwenden, in dem wir die Übersetzung fürdie Menüpunkte zum Speichern, Speichern unter neuem Namen, Neue Datei öffnen,Öffnen und Drucken angeben.Zum Übersetzen der Begriffe:Englisch – Deutsch – Französisch – ItalienischFile – Datei – Fichier – FileNew – Neu – Nouveau – NuovoOpen – Öffnen – Ouvrir – ApriSave – Speichern – Enregistrer – SalvaSave as – Speichern unter – Enregistrer sous – Salva comePrint Preview – Druckansicht – Apercu avant impressioner – Anteprima di stampaPrint – Drucken – Imprimer – StampaClose – Schließen – Fermer – ChiudiExit – Verlassen – Quitter – EsciLösung: Lösungen zu Kapitel 6 (Dictionaries), Seite 381

Page 63: 3446435476_Pytho

6.10 Aufgaben 45

2. Aufgabe:

BILD 6.3 Schachbrett

Überlegen Sie sich eine Darstellung des Schachbretts als Dictionary. Die Schlüsseldieses Dictionaries sollen die Felder des Schachbrettes sein und die Werte die Infor-mation, welche Figur sich auf einem Feld befindet.Das Ergebnis könnte also beispielsweise von der Form („König”, „schwarz”) sein.Ein Schachbrett besteht bekanntlich aus 64 Feldern, die in 8 Zeilen und 8 Spaltenangeordnet sind. Ein Feld des Schachbretts kann mit einem Tupel bezeichnet werden.Die erste Komponente dieses Tupels entspricht den Spalten des Spielfeldes. Diesewerden mit Buchstaben zwischen „a” und „h” von links nach rechts gekennzeichnet.Die zweite Komponente stellt eine Zahl zwischen 1 und 8 dar.Lösung: Lösungen zu Kapitel 6 (Dictionaries), Seite 382

Page 64: 3446435476_Pytho

7 Mengen

7.1 Übersicht

BILD 7.1 Mengen Diagramme

Auch wenn die Mengenlehre über lan-ge Zeit heftig kritisiert wurde und immernoch kritisiert wird, ist sie ein wesentlichesGebiet der Mathematik. Die heutige Ma-thematik ist in der Terminologie der Men-genlehre formuliert und baut auf derenAxiomen auf. Die Mengenlehre ist nochein recht junges Gebiet der Mathematik.Der deutsche Mathematiker Georg Cantor(1845 - 1918) begründete die Mengenleh-re mit seinem 1874 erschienenen Artikel„Über eine Eigenschaft des Inbegriffes al-ler reellen algebraischen Zahlen”. Anfangs,also bis ins Jahr 1977, bezeichnete er üb-rigens die Mengenlehre noch als „Mannig-faltigkeitslehre”.

1895 gab er folgende Definition einer Men-ge: „Unter einer ,Menge’ verstehen wir je-de Zusammenfassung M von bestimmten wohlunterschiedenen Objekten m unserer An-schauung oder unseres Denkens (welche die ,Elemente’ von M genannt werden) zu einemGanzen.”

Eine Menge kann beliebige Elemente enthalten: zum Beispiel Zahlen, Zeichen, Buchsta-ben, Wörter, Namen oder sogar andere Mengen. Eine Menge wird in der Mathematik übli-cherweise mit einem Großbuchstaben bezeichnet.

7.2 Mengen in PythonDer Datentyp „set”, der ein sogenannter „collection”-Typ ist, ist in Python seit Version 2.4.enthalten. Ein set enthält eine ungeordnete Sammlung von einmaligen und unveränderli-

Page 65: 3446435476_Pytho

48 7 Mengen

chen Elementen. In anderen Worten: Ein Element kann in einem set-Objekt nicht mehr-mals vorkommen, was bei Listen und Tupel jedoch möglich ist. Beim Datentyp set handeltes sich um die Python-Implementierung von Mengen, wie sie aus der Mathematik bekanntsind.

7.2.1 Sets erzeugen

Will man eine Menge erzeugen, so ist dies in Python3 sehr einfach. Man bedient sich dergewohnten mathematischen Schreibweise.

>>> staedte = {'Hamburg', 'München', 'Frankfurt', 'Berlin'}>>> print(staedte){'München', 'Berlin', 'Frankfurt', 'Hamburg'}>>> 'Berlin' in staedteTrue>>> 'Köln' in staedteFalse

Im obigen Beispiel mag vielleicht verwundern, dass wir die geschweiften Klammern fürMengen verwenden, da wir diese ja bisher für Dictionaries verwendet hatten.

Nun wollen wir zeigen, was passiert, wenn wir ein Tupel mit wiederholt auftretenden Ele-menten an die set-Funktion übergeben – in unserem Beispiel tritt die Stadt Paris mehrmalsauf:

>>> cities = set(("Paris", "Lyon", "London","Berlin","Paris","Birmingham"))

>>> cities{'Paris', 'Birmingham', 'Lyon', 'London', 'Berlin'}>>>

Wie erwartet, gibt es keine doppelten Einträge in unserer resultierenden Menge.

Im folgenden Beispiel wird mit dem Casting-Operator set ein String in seine Zeichen ver-einzelt, um die Menge der im String enthaltenen Buchstaben zu erhalten:

>>> x = set("A Python Tutorial")>>> x{'A', ' ', 'i', 'h', 'l', 'o', 'n', 'P', 'r', 'u', 't', 'a', 'y', 'T'}>>> type(x)<class 'set'>>>>

7.2.2 Mengen von unveränderlichen Elementen

Sets sind so implementiert, dass sie keine veränderlichen (mutable) Objekte erlauben. Dasfolgende Beispiel demonstriert, dass wir beispielsweise keine Listen als Elemente habenkönnen:

Page 66: 3446435476_Pytho

7.3 Frozensets 49

>>> cities = set((("Python","Perl"), ("Paris", "Berlin", "London")))>>> cities = set((["Python","Perl"], ["Paris", "Berlin", "London"]))Traceback (most recent call last):File "<stdin>", line 1, in <module>

TypeError: unhashable type: 'list'

7.3 FrozensetsAuch wenn sets keine veränderlichen Elemente enthalten können, sind sie selbst verän-derlich. Wir können zum Beispiel neue Elemente einfügen:

>>> cities = {"Frankfurt", "Basel","Freiburg"}>>> cities.add("Strasbourg")>>> cities{'Freiburg', 'Frankfurt', 'Basel', 'Strasbourg'}

Frozensets sind wie sets, aber sie können nicht verändert werden. Sie sind also unverän-derlich (immutable):

>>> cities = frozenset(["Frankfurt", "Basel","Freiburg"])>>> cities.add("Strasbourg")Traceback (most recent call last):File "<stdin&module>", line 1, in <module>

AttributeError: 'frozenset' object has no attribute 'add'>>>

7.4 Operationen auf „set”-Objekten

7.4.1 add(element)

Mit der Methode add fügt man ein Objekt in eine Menge als neues Element ein, falls esnoch nicht vorhanden ist. Es gilt zu beachten, dass es sich dabei um ein unveränderlichesElement handelt. Im folgenden Beispiel fügen wir einen String ein:

>>> colours = {"red","green"}>>> colours.add("yellow")>>> colours{'green', 'yellow', 'red'}>>> colours.add(["black","white"])Traceback (most recent call last):File "<stdin&module>", line 1, in <module>

TypeError: unhashable type: 'list'>>>

Page 67: 3446435476_Pytho

50 7 Mengen

Selbstverständlich wird ein Objekt nur dann als neues Element eingefügt, wenn es nochnicht enthalten ist. Ist es bereits enthalten, hat der Aufruf der Methode keine Auswirkun-gen.

7.4.2 clear()

Alle Elemente einer Menge werden entfernt. Die Menge ist also anschließend leer, wie wirim folgenden Beispiel sehen:

>>> cities = {"Stuttgart", "Konstanz", "Freiburg"}>>> cities.clear()>>> citiesset()>>>

7.4.3 copy

copy erzeugt eine flache Kopie einer Menge, die zurückgeliefert wird. Wir demonstrierendie Benutzung anhand eines Beispiels:

>>> more_cities = {"Winterthur","Schaffhausen","St. Gallen"}>>> cities_backup = more_cities.copy()>>> more_cities.clear()>>> cities_backup{'St. Gallen', 'Winterthur', 'Schaffhausen'}>>>

Nur für diejenigen, die glauben, dass eine einfache Zuweisung auch genügen könnte:

>>> more_cities = {"Winterthur","Schaffhausen","St. Gallen"}>>> cities_backup = more_cities>>> more_cities.clear()>>> cities_backupset()>>>

Die Zuweisung „cities_backup = more_cities” erzeugt nur einen Pointer, d.h. einen weite-ren Namen für das gleiche Objekt.

7.4.4 difference()

Diese Methode liefert die Differenz von zwei oder mehr Mengen zurück. Wir illustrierendies wie immer an einem Beispiel:

>>> x = {"a","b","c","d","e"}>>> y = {"b","c"}>>> z = {"c","d"}

Page 68: 3446435476_Pytho

7.4 Operationen auf „set”-Objekten 51

>>> x.difference(y)set(['a', 'e', 'd'])>>> x.difference(y).difference(z){'a', 'e'}>>>

Statt die Methode difference zu benutzen, hätten wir auch den Operator „-” benutzen kön-nen:

>>> x - y{'a', 'e', 'd'}>>> x - y - z{'a', 'e'}>>>

7.4.5 difference_update()

Die Methode difference_update entfernt alle Elemente einer anderen Menge aus einerMenge. „x.difference_update()” ist gleichbedeutend mit „x = x - y”

>>> x = {"a","b","c","d","e"}>>> y = {"b","c"}>>> x.difference_update(y)>>> x{'a', 'e', 'd'}>>>>>> x = {"a","b","c","d","e"}>>> y = {"b","c"}>>> x = x - y>>> x{'a', 'e', 'd'}>>>

7.4.6 discard(el)

Das Element el wird aus einer Menge entfernt, falls es enthalten ist. Falls el nicht in derMenge enthalten ist, passiert nichts.

>>> x = {"a","b","c","d","e"}>>> x.discard("a")>>> x{'c', 'b', 'e', 'd'}>>> x.discard("z")>>> x{'c', 'b', 'e', 'd'}>>>

Page 69: 3446435476_Pytho

52 7 Mengen

7.4.7 remove(el)

Funktioniert wie discard(), aber falls el nicht in der Menge enthalten ist, wird ein Fehlergeneriert, d.h. ein KeyError:

>>> x = {"a","b","c","d","e"}>>> x.remove("a")>>> x{'c', 'b', 'e', 'd'}>>> x.remove("z")Traceback (most recent call last):File "<stdin&module>", line 1, in <module>

KeyError: 'z'>>>

7.4.8 intersection(s)

Liefert die Schnittmenge von s und der Instanzmenge zurück.

>>> x = {"a","b","c","d","e"}>>> y = {"c","d","e","f","g"}>>> x.intersection(y){'c', 'e', 'd'}>>>

Dies kann auch mit dem „&”-Zeichen formuliert werden:

>>> x = {"a","b","c","d","e"}>>> y = {"c","d","e","f","g"}>>> x.intersection(y){'c', 'e', 'd'}>>>>>> x = {"a","b","c","d","e"}>>> y = {"c","d","e","f","g"}>>> x & y{'c', 'e', 'd'}>>>

7.4.9 isdisjoint()

Diese Methode liefert True zurück, wenn zwei Mengen eine leere Schnittmenge haben.

>>> x = {"a","b"}>>> y = {"c","d"}>>> z = {"b","c"}>>> x.isdisjoint(y)True>>> x.isdisjoint(z)False

Page 70: 3446435476_Pytho

7.4 Operationen auf „set”-Objekten 53

7.4.10 issubset()

x.issubset(y) liefert True zurück, falls x eine Untermenge von y ist. „<” kann statt des Aufrufsder Methode verwendet werden.

>>> x = {"a","b","c","d","e"}>>> y = {"c","d"}>>> x.issubset(y)False>>> y.issubset(x)True>>>>>> x > yTrue>>> y < xTrue>>>

7.4.11 issuperset()

x.issuperset(y) liefert True zurück, falls x eine Obermenge von y ist. „>” kann statt des Auf-rufs der Methode verwendet werden.

>>> x = {"a","b","c","d","e"}>>> y = {"c","d"}>>> x.issuperset(y)True>>>>>> x > yTrue>>>

7.4.12 pop()

pop() liefert ein beliebiges Element der Menge zurück. Dieses Element wird dabei aus derMenge entfernt. Die Methode erzeugt einen KeyError, falls die Menge leer ist.

>>> x = {"a","b","c","d","e"}>>> x.pop()'a'>>> x.pop()'c'

Page 71: 3446435476_Pytho

8 Verzweigungen

8.1 Übersicht

BILD 8.1 Verzweigungen

Bisher sind wir in der Lage, Programme zu schrei-ben, in denen eine Anweisung auf die andere folgtund diese auch in dieser Reihenfolge ausgeführtwerden. Aber nur wenige Probleme lassen sichdurch einen linearen Programmablauf kontrollie-ren. Man möchte beispielsweise einen bestimmtenTeil des Programms nur dann ausführen, wenn be-stimmte Bedingungen zutreffen, oder ein andererTeil soll gegebenenfalls mehrmals ausgeführt wer-den. Dazu bietet jede Programmiersprache Kon-trollstrukturen, die sich in zwei Kategorien unter-scheiden lassen: Verzweigungen und Schleifen.

In diesem Kapitel beschäftigen wir uns mit bedingten Anweisungen, wie Verzweigungenauch genannt werden. Dies sind Codeteile, die unter einer bedingten Bedingung ausge-führt werden. Ist die Bedingung nicht erfüllt, wird dieser Code nicht ausgeführt. Andersausgedrückt: Eine Verzweigung legt fest, welcher von zwei (oder auch mehr) Programm-teilen (Alternativen) in Abhängigkeit von einer (oder mehreren) Bedingungen ausgeführtwird. Bedingte Anweisungen und Verzweigungen werden in Programmiersprachen (eben-so wie die Schleifen) den Kontrollstrukturen zugerechnet, weil mit ihrer Hilfe ein Pro-gramm auf verschiedene Zustände, die sich aus Eingaben und Berechnungen ergeben,reagieren kann.

8.2 Bedingte Anweisungen in PythonDie einfachste Form einer if-Anweisung sieht in Python wie folgt aus:

if bedingung:anweisungen

Page 72: 3446435476_Pytho

56 8 Verzweigungen

Der eingerückte Codeblock wird nur ausgeführt, wenn die Bedingung „bedingung” wahr,also True ist.

Ganz allgemein sieht es so aus:

if bedingung:anweisungen

elif bedingung:anweisungen

...elif bedingung:

anweisungenelse:

anweisungen

Wir wollen uns im folgenden Beispiel-Skript mit der einfachen if-Anweisung beschäftigen,also ohne „elif” und „else”.

person = input("Nationalität? ")if person == "französisch":

print("Préférez-vous parler français?")if person == "italienisch":

print("Preferisci parlare italiano?")

Obiges kleines Python-Skript hat zwei Nachteile. Wenn jemand als Nationalität „franzö-sisch” angegeben hat, wird wie zu erwarten die print-Anweisung des ersten if ausgeführt.Anschließend wird aber in der nächsten if-Anweisung geprüft, ob „person” möglicherwei-se „italienisch” ist, was in diesem Fall unmöglich ist. Dieses Problem kann man mit einem„elif” lösen. Der Ausdruck hinter dem „elif” wird nur geprüft, wenn der Ausdruck beimletzten „elif” oder „if” „False” war.

Zum anderen gibt es aber auch noch andere Nationalitäten. Wir haben keine Anweisungenfür diesen Fall. Wir können als Default Englisch einstellen. Dazu benötigen wir noch denelse-Zweig der if-Anweisung. Der Block unter dem „else” wird immer ausgeführt, wenn dieAusdrücke hinter dem „if” und den „elif”s nicht zutrafen (True).

person = input("Nationalität? ")

if person == "französisch":print("Préférez-vous parler français?")

elif person == "italienisch":print("Preferisci parlare italiano?")

else:print("You are neither Italian nor French,")print("so we have to speak English.")

8.3 Beispiel: HundejahreKinder und Hundeliebhaber stellen sich häufig die Frage, wie alt ihr Hund wohl wäre, wenner kein Hund sondern ein Mensch wäre. Landläufig rechnet man Hundejahre in Menschen-

Page 73: 3446435476_Pytho

8.4 Wahr oder falsch: Bedingungen in Verzweigungen 57

jahre um, indem man das Alter des Hundes mit 7 multipliziert. Je nach Hundegröße undRasse sieht die Umrechnung jedoch etwas komplizierter aus, z.B.:

■ Ein einjähriger Hund entspricht in etwa einem 14-jährigen Menschen

■ 2 Jahre eines Hundes entsprechen 22 Jahre eines Menschens.

■ Ab dann entspricht ein Hundejahr jeweils 5 Menschenjahren.

Das folgende kleine Beispielprogramm verlangt die Eingabe für das Alter des Hundes undberechnet nach obiger Regel das Alter in Menschenjahren:

age = input("Alter des Hundes: ")printif age < 0:

print "Das stimmt wohl kaum!"elif age == 1:

print "entspricht ca. 14 Jahre"elif age == 2:

print "entspricht ca. 22 Jahre"elif age > 2:

human = 22 + (age -2)*5print "Menschenjahre", human

###raw_input('press Return>')

8.4 Wahr oder falsch„Was nicht verboten ist, ist erlaubt”: So funktioniert unser Gesetz. Dadurch wird vermie-den, dass es Lücken im Gesetz gibt. Ähnlich verhält es sich auch mit den Bedingungen inPython. Alles, was nicht False ist, hat den Wert True. Wir brauchen also nur zu definieren,was „falsch”, also False ist:

■ numerische Null-Werte(0, 0L, 0.0, 0.0+0.0j),

■ der boolsche Wert False,

■ leere Zeichenketten,

■ leere Listen, leere Tupel,

■ leere Dictionaries

■ sowie der spezielle Wert None.

Alle anderen Werte betrachtet Python als „wahr”, also True.

Page 74: 3446435476_Pytho

58 8 Verzweigungen

8.5 Aufgaben

1. Aufgabe:Ein Kalenderjahr hat bekanntlich 365 oder 366 Tage. Nach dem Gregorianischen Ka-lenderjahr dauert ein Jahr exakt 365,2425 Tage, also 365 Tage, 5 Stunden, 49 Minu-ten, 12 Sekunden oder anders ausgedrückt 31.556.952 Sekunden. Man sieht, dassein Jahr also grob gesprochen einen Viertel Tag länger ist als 365 Tage. Um diesenUnterschied zu beheben, hat man Schalttage eingefügt. Alle vier Jahre wird mit dem29. Februar ein Schalttag eingefügt. Allerdings machen wir damit einen neuen klei-nen „Fehler”, denn nun haben wir einen Hundertertstel Tag zuviel. Aus diesem Grundewird alle Hundert Jahre – und zwar wenn die Jahreszahl durch Hundert teilbar ist –auf einen Schalttag verzichtet. So war beispielsweise das Jahr 1900 kein Schaltjahr,obwohl es durch vier teilbar war. Man benötigt jedoch noch alle 400 Jahre ein weite-res Korrektiv, dann wird ein Schalttag eingefügt, obwohl die Jahreszahl durch Hundertteilbar ist. Nach dieser Regel war das Jahr 2000 ein Schaltjahr.Schreiben Sie nun ein Python-Programm, das berechnet, ob eine gegebene Jahres-zahl ein Schaltjahr ist oder nicht.Lösung: Lösungen zu Kapitel 8 (Verzweigungen), Seite 383

2. Aufgabe:Auch bei der zweiten Aufgabe bleiben wir bei der Zeitrechnung. Zu einer gegebenenZeit in Stunden, Minuten und Sekunden sollen Sie eine Sekunde hinzuzählen.Aus der Uhrzeit 12:59:59 wird beispielsweise nach der Addition von einer Sekunde dieZeit 13:00:00 Uhr.Lesen Sie Stunden-, Minuten- und Sekundenwerte der Uhrzeit in drei Integerwerteein.Das Programm soll die neue Uhrzeit in der obigen Form ausgeben, also hh:mm:ss.Lösung: Lösungen zu Kapitel 8 (Verzweigungen), Seite 384

Page 75: 3446435476_Pytho

9 Schleifen

9.1 Übersicht

BILD 9.1 Karussell

Schleifen werden benötigt, um einen Co-deblock, also eine oder mehrere Python-Anweisungen, wiederholt auszuführen.Einen solchen Codeblock bezeichnet manauch als Schleifenkörper oder Body. InPython gibt es zwei Schleifentypen: diewhile-Schleife und die for-Schleife.

Die meisten Schleifenarten, die in Pro-grammiersprachen Verwendung finden,enthalten einen Zähler oder ganz allge-mein Variablen, die im Verlauf der Be-rechnungen innerhalb des Schleifenkör-pers ihre Werte ändern. Außerhalb, dasheißt noch vor dem Beginn der Schleife,werden diese Variablen initialisiert. Vor je-dem Schleifendurchlauf wird geprüft, obein Ausdruck, in dem diese Variable oderVariablen vorkommen, wahr ist. DieserAusdruck bestimmt das Endekriterium derSchleife. Solange die Berechnung diesesAusdrucks wahr ist, d.h. „True” liefert, wirdder Rumpf der Schleife ausgeführt. Nach-dem alle Anweisungen des Schleifenkör-pers durchgeführt worden sind, springt die Programmsteuerung automatisch zum Anfangder Schleife, also zur Prüfung des Endekriteriums zurück und prüft wieder, ob diese noch-mals erfüllt ist. Wenn ja, geht es wie oben beschrieben weiter, ansonsten wird der Schlei-fenkörper nicht mehr ausgeführt, und es wird mit dem Rest des Skripts fortgefahren. Dasunten stehende Diagramm zeigt dies schematisch.

Page 76: 3446435476_Pytho

60 9 Schleifen

9.2 while-Schleife

Das folgende Skript, das wir in der interaktiven Shell direkt eintippen können, gibt die Zah-len von 1 bis 10 unter Benutzung einer while-Schleife aus:

>>> i = 1>>> while i <= 10:... print(i)... i += 1...12345678910>>>

Auch die Summe der Zahlen von 1 bis 100 lässt sich mittels einer while-Schleife leicht be-rechnen, wie wir im folgenden Programm sehen können:

#!/usr/bin/env python3

n = 100sum = 0i = 1while i <= n:

sum = sum + ii = i + 1

print("Summe von 1 bis %d: %d" % (n,sum))

Page 77: 3446435476_Pytho

9.3 die Alternative im Erfolgsfall: else 61

9.3 else-Teil

C-Programmierern1 wie auch Programmierern anderer Programmiersprachen kommt esmeist sehr merkwürdig vor, wenn sie ein else ohne zugehöriges if finden. Schleifen kön-nen, aber müssen nicht von einem else-Teil gefolgt werden. Die Anweisungen im else-Teilwerden ausgeführt, sobald die Bedingung nicht mehr erfüllt ist. Sicherlich fragen sich eini-ge nun, worin dann der Unterschied zu einer normalen while-Schleife liegt. Hätte man dieAnweisungen nicht in den else-Teil gesteckt, sondern einfach hinter die while-Schleife ge-stellt, wären sie ja auch genauso ausgeführt worden. Der else-Teil einer while-Schleife wirderst zusammen mit dem break-Kommando sinnvoll. Normalerweise wird eine Schleife nurbeendet, wenn die Bedingung im Schleifenkopf erfüllt ist. Mit break kann man aber eineSchleife vorzeitig verlassen.

Im folgenden Beispiel, einem einfachen Zahlenratespiel, kann man erkennen, dass inKombination mit einem break der else-Zweig durchaus sinnvoll sein kann. Nur wenn diewhile-Schleife regulär beendet wird, d.h. der Spieler die Zahl erraten hat, gibt es einenGlückwunsch. Gibt der Spieler auf, d.h. break, dann wird der else-Zweig der while-Schleifenicht ausgeführt.

import randomn = 20to_be_guessed = int(n * random.random()) + 1guess = 0while guess != to_be_guessed:

guess = int(input("New number: "))if guess > 0:

if guess > to_be_guessed:print("Number too large")

else:print("Number too small")

else:print("Sorry, that you're giving up!")break

1 Es gibt auch eine else-Schleife in C und C++. Diese wird aber nur selten verwendet.

Page 78: 3446435476_Pytho

62 9 Schleifen

else:print("Congratulation. You made it!")

Die Ausgabe einer Spielsitzung könnte beispielsweise so aussehen:

$ python3 number_game.pyNew number: 12Number too smallNew number: 15Number too smallNew number: 18Number too largeNew number: 17Number too smallCongratulation. You made it!$

9.4 For-SchleifeWie auch die while-Schleife ist die for-Schleife eine Kontrollstruktur, mit der eine Grup-pe von Anweisungen (ein Block) wiederholt ausführt werden kann. Die Syntax der for-Schleifen unterscheidet sich in den verschiedenen Programmiersprachen. Ebenso ist dieSemantik einer for-Schleife, also wie sie vom Compiler oder Interpreter zu verstehen bzw.auszuführen ist, von Programmiersprache zu Programmiersprache unterschiedlich. Die„klassische” numerische Schleife, wie sie C und C++ kennt, besitzt eine Schleifenvaria-ble, die mit einem Startwert initialisiert wird und sich nach jedem Durchlauf des Schlei-fenkörpers verändert, d.h. meistens um einen bestimmten Wert (z.B. 1) erhöht oder ver-mindert wird, bis der definierte Zielwert erreicht ist. Man nennt diese Schleifenform auchZählschleife, weil die Schleifenvariable und damit auch der Startwert, der Endwert und dieSchrittweite numerisch sein müssen.

Im folgenden Beispiel sehen wir eine for-Schleife in C, die die Zahlen von 1 bis 100 aus-druckt:

for( i = 0; i < 100; i++)printf("i: %d\n", i);

Auch wenn Sie diese Schleifenform bereits in C oder einer anderen Sprache liebgewonnenhaben, müssen wir Sie leider enttäuschen: Python kennt keine solche for-Schleife. Wohl-gemerkt „keine solche”, aber sehr wohl eine for-Schleife. Die in Python benutzte Art vonfor-Schleife entspricht der in der Bash-Shell oder in Perl verwendeten foreach-Schleife. Beidieser Schleifenart handelt es sich um ein Sprachkonstrukt, mit dessen Hilfe nacheinan-der die Elemente einer Menge oder Liste bearbeitet werden können. Dazu werden sie einerVariable zugewiesen.

Im Folgenden sehen wir die allgemeine Syntax der for-Schleife in Python. Sequenz stehtfür ein iterierbares Objekt.

Page 79: 3446435476_Pytho

9.4 For-Schleife 63

for Variable in Sequenz:Anweisung_1Anweisung_2...Anweisung_n

else:Else-Anweisung_1Else-Anweisung_2...Else-Anweisung_m

Wie bereits gesagt, dient in Python die for-Schleife zur Iteration über eine Sequenz vonObjekten, während sie in vielen anderen Sprachen meist nur „eine etwas andere while-Schleife” ist.

Beispiel einer for-Schleife in Python:

>>> languages = ["C", "C++", "Perl", "Python"]>>> for language in languages:... print(language)...CC++PerlPython>>>

Wie die while-Schleife besitzt auch die for-Schleife einen optionalen else-Block. DieserBlock wird nur ausgeführt, wenn die Schleife nicht durch eine break-Anweisung abgebro-chen wurde. Das bedeutet, dass der else-Block nur dann ausgeführt wird, wenn alle Ele-mente der Sequenz abgearbeitet worden sind.

Trifft der Programmablauf auf eine break-Anweisung, so wird die Schleife sofort verlassenund das Programm nach der Anweisung fortgesetzt, die der for-Schleife folgt, falls es über-haupt noch Anweisungen nach der for-Schleife gibt.

Üblicherweise befindet sich die break-Anweisung wie im folgenden Beispiel innerhalb ei-ner Konditionalanweisung:

edibles = ["ham", "spam","eggs","nuts"]for food in edibles:

if food == "spam":print("No more spam please!")break

print("Great, delicious " + food)else:

print("I am so glad: No spam!")print("Finally, I finished stuffing myself")

Wenn wir obiges Beispiel unter for.py speichern und aufrufen, erhalten wir folgende Aus-gaben:

Page 80: 3446435476_Pytho

64 9 Schleifen

$ python for.pyGreat, delicious hamNo more spam please!Finally, I finished stuffing myself$

Wenn wir „spam” aus der Liste der essbaren Dinge entfernen, erhalten wir folgende Ausga-be:

$ python for.pyGreat, delicious hamGreat, delicious eggsGreat, delicious nutsI am so glad: No spam!Finally, I finished stuffing myself$

Vielleicht ist unsere Abscheu vor dem Dosenfutter „spam” nicht so groß, dass wir sofortaufhören zu essen. In diesem Fall kommt die continue-Anweisung ins Spiel. Im folgendenkleinen Skript benutzen wir continue, um mit dem nächsten Artikel der essbaren Artikelweiterzumachen. „continue” schützt uns davor, „spam” essen zu müssen:

edibles = ["ham", "spam", "eggs","nuts"]for food in edibles:

if food == "spam":print("No more spam please!")continue

print("Great, delicious " + food)# here can be the code for enjoying our food :-)

else:print("I am so glad: No spam!")

print("Finally, I finished stuffing myself")

Die Ausgabe sieht dann wie folgt aus:

$ python for.pyGreat, delicious hamNo more spam please!Great, delicious eggsGreat, delicious nutsI am so glad: No spam!Finally, I finished stuffing myself$

Page 81: 3446435476_Pytho

9.5 Aufgaben 65

9.5 AufgabenFür die erste Aufgabe benötigen wir die römischen Zahlen. Für diejenigen, die sich nicht soganz sicher sind mit römischen Zahlen, geben wir hier die Zuordnungen in der folgendenTabelle.

TABELLE 9.1 Römische Zahlen

Römische Zahl Wert (Dezimalzahl)I 1II 2III 3IV 4V 5VI 6VII 7VIII 8IX 9X 10XI 11. . . . . .XIV 14XV 15XVI 16. . . . . .XIX 19XX 20XXI 21. . . . . .XXIX 29XXX 30XL 40L 50LX 60XC 90C 100CC 200CD 400D 500CM 900M 1000MM 2000

Page 82: 3446435476_Pytho

66 9 Schleifen

1. Aufgabe:Schreiben Sie ein Python-Programm, das eine beliebige römische Zahl in eine „ge-wöhnliche” Dezimalzahl umrechnet.Lösung: 33.4 (1. Aufgabe), Seite 385

2. Aufgabe:

BILD 9.2 Achtung Frösche

In der nächsten Aufgabe lernen wir einen besonderen Frosch kennen, so wie ihn sichnur Mathematiker ausdenken können. Besonders seine Art, eine Straße zu über-queren, macht es zweifelhaft, ob er in der realen Welt lange überleben könnte. Erüberquert eine 2,50 Meter breite Straße wie folgt: Mit dem ersten Sprung legt er dieerstaunliche Distanz von einem Meter zurück, dann springt er wegen zunehmenderErschöpfung mit jedem weiteren Schritt immer nur noch halb so weit wie vorher.Die Entfernung, die er dabei zurücklegt, berechnet sich also als Summe der Werte 1 +0,5 + 0.25 + 0,125 und so weiter.Dies entspricht natürlich in mathematischer Schreibweise der folgenden Notation:

n∑i=1

1

i= 1+ 1

2+ 1

4+ 1

8+ 1

16. . .

Versuchen Sie, mittels eines Python-Programmes herauszubekommen, ob der Frosches auf die andere Straßenseite schafft.Lösung: 33.4 (2. Aufgabe), Seite 386

3. Aufgabe:Der indische Herrscher Shihram tyrannisierte seine Untertanen und stürzte sein Landin Not und Elend. Um die Aufmerksamkeit des Königs auf seine Fehler zu lenken,ohne seinen Zorn zu entfachen, schuf Dahers Sohn, der weise Brahmane Sissa, einSpiel, in dem der König als wichtigste Figur ohne Hilfe anderer Figuren und Bauernnichts ausrichten kann. Der Unterricht im Schachspiel machte auf Shihram einen star-ken Eindruck. Er wurde milder und ließ das Schachspiel verbreiten, damit alle davonKenntnis nähmen. Um sich für die anschauliche Lehre von Lebensweisheit und zu-gleich Unterhaltung zu bedanken, gewährte er dem Brahmanen einen freien Wunsch.Dieser wünschte sich Weizenkörner: Auf das erste Feld eines Schachbretts wollte erein Korn, auf das zweite Feld die doppelte Menge, also zwei, auf das dritte wiederum

Page 83: 3446435476_Pytho

9.5 Aufgaben 67

doppelt so viele, also vier und so weiter. Der König lachte und war gleichzeitig erbostüber die vermeintliche Bescheidenheit des Brahmanen.Als sich Shihram einige Tage später erkundigte, ob Sissa seine Belohnung in Emp-fang genommen habe, musste er hören, dass die Rechenmeister die Menge der Wei-zenkörner noch nicht berechnet hätten. Der Vorsteher der Kornkammer meldete nachmehreren Tagen ununterbrochener Arbeit, dass er diese Menge Getreidekörner imganzen Reich nicht aufbringen könne. Auf allen Feldern eines Schachbretts zusam-men wären es 18.446.744.073.709.551.615 Weizenkörner. Nun stellte er sich dieFrage, wie das Versprechen eingelöst werden könne. Der Rechenmeister half demHerrscher aus der Verlegenheit, indem er ihm empfahl, er solle Sissa ibn Dahir ganzeinfach das Getreide Korn für Korn zählen lassen.2

Berechnen Sie mit Hilfe eines Python-Programmes ohne eine Formel zu Hilfe zu neh-men, wieviele Weizenkörner angehäuft werden müssen.Lösung: 33.4 (3. Aufgabe), Seite 387

2 Die Weizenkornlegende wurde wörtlich von Wikipedia entnommen

Page 84: 3446435476_Pytho

10 Dateien lesenund schreiben

10.1 Dateien

BILD 10.1 Dateien

Man findet wohl kaum jemanden im 21. Jahr-hundert, der nicht wüsste, was eine Datei ist.Auch wenn nicht jeder eine exakte Definitiongeben könnte, also beispielsweise: Eine Dateiist eine Menge von logisch zusammenhängen-den und meist sequentiell geordneten Daten,die auf einem Speichermedium dauerhaft ge-speichert werden und mittels eines Bezeich-ners bzw. Namens wieder identifizierbar unddamit ansprechbar sind. Die Daten einer Da-tei existieren also über die Laufzeit eines Pro-gramms hinaus. Deswegen werden sie auch alsnicht flüchtig oder persistent bezeichnet.

Auch wenn es so scheinen mag: Das Wort Dateiist kein altes deutsches Wort. Es ist eine künstli-che Schöpfung des Deutschen Instituts für Nor-mung (DIN). Die beiden Wörter Daten und Kar-tei wurden zu dem neuen Wort Datei verschmolzen. Ein solches Kunstwort wird übrigensals Portmanteau oder Kofferwort bezeichnet.

Der Inhalt jeder Datei besteht grundsätzlich aus einer eindimensionalen Aneinanderrei-hung von Bits, die normalerweise in Byte-Blöcken zusammengefasst interpretiert werden.Die Bytes erhalten erst durch Anwendungsprogramme und das Betriebssystem eine Be-deutung. Sie entscheiden also, ob es sich um eine Textdatei, ein ausführbares Programmoder beispielsweise ein Musikstück oder ein Bild handelt.

10.2 Text aus einer Datei lesenUnser erstes Beispiel zeigt, wie man Daten aus einer Datei ausliest. Um dies tun zu können,muss man zuerst die Datei zum Lesen öffnen. Dazu benötigt man die open()-Funktion. Mit

Page 85: 3446435476_Pytho

70 10 Dateien lesen und schreiben

der open-Funktion erzeugt man ein Dateiobjekt und liefert eine Referenz auf dieses Objektals Ergebniswert zurück. Die open-Funktion erwartet zwei Parameter: einen Dateinamen,gegebenenfalls mit einem Pfad, und optional kann man den Modus angeben:

open(filename,mode)

Im folgenden Beispiel öffnen wir die Datei „ad_lesbiam.txt” zum Lesen, da der Modus auf„r” (read) gesetzt ist:

fobj = open("ad_lesbiam.txt", "r")

Alternativ kann man die obige Anweisung auch ohne die Angabe des „r” schreiben. Fehltder Modus, wird automatisch Lesen zum Defaultwert:

fobj = open("ad_lesbiam.txt")

Sehr häufig bearbeitet man eine Datei zeilenweise, d.h. man liest eine Datei Zeile für Zeil.Das folgende Programmstück demonstriert, wie man eine Datei zeilenweise einliest. JedeZeile wird dann mit print ausgegeben. Die String-Methode rstrip() entfernt vor der Ausgabeetwaige Leerzeichen und Newlines vom rechten Rand:

fobj = open("ad_lesbiam.txt")for line in fobj:

print(line.rstrip())fobj.close()

Führt man obiges Programm aus, erhält man folgende Ausgabe1:

VIVAMUS mea Lesbia, atque amemus,rumoresque senum severiorumomnes unius aestimemus assis!soles occidere et redire possunt:nobis cum semel occidit brevis lux,nox est perpetua una dormienda.da mi basia mille, deinde centum,dein mille altera, dein secunda centum,deinde usque altera mille, deinde centum.dein, cum milia multa fecerimus,conturbabimus illa, ne sciamus,aut ne quis malus invidere possit,cum tantum sciat esse basiorum.

1 Es handelt sich um ein Gedicht des Dichters Catullus. Deutsche Übersetzung: „Lass uns leben, meine liebeLesbia, und lieben und lass uns alle Gerüchte der allzu strengen Alten nicht höher als einen Cent achten!Sonnen können untergehen und wieder aufgehen: wenn uns einmal das kurze Licht ausgeht, müssen wireine ewige Nacht schlafen. Gib mir tausend Küsse, darauf hundert, darauf ein anderes Tausend und einzweites Hundert, als nächstes ein weiteres Tausend und dann hundert. Dann, wenn wir viele Tausendegemacht haben, werden wir jene verwirren, damit wir es nicht wissen, noch irgendein Schlechter auf unsneidisch sein kann, wenn er weiß, wie viele Küsse es gewesen sind.”

Page 86: 3446435476_Pytho

10.3 Schreiben in eine Datei 71

10.3 Schreiben in eine DateiAuch das Schreiben in eine Datei lässt sich in Python einfach bewerkstelligen. Zum Öffnender Datei benutzt man „w” statt „r” als Modus. Daten schreibt man in eine Datei mit derMethode write des Dateiobjekts.

Im folgenden Programm versehen wir die vorige Datei mit Zeilennummern, d.h. wir schrei-ben eine neue Datei, die alle Zeilen der alten Datei plus eine Zeilennummer vor jeder Zeileenthält.

fobj_in = open("ad_lesbiam.txt")fobj_out = open("ad_lesbiam2.txt", "w")

counter = 0for line in fobj_in:

counter += 1out_line = "{0:>3s} {1:s}\n".format(str(counter),line.rstrip())fobj_out.write(out_line)

fobj_in.close()fobj_out.close()

Die Datei „ad_lesbiam2.txt” enthält nach Ausführung des obigen Programms folgendenText:

1 VIVAMUS mea Lesbia, atque amemus,2 rumoresque senum severiorum3 omnes unius aestimemus assis!4 soles occidere et redire possunt:5 nobis cum semel occidit brevis lux,6 nox est perpetua una dormienda.7 da mi basia mille, deinde centum,8 dein mille altera, dein secunda centum,9 deinde usque altera mille, deinde centum.10 dein, cum milia multa fecerimus,11 conturbabimus illa, ne sciamus,12 aut ne quis malus invidere possit,13 cum tantum sciat esse basiorum.14

10.4 Die Methoden read() und readlines()Bis jetzt haben wir Dateien Zeile für Zeile mit Schleifen verarbeitet. Aber es kommt öftersvor, dass man eine Datei gerne in eine komplette Datenstruktur einlesen will, z.B. einenString oder eine Liste. Auf diese Art kann die Datei schnell wieder geschlossen werden, undman arbeitet anschließend nur noch auf der Datenstruktur weiter:

Page 87: 3446435476_Pytho

72 10 Dateien lesen und schreiben

>>> poem = open("ad_lesbiam.txt").readlines()>>> print(poem)['VIVAMUS mea Lesbia, atque amemus,\n', 'rumoresque senum severiorum\n',

'omnes unius aestimemus assis!\n', 'soles occidere et redire possunt:\n', 'nobis cum semel occidit brevis lux,\n', 'nox est perpetua unadormienda.\n', 'da mi basia mille, deinde centum,\n', 'dein millealtera, dein secunda centum,\n', 'deinde usque altera mille, deindecentum.\n', 'dein, cum milia multa fecerimus,\n', 'conturbabimus illa, ne sciamus,\n', 'aut ne quis malus invidere possit,\n', 'cum tantumsciat esse basiorum. \n', '\n']

>>> print(poem[2])omnes unius aestimemus assis!

Im obigen Beispiel wurde das ganze Gedicht in eine Liste namens poem geladen. Wir kön-nen nun beispielsweise die dritte Zeile mit poem[2] ansprechen.

Eine andere angenehme Methode, eine Datei einzulesen, bietet die Methode read() vonopen. Mit dieser Methode kann man eine ganze Datei in einen String einlesen, wie wir imfolgenden Beispiel zeigen:

>>> poem = open("ad_lesbiam.txt").read()>>> print(poem[20:34])atque amemus,

10.5 Aufgaben

1. Aufgabe:Gegeben sei eine Datei, in der jeweils abwechselnd ein Vorname und ein Nachnamein einer Zeile steht, also beispielsweise so:

FredMillerEveTurnerSteveBaker

Diese Datei soll in eine andere Datei überführt werden, in der pro Zeile jeweils einVorname und ein Nachname steht.Lösung: 33.5 (1. Aufgabe), Seite 388

2. Aufgabe:Schreiben Sie ein Programm, das jeweils drei Zeilen aus der folgenden Datei zu je-weils einer zusammenfasst und diese als Standardausgabe mittels print() ausgibt.

Page 88: 3446435476_Pytho

10.5 Aufgaben 73

D.h. eine Ausgabezeile soll jeweils den Vornamen, den Nachnamen und den Künst-lernamen bzw. die Band des Künstlers enthalten. Der Band- bzw. Künstlername soll inKlammern ausgegeben werden. Also z.B. „Marshall Bruce Mathers III (Eminem)”:

BrianMolkoPlaceboJimMorrisonThe DoorsRayDaviesThe KinksMarshall BruceMathers IIIEminemAndre RomelleYoungDr. DreBethGibbonsPortishead

Lösung: 33.5 (2. Aufgabe), Seite 389

3. Aufgabe:Ändern Sie das vorige Programm so, dass es nun die Ausgabe in eine Ausgabedateischreibt.Lösung: 33.5 (3. Aufgabe), Seite 389

Page 89: 3446435476_Pytho

11 Listen und Tupelim Detail

11.1 Stapelspeicher

BILD 11.1 Alles Strings

In den bisherigen Kapiteln haben wir bereits ei-niges über Listen erfahren. Aber es fehlen nocheinige wichtige Aspekte.

So kann man eine Liste auch wie einen Sta-pelspeicher1 behandeln. In der Informatik be-zeichnet ein Stapelspeicher (oder Stapel) –manchmal auch Kellerspeicher (Keller) ge-nannt – eine Datenstruktur, die im Wesentli-chen zwei Operationen besitzt: eine, mit derman Daten auf den Stapel legen kann, und ei-ne, um das oberste Element vom Stapel wegzu-nehmen. Stellen Sie sich das wie einen Stapelnoch zu lesender Bücher vor. Sie wollen die Bü-cher des Stapels von oben nach unten durchle-sen. Kaufen Sie zwischendurch ein neues Buch,kommt es oben drauf und ist damit das nächsteBuch, was gelesen werden „muss”.

In den Programmiersprachen werden hierfür meistens die folgenden Operationen zur Ver-fügung gestellt:

■ pushMit dieser Methode legt man ein neues Objekt oben2 auf den Stapel. Diese Operationbezeichnet man im Deutschen als „einkellern”. Bei Python gibt es im Gegensatz zu vielenanderen Sprachen keine Methode mit dem Namen „push”, aber „append” erfüllt diesegleiche Funktionalität.

■ popDiese Methode liefert das oberste Objekt eines Stapels zurück. Dabei wird das Objektvom Stapel entfernt. Man bezeichnet dies im Deutschen als „auskellern”.

1 englisch: stack2 oder „rechts”, je nach Sichtweise

Page 90: 3446435476_Pytho

76 11 Listen und Tupel im Detail

■ peekDiese Methode dient zum Nachschauen, was zuoberst auf dem Stapel liegt. Dabei wirddas Objekt aber nicht wie bei pop entfernt. In Python gibt es keine solche Methode. BeiListen oder Tupel kann man sie jedoch mit einem Indexzugriff simulieren. Falls „liste”eine Liste ist, dann entspricht liste[-1] einem liste.peek(), wenn es peek bei Python gäbe.

11.2 Stapelverarbeitung in Python■ s.append(x)

Hängt x ans Ende der Liste s an. Dies entspricht der Methode „push”, so wie sie sich inanderen Programmiersprachen findet.

■ s.pop(i)Gibt das i-te Element von s zurück und entfernt es dabei aus der Liste. Normalerweise,also in anderen Sprachen, liefert pop nur das „oberste” bzw. das am weitesten rechtsstehende Element zurück.

■ s.pop()Ist i nicht angegeben, wird das letzte Element genommen, was dem „normalen” pop ent-spricht, wie es in anderen Programmiersprachen verwendet wird.

Wie man „pop” und „append” anwendet, kann man dem folgenden Beispiel entnehmen.

>>> colors=["red","green"]>>> colors.append("blue")>>> colors['red', 'green', 'blue']>>> c = colors.pop()>>> c'blue'>>> colors['red', 'green']>>> colors.append(colors.pop())>>> colors['red', 'green']

BILD 11.2 pop und append

Page 91: 3446435476_Pytho

11.3 extend 77

11.3 extendDie Methode „extend” dient dazu, an eine Liste mehrere Elemente anzuhängen.

s.extend(t)

Dabei muss das Argument „t” ein iterierbares Objekt sein.

>>> fib = [0,1,1,2,3,5]>>> fib.extend([8,13,21])>>> fib[0, 1, 1, 2, 3, 5, 8, 13, 21]

Man beachte den Unterschied zu append:

>>> fib = [0,1,1,2,3,5]>>> fib.append([8,13,21])>>> fib[0, 1, 1, 2, 3, 5, [8, 13, 21]]

Das Argument von extend() kann auch ein Tupel sein. Das Ergebnis bleibt gleich:

>>> t = (8,13,21)>>> l = [8,13,21]>>> fib = [0,1,1,2,3,5]>>> fib.extend(t)>>> print fib[0, 1, 1, 2, 3, 5, 8, 13, 21]>>> fib = [0,1,1,2,3,5]>>> fib.extend(l)>>> print fib[0, 1, 1, 2, 3, 5, 8, 13, 21]>>>

Übergibt man einen String als Argument für extend, so wird der String in seine einzelnenBuchstaben zerlegt, die dann einzeln in die Liste eingefügt werden:

>>> liste = []>>> liste.extend("Ich bin ein String")>>> liste['I', 'c', 'h', ' ', 'b', 'i', 'n', ' ', 'e', 'i', 'n', ' ', 'S', 't', 'r

', 'i', 'n', 'g']>>>

11.4 Entfernen eines WertesMit der Methode „remove” kann man einen bestimmten Wert ohne Kenntnis des Indexesaus einer Liste entfernen.

s.remove(x)

Page 92: 3446435476_Pytho

78 11 Listen und Tupel im Detail

Dieser Aufruf entfernt das erste Vorkommen des Wertes x aus der Liste s. Falls x beim Aufrufnicht in der Liste vorhanden ist, gibt es einen ValueError.

Im folgenden Beispiel rufen wir dreimal die remove-Methode auf, um die Farbe „green” zuentfernen. Da die Farbe nur zweimal in der Liste „colours” ist, erhalten wir beim drittenMal einen ValueError:

>>> colours = ["red", "green", "blue", "green", "yellow"]>>> colours.remove("green")>>> colours['red', 'blue', 'green', 'yellow']>>> colours.remove("green")>>> colours['red', 'blue', 'yellow']>>> colours.remove("green")Traceback (most recent call last):File "<stdin>", line 1, in <module>

ValueError: list.remove(x): x not in list>>>

11.5 Enthalten in ListeWie wir bereits gesehen haben, kann man mit „el in liste” testen, ob ein Wert „el” in derListe „liste” vorkommt. Man erhält als Ergebnis True, wenn „el” einmal oder mehrmals inder Liste vorkommt.

Ist man an der genauen Anzahl interessiert, bietet sich die Methode „count” an. „count”liefert die Anzahl der Vorkommen eines Elements in einer Liste zurück.

>>> colours = ["red", "green", "blue", "green", "yellow"]>>> colours.count("green")2>>> colours.count("black")0>>>

11.6 PositionsbestimmungMit der Methode index kann man die Position eines Elements innerhalb einer Liste ermit-teln.

s.index(x[, i[, j]])

Es wird der Index für das x ermittelt. Falls der optionale Parameter i gegeben ist, beginntdie Suche erst ab dieser Position und endet bei der Position j, falls j gegeben ist. Es erfolgteine Fehlermeldung, falls x nicht in s vorkommt.

Page 93: 3446435476_Pytho

11.7 Einfügen von Elementen 79

>>> colours = ["red", "green", "blue", "green", "yellow"]>>> colours.index("green")1>>> colours.index("green", 2)3>>> colours.index("green", 3,4)3>>> colours.index("black")Traceback (most recent call last):File "<stdin>", line 1, in <module>

ValueError: 'black' is not in list>>>

11.7 Einfügen von ElementenWir haben gesehen, dass wir mit append ein Element an das Ende einer Liste anhängenkönnen. Aber es gibt keine Möglichkeit, mittels append ein Element an eine beliebige Stelleeiner Liste einzufügen. Dazu gibt es die Methode „insert”:

s.insert(index, object)

Ein Objekt „object” wird in die Liste „s” eingefügt. Die früheren Elemente ab der Positionindex werden um eins nach rechts verschoben.

>>> colours = ["red", "green", "blue", "yellow"]>>> colours.insert(1,"black")>>> colours['red', 'black', 'green', 'blue', 'yellow']>>>

Die Methode „append” lässt sich sehr einfach mit „insert” realisieren:

>>> abc = ["a","b","c"]>>> abc.insert(len(abc),"d")>>> abc['a', 'b', 'c', 'd']>>>

11.8 Besonderheiten bei TupelWir haben bereits erfahren, dass Tupel unveränderlich sind, d.h. man kann Elemente nichtentfernen, verändern oder neue Elemente anfügen. Das bedeutet, dass es die Methoden„pop”, „append” und „insert” für Tupel nicht gibt. Auch über den Index kann man ein Tupelnicht verändern, wie wir im Folgenden sehen:

Page 94: 3446435476_Pytho

80 11 Listen und Tupel im Detail

>>> t = (1,1,2,3,5,8,13)>>> t[0] = 42Traceback (most recent call last):File "<stdin>", line 1, in <module>

TypeError: 'tuple' object does not support item assignment>>>

11.8.1 Leere Tupel

Leere Tupel definiert man mit leeren runden Klammern:

>>> t = ()>>> type(t)<class 'tuple'>>>>

11.8.2 1-Tupel

Wie sieht es aber aus, wenn man ein Tupel mit nur einem Element definieren will? Run-de Klammern werden auch in arithmetischen Ausdrücken zur Klammerung von Teilaus-drücken verwendet, also zum Beispiel:

((a +b)∗ c)

Aber auch dies ist ein gültiger Ausdruck:

(c)

Damit versteht man auch, dass „t = (5)” eine Integerzahl und nicht einen Einer-Tupel mitder Zahl 5 definiert:

>>> t = (5)>>> type(t)<class 'int'>>>>

Tupel mit nur einem Element kann man nur mit einem „Trick” generieren. Hinter demWert muss ein Komma folgen. Dies sieht nicht schön aus, aber erfüllt seinen Zweck:

>>> t = (5,)>>> type(t)<class 'tuple'>>>>

Page 95: 3446435476_Pytho

11.9 Die veränderliche Unveränderliche 81

11.8.3 Mehrfachzuweisungen, Packing und Unpacking

Wir haben schon gesehen, dass es in Python eine Mehrfachzuweisung gibt, mit der manbeispielsweise ideal Variablenwerte vertauschen kann:

>>> x, y = 47, 11>>> x, y = y, x>>> print(x,y)11 47>>>

Wir wollen nun zeigen, worin der Zusammenhang zwischen obigen Anweisungen und Tu-peln besteht.

Die folgende Anweisung bezeichnet man als Tupel-Packing, da man die Zahlen 47 und 11in ein Tupel packt:

>>> t = "Philip", "Glass">>> type(t)<class 'tuple'>>>>

Dies ist gewissermaßen eine Kurzschreibweise für

>>> t = ("Philip", "Glass")

Man spricht von Tupel-Unpacking oder Tupel-Entpacken, wenn man die einzelnen Werteeines Tupels Variablen zuordnet:

>>> (first, last) = t>>> print(first, last)Philip Glass>>>

11.9 Die veränderliche UnveränderlicheWir haben festgestellt, dass sich Tupel nicht verändern können! Oder können sie es viel-leicht doch?

Folgender Code zeigt scheinbar, dass es doch geht:

>>> galileo = ([],)>>> galileo[0].append("Sie dreht sich doch!")>>> galileo(['Sie dreht sich doch!'],)>>>

Nein, keine Angst. Tupel sind unveränderlich, so wie sich die Sonne auch nicht wirklich umdie Erde dreht.

Page 96: 3446435476_Pytho

82 11 Listen und Tupel im Detail

Im obigen Beispiel ist das erste und einzige Objekt ein Zeiger auf eine Liste. Die Liste wirddann mit der in-Place-Methode append verändert. Wir haben also die Liste innerhalb desTupels und nicht das Tupel selbst verändert.

Versucht man, dem ersten Element direkt einen neuen Wert zuzuweisen, erhält man wieerwartet einen Fehler:

>>> galileo = ([],)>>> galileo[0] = "Sie dreht sich doch!"Traceback (most recent call last):File "<stdin>", line 1, in <module>

TypeError: 'tuple' object does not support item assignment>>>

Nach der Lektüre unseres Kapitels 13 (Flaches und tiefes Kopieren) sollte das oben Gesagtevöllig klar sein.

11.10 Sortieren von Listen11.10.1 „sort” und „sorted”

BILD 11.3 Alles Strings

Zum Sortieren von Python-Listen gibt eszwei Möglichkeiten: zum einen die sort-Methode der list-Klasse, die die Liste in-place sortiert, und zum anderen die ein-gebaute Funktion „sorted”, die aus einemIterator-Objekt eine neue sortierte Listebaut.

Schauen wir uns zunächst einmal „sort”an:

>>> liste = [32,7,12,20,8]>>> liste.sort()>>> liste[7, 8, 12, 20, 32]>>>

Die Methode „sort” sortiert also direkt dasObjekt und liefert kein Ergebnis zurückoder besser gesagt den speziellen Wert„None”. Ein beliebter Fehler besteht darin, dass man glaubt, dass liste.sort() die sortierteListe zurückgibt. Im folgenden Beispiel hat dann „liste” nur noch den Wert „None”, und dieListe ist verloren:

>>> liste = [32,7,12,20,8]>>> liste = liste.sort()>>> print(liste)None>>>

Page 97: 3446435476_Pytho

11.10 Sortieren von Listen 83

Die Funktion „sorted” hingegen liefert als Ergebnis die sortierte Liste zurück, und die Ori-ginalliste bleibt unverändert:

>>> liste = [32,7,12,20,8]>>> x = sorted(liste)>>> x[7, 8, 12, 20, 32]>>> liste[32, 7, 12, 20, 8]>>>

11.10.2 Umkehrung der Sortierreihenfolge

Um die Sortierreihenfolge umzukehren, genügt es, sowohl bei „sort” als auch bei „sorted”den Schlüsselwortparameter „reverse” auf True zu setzen. Standardmäßig steht er auf Fal-se.

>>> liste = [32,7,12,20,8]>>> x = sorted(liste, reverse=True)>>> x[32, 20, 12, 8, 7]>>> liste.sort(reverse=True)>>> liste[32, 20, 12, 8, 7]>>>

11.10.3 Eigene Sortierfunktionen

BILD 11.4 sort mit eigener Sortierabbildung

Manchmal kommt es vor, dass man ei-ne Liste oder ein iterierbares Objekt nichtnach der Standard-Ordnungsrelation sor-tieren will. Für diesen Fall übergeben wirdem Schlüsselwortparameter „key” eineFunktionsreferenz auf eine Funktionsrefe-renz, die als Argument den gleichen Typ wiedie Listenelemente erwartet.

Die Arbeitsweise kann man sich mathema-tisch wie folgt vorstellen:

Die zu sortierende Liste sieht wie folgt aus:

l = [x1, x2, x3, . . . xn]

Sei nun f die an „key” übergebene Funktion, dann wird die ganze Liste mittels f in eine neueListe abgebildet:

[ f (x1), f (x2), f (x3), . . . f (xn)]

Page 98: 3446435476_Pytho

84 11 Listen und Tupel im Detail

Die neue Liste wird von Python standardmäßig sortiert, aber jedes Mal, wenn zwei Wer-te, sagen wir beispielsweise f (xi ) und f (x j ), vertauscht werden müssen, werden auch dieentsprechenden Elemente in l vertauscht, also xi und x j .

Wir demonstrieren nun die Arbeitsweise von „key” im folgenden Beispiel. Wenn wir eineListe von Strings sortieren, erhalten wir keine alphabetische Sortierung, sondern eine, diesich nach der Codierung der Zeichen richtet. Das heißt, dass jeder Großbuchstabe bzgl. derSortierreihenfolge kleiner ist als alle Kleinbuchstaben:

>>> l = ["yellow", "Green", "blue", "Black", "red"]>>> sorted(l)['Black', 'Green', 'blue', 'red', 'yellow']>>>

Wären alle Farben in obigem Beispiel klein geschrieben, gäbe es das Problem nicht mit„Black” und „Green”. Dies führt uns zur Lösung: Wir bilden alle Strings mittels des Parame-ters „key” in Kleinbuchstaben ab:

>>> l = ["yellow", "Green", "blue", "Black", "red"]>>> sorted(l, key=str.lower)['Black', 'blue', 'Green', 'red', 'yellow']>>>

Bei Python ist nicht für jeden Datentyp oder Klasse eine Ordnung definiert. So beispiels-weise auch nicht für die komplexen Zahlen. Versucht man, eine Liste mit komplexen Zah-len zu sortieren, erhält man einen „TypeError”:

>>> x = [ 3 + 4j, 2 - 1j, -4 -7j, 3 +2j]>>> x.sort()Traceback (most recent call last):File "<stdin>", line 1, in <module>

TypeError: unorderable types: complex() < complex()

Übergibt man jedoch die Funktion „abs” an „key”, funktioniert das Sortieren, da „abs” dieLänge eines Vektors berechnet, was einer Float-Zahl entspricht:

>>> x = [ 3 + 4j, 2 - 1j, -4 -7j, 3 +2j]>>> x.sort(key=abs)>>> x[(2-1j), (3+2j), (3+4j), (-4-7j)]>>>

Im Folgenden wollen wir Listen mit Tupel oder anderen Listen als Elementen anhand ei-ner bestimmten Komponente dieser Listen- oder Tupelelemente sortieren. Dazu müssenwir eine Funktion übergeben, die ein Tupel oder eine Liste als Argument hat und eine Kom-ponente zurückliefert.

Sehr elegant kann man diese Aufgabe mit der lambda-Notation lösen, die wir aber erst inKapitel lambda, map, filter und reduce, Seite 289 behandeln:

>>> phone = [("John", "Miller", "4567"), ("Tina", "Baker", "4289"), ("Randy", "Steiner", "4103")]

>>> sorted(phone, key=lambda x:x[0])

Page 99: 3446435476_Pytho

11.10 Sortieren von Listen 85

[('John', 'Miller', '4567'), ('Randy', 'Steiner', '4103'), ('Tina', 'Baker', '4289')]

>>> sorted(phone, key=lambda x:x[1])[('Tina', 'Baker', '4289'), ('John', 'Miller', '4567'), ('Randy', '

Steiner', '4103')]>>> sorted(phone, key=lambda x:x[2])[('Randy', 'Steiner', '4103'), ('Tina', 'Baker', '4289'), ('John', '

Miller', '4567')]>>>

Will man die Aufgabe ohne Hilfe der lambda-Notation lösen, empfiehlt sich der Einsatz derFunktion „itemgetter” aus dem Modul „operator”.

Die Funktion „itemgetter” wird mit einem Index „index” aufgerufen und liefert dann eineFunktion zurück, mit der man dann von einem beliebigen iterierbaren Objekt den Wert desElementes an der Position „index” zurückliefert.

>>> from operator import itemgetter>>> colours = ["red", "green", "blue", "yellow", "pink", "brown"]>>> numbers = [10,15,21,33,5,8]>>> get_index1 = itemgetter(1)>>> get_index1(colours)'green'>>> get_index1(numbers)15>>>

Natürlich bedarf es nicht des Umwegs, dass man sich einen extra Funktionsnamen für deni-ten Itemzugriff anlegt. Man kann dies auch direkt tun:

>>> from operator import itemgetter>>> colours = ["red", "green", "blue", "yellow", "pink", "brown"]>>> numbers = [10,15,21,33,5,8]>>> itemgetter(1)(colours)'green'>>> itemgetter(1)(numbers)15>>> itemgetter(4)(numbers)5>>> itemgetter(4)(colours)'pink'>>>

Nun können wir unser ursprüngliches Sortierproblem, was wir bereits in lambda-Notationgelöst hatten, auch mit der itemgetter-Funktion lösen:

>>> from operator import itemgetter>>> phone = [("John", "Miller", "4567"), ("Tina", "Baker", "4289"), ("

Randy", "Steiner", "4103")]>>> # nach dem 0-ten Index sortieren:...>>> sorted(phone, key=itemgetter(0))

Page 100: 3446435476_Pytho

86 11 Listen und Tupel im Detail

[('John', 'Miller', '4567'), ('Randy', 'Steiner', '4103'), ('Tina', 'Baker', '4289')]

>>> # nach dem 1-ten Index sortieren:...>>> sorted(phone, key=itemgetter(1))[('Tina', 'Baker', '4289'), ('John', 'Miller', '4567'), ('Randy', '

Steiner', '4103')]>>> # nach dem 2-ten Index sortieren:...>>> sorted(phone, key=itemgetter(2))[('Randy', 'Steiner', '4103'), ('Tina', 'Baker', '4289'), ('John', '

Miller', '4567')]>>>

11.11 Aufgaben

1. Aufgabe:Schreiben Sie eine Funktion, die die Reihenfolge einer Liste umkehrt. Verzichten Sieaber auf die Benutzung der Listen-Methode „reverse” und versuchen Sie stattdessen,nur die Methoden „pop” und „append” zu benutzen.

>>> liste = ["a","b","c","d"]>>> rev(liste)["d","c","b","a"]

Lösung: Lösungen zu Kapitel 11 (Listen und Tupel im Detail), Seite 390

2. Aufgabe:Schreiben Sie mit Hilfe der Methoden extend() und append() eine Funktion flatten(),die eine verschachtelte Liste (oder ein Tupel) in eine flache Liste überführt. type() wirdauch benötigt.

>>> flatten([(1,2), "Python", ["a",[1,7]], 1, 1.3])[1, 2, 'Python', 'a', 1,7,1, 1.3]

Lösung: Lösungen zu Kapitel 11 (Listen und Tupel im Detail), Seite 390

3. Aufgabe:Sortieren Sie eine Liste von Zahlen in alphabetischer Reihenfolge, d.h. das gilt:

1 < 10 < 111 < 19 < 2

Lösung: Lösungen zu Kapitel 11 (Listen und Tupel im Detail), Seite 391

Page 101: 3446435476_Pytho

11.11 Aufgaben 87

4. Aufgabe:Schreiben Sie eine Funktion, die die Buchstabenhäufigkeit eines Strings bestimmt. DieHäufigkeiten sollen sortiert ausgegeben werden. Dabei sollen keine Sonderzeichenund Ziffern berücksichtigt werden.Beispiel:

s = "Monty Python"x = letter_frequency(s)print xAusgegeben wird:[('o', 2), ('n', 2), ('t', 2), ('y', 2), ('h', 1), ('m', 1), ('p

', 1)]

Lösung: Lösungen zu Kapitel 11 (Listen und Tupel im Detail), Seite 391

5. Aufgabe:Gegeben ist eine Liste von Verkäufern mit ihren getätigten Verkäufen für ein bestimm-tes Produkt. Die Liste besteht aus 4 Tupeln der Art (Vorname, Nachname, Anzahl,Einzelpreis):

[ ('John', 'Miller', 46, 18.67),('Randy', 'Steiner', 48, 27.99),('Tina', 'Baker', 53, 27.23),('Andrea', 'Baker', 40, 31.75),('Eve', 'Turner', 44, 18.99),('Henry', 'James', 50, 23.56)]

Sortieren Sie die Liste zuerst nach den Verkaufserlösen, also dem Produkt aus derAnzahl und dem Einzelpreis.Dann geben Sie die Liste nochmals nach den Nachnamen sortiert aus. Bei Gleichheitdes Nachnamens sortieren Sie anhand des Vornamens weiter.Lösung: Lösungen zu Kapitel 11 (Listen und Tupel im Detail), Seite 391

Page 102: 3446435476_Pytho

12 Modularisierung

12.1 Module

BILD 12.1 Dateien

Modulare Programmierung ist eine Software-Designtech-nik, die auf dem allgemeinen Prinzip des modularen De-signs beruht. Modulares Design ist ein Ansatz, der sichim Ingenieurwesen schon lange vor den ersten Compu-tern als unausweichlich und unentbehrlich herausgestellthat. Unter modularem Design versteht man, dass manein komplexes System in kleinere selbständige Einheitenoder Komponenten zerlegt. Diese Komponenten bezeich-net man üblicherweise als Module. Ein Modul kann un-abhängig vom Gesamtsystem erzeugt und separat getestetwerden. In den meisten Fällen kann man ein Modul auchin anderen Systemen verwenden.

Heutzutage gibt es kaum ein Produkt, das nicht auf Modularisierung beruht, so wie Autos,Mobiltelefone und so weiter. Computer gehören zu den Produkten, die bis zum Äußerstenmodularisiert sind. Das was für die Hardware ein Muss ist, stellt auch für die Software, dieauf ihr läuft, eine unvermeidliche Notwendigkeit dar.

Wenn man Programme schreiben will, die lesbar, zuverlässig und und ohne hohen Auf-wand wartbar sind, geht es nicht ohne modulares Software-Design, insbesondere bei grö-ßeren Software-Projekten. Es gibt verschiedene Konzepte, um Programme modular zu ge-stalten. Der Ansatz der modularen Programmierung besteht darin, Programme systema-tisch in logische Teilblöcke, d.h. Module, aufzuspalten. Die Aufteilung eines Quelltextes ineinzelne Teile (Module) bezeichnet man als Modularisierung.

In Python unterscheiden wir zwei Arten von Modulen:

■ Bibliotheken (Libraries)

Stellen Datentypen oder Funktionen für alle Python-Programme bereit.

Es gibt:

– die umfangreiche Standardbibliothek

– eigene Module

– Module von Drittanbietern

Page 103: 3446435476_Pytho

90 12 Modularisierung

■ lokale Module

nur für ein Programm verfügbar

Ein Modul, manchmal auch als Bibliothek bezeichnet, egal ob aus der Standardbibliothekoder ein eigenes, wird mit der import-Anweisung eingebunden.

Mit der folgenden Anweisung wird beispielsweise das math-Modul aus der Standardbiblio-thek importiert.

import math

Es stellt, wie der Name vermuten lässt, mathematische Funktionen und Konstanten zurVerfügung, so zum Beispiel die Konstante π (math.pi), die Sinus-Funktion (math.sin()) unddie Cosinus-Funktion (math.cos()):

>>> math.pi3.141592653589793>>> math.sin(math.pi/2)1.0>>> math.cos(math.pi/2)6.123031769111886e-17>>> math.cos(math.pi)-1.0

Nach dem Schlüsselwort import können auch mehrere durch Komma getrennte Modulna-men folgen:

import math, random

import-Anweisungen können an jeder Stelle des Quellcodes stehen, aber man sollte sie derÜbersichtlichkeit willen an den Anfang stellen.

12.1.1 Namensräume von Modulen

Wird eine Bibliothek wie z.B.

import math

importiert, dann stehen die Namen der Bibliothek in einem eigenen Namensraum zur Ver-fügung. Auf die sin()-Funktion des math-Moduls kann man zunächst nur über den vollenNamen („fully qualified”) zugreifen, d.h.

>>> math.sin(3.1415)9.265358966049024e-05

sin() ohne Präfix math ist nicht bekannt und führt zu einer entsprechenden Fehlermel-dung:

>>> sin(3.1415)Traceback (most recent call last):File "<stdin>", line 1, in <module>

NameError: name 'sin' is not defined

Page 104: 3446435476_Pytho

12.1 Module 91

Möchte man gerne „bequem” auf Funktionen wie z.B. die Sinus-Funktion zugreifen kön-nen, also ohne das Präfix math., so kann man die entsprechenden Funktionen direkt im-portieren:

>>> from math import sin, pi>>> print(pi)3.141592653589793>>> sin(pi/2)1.0>>>

Die anderen Methoden der Bibliothek stehen dann nicht zur Verfügung weder mit vollemNamen noch direkt. Man im obigen Fall nur auf sin und pi zugreifen.

Man kann auch eine Bibliothek komplett in den globalen Namensraum einbinden. Da-bei werden dann gegebenenfalls bereits vorhandene gleichlautende Namen überschrie-ben, wie dies im folgenden Beispiel dargestellt wird:

>>> pi = 57.898>>> print(pi)57.898>>> from math import *>>> print(pi)3.141592653589793

12.1.2 Namensräume umbenennen

Beim Import einer Bibliothek kann man auch einen neuen Namen für den Namensraumwählen. Im Folgenden importieren wir math als m. Dies führt bei der Benutzung des math-Moduls zu einer deutlichen Schreiberleichterung, ohne dass die Vorteile eines Namens-raums aufgegeben werden:

>>> import math as m>>> m.pi3.141592653589793>>> m.sin(m.pi)

12.1.3 Modularten

Es gibt verschiedene Modularten:

■ in Python geschriebene Modulendung: .py oder .pyc

■ Dynamisch geladene C-ModuleEndung: .dll, .pyd, .so, .sl, usw.

■ C-Module, die mit dem Interpreter gelinkt sind

Page 105: 3446435476_Pytho

92 12 Modularisierung

Um eine Liste dieser Module zu erhalten:

>>> import sys>>> print(sys.builtin_module_names)('__main__', '_ast', '_bisect', '_codecs', '_collections', '_datetime', '

_elementtree', '_functools', '_heapq', '_io', '_locale', '_pickle', '_posixsubprocess', '_random', '_socket', '_sre', '_string', '_struct', '_symtable', '_thread', '_warnings', '_weakref', 'array', 'atexit', 'binascii', 'builtins', 'errno', 'fcntl', 'gc', 'grp', 'imp', 'itertools', 'marshal', 'math', 'operator', 'posix', 'pwd', 'pyexpat','select', 'signal', 'spwd', 'sys', 'syslog', 'time', 'unicodedata','xxsubtype', 'zipimport', 'zlib')

12.1.4 Suchpfad für Module

Wenn man ein Modul importiert, z.B. abc, sucht der Interpreter nach abc.py in der folgen-den Reihenfolge

■ im aktuellen Verzeichnis

■ PYTHONPATH

■ Falls PYTHONPATH nicht gesetzt ist, wird installationsabhängig im Default-Pfad ge-sucht, also unter Linux/Unix z.B. in /usr/lib/python3.3.

sys.path enthält die Verzeichnisse, in denen Module gesucht werden:

>>> import sys>>> for dir in sys.path:... print(dir)...

/usr/lib/python3.2/usr/lib/python3.2/plat-linux2/usr/lib/python3.2/lib-dynload/usr/local/lib/python3.2/dist-packages/usr/lib/python3/dist-packages

In der folgenden interaktiven Sitzung sehen wir, wie man herausfinden kann, wo sich einzuvor geladenes Modul befindet:

>>> import numpy>>> numpy.__file__

'/usr/lib/python3/dist-packages/numpy/__init__.py'>>> import math>>> math.__file__

Traceback (most recent call last):File "<stdin>", line 1, in <module>

AttributeError: 'module' object has no attribute '__file__'

Wir wir oben sehen, ist das __file__-Attribut nicht vorhanden, wenn es sich bei dem Modulum ein C-Modul handelt, welches statisch an den Interpreter gelinkt ist.

Page 106: 3446435476_Pytho

12.1 Module 93

12.1.5 Inhalt eines Modules

Mit der built-in-Funktion dir() kann man sich die in einem Modul definierten Namen aus-geben lassen.

>>> dir(math)['__doc__', '__name__', '__package__', 'acos', 'acosh', 'asin', 'asinh',

'atan', 'atan2', 'atanh', 'ceil', 'copysign', 'cos', 'cosh', 'degrees', 'e', 'erf', 'erfc', 'exp', 'expm1', 'fabs', 'factorial', 'floor','fmod', 'frexp', 'fsum', 'gamma', 'hypot', 'isfinite', 'isinf', 'isnan', 'ldexp', 'lgamma', 'log', 'log10', 'log1p', 'modf', 'pi', 'pow', 'radians', 'sin', 'sinh', 'sqrt', 'tan', 'tanh', 'trunc']

Ohne Argumente liefert dir() die definierten Namen des Namensraums, in dem man sichbefindet:

$ python3Python 3.2.3 (default, Oct 19 2012, 19:53:57)[GCC 4.7.2] on linux2Type "help", "copyright", "credits" or "license" for more information.>>> import math>>> a = 42>>> b = 4.5>>> l = [2344,"Hallo"]>>> dir()['__builtins__', '__doc__', '__name__', '__package__', 'a', 'b', 'l', '

math']>>>

12.1.6 Eigene Module

Eigene Module zu schreiben, ist in Python extrem einfach. Viele tun es, ohne es zu wissen.Jedes Python-Programm ist automatisch auch ein Modul.

Die beiden folgenden Funktionen fib(), die den n-ten Fibonacci-Wert zurückliefert, unddie Funktion fiblist() werden in einer Datei fibonacci.py gespeichert.

def fib(n):a, b = 0, 1for i in range(n):

a, b = b, a + breturn a

def fiblist(n):fib = [0,1]for i in range(1,n):

fib += [fib[-1]+fib[-2]]return fib

Page 107: 3446435476_Pytho

94 12 Modularisierung

Von einem anderen Programm oder von der interaktiven Shell kann man nun, falls fi-bonacci.py innerhalb des Suchpfads zu finden ist, die Datei mit den beiden Fibonacci-Funktionen als Modul aufrufen.

>>> import fibonacci>>> fibonacci.fib(10)55>>> fibonacci.fiblist(10)[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]>>> fibonacci.__name__

'fibonacci'>>>

12.1.7 Dokumentation für eigene Module

Auch wenn es so ist, wie wir im vorigen Abschnitt gesagt haben, dass jedes Python-Programm automatisch auch ein Modul ist, so sollte jedes Modul dennoch über ausrei-chend Kommentare verfügen. Das pydoc-Modul erzeugt automatisch eine Dokumentationfür jedes Modul.

Rufen wir beispielsweise help auf unser fibonacci-Modul auf, erhalten wir folgende Ausga-be:

Help on module fibonacci:

NAMEfibonacci

FUNCTIONSfib(n)

fiblist(n)

FILE/home/data/bodenseo/python/fibonacci.py

Wünschenswert wären jedoch noch allgemeine Informationen zum fibonacci-Modul undzu den einzelnen Methoden. Eine allgemeine Beschreibung des Moduls kann man in ei-nem Docstring zu Beginn einer Moduldatei verfassen. Die Funktionen dokumentiert manwie üblich mit einem Docstring unterhalb der ersten Funktionszeile:

""" Modul mit wichtigen Funktionen zur Fibonacci-Folge """

def fib(n):""" Iterative Fibonacci-Funktion """a, b = 0, 1for i in range(n):

a, b = b, a + breturn a

Page 108: 3446435476_Pytho

12.2 Pakete 95

def fiblist(n):""" produziert Liste der Fibo-Zahlen """fib = [0,1]for i in range(1,n):

fib += [fib[-1]+fib[-2]]return fib

Die help-Ausgabe sieht nun zufriedenstellend aus:

Help on module fibonacci:

NAMEfibonacci - Modul mit wichtigen Funktionen zur Fibonacci-Folge

FUNCTIONSfib(n)

Iterative Fibonacci-Funktion

fiblist(n)produziert Liste der Fibo-Zahlen

FILE/home/data/bodenseo/python/fibonacci.py

12.2 Pakete

BILD 12.2 Beispielpaket in der Übersicht

Beim Verfassen sprachlicher Dokumente stehtnatürlich der Inhalt selbst im Vordergrund,aber man wird wohl kaum diesen Inhalt anseine Leserinnen und Leser bringen können,wenn dieser Inhalt nicht gegliedert ist. Mangliedert in Bücher, Kapitel, Unterkapitel, Ab-schnitte und so weiter.

Ähnliches gilt natürlich auch für Python-Programme. Werden sie nicht ordentlich ge-gliedert, d.h. modularisiert, wird es schwer, siezu verstehen und zu warten. Im vorigen Kapitelhatten wir Module kennengelernt, die sich her-vorragend eignen, Programme zu strukturierenbzw. zu modularisieren. Was aber, wenn die Zahl unserer Module wächst, wenn wir dieÜbersicht über unsere Module verlieren könnten? Für diesen Fall hat Python das Paket-konzept. Wir können mehrere oder beliebig viele Module zu einem Paket „schnüren”. Derdazu erforderliche Mechanismus ist in Python wiederum denkbar einfach gelöst.

Um ein Paket in Python zu erstellen, sind nur zwei Dinge zu tun: Man erzeugt einen Un-terordner in einem Verzeichnis, in dem Python Module erwartet bzw. sucht. In diesem neuerzeugten Ordner muss man nun eine Datei mit dem Namen __init__.py anlegen. Die Datei

Page 109: 3446435476_Pytho

96 12 Modularisierung

kann leer sein oder Initialisierungscode enthalten, der beim Einbinden des Pakets einmaligausgeführt wird. Pakete können wiederum Pakete enthalten.

Im Folgenden zeigen wir nun anhand eines einfachen Beispiels, wie man ein Paket erzeugt.Unser Paket soll SimplePackage heißen. Dazu erzeugen wir einen Ordner1 mit dem NamenSimplePackage. In diesem Ordner legen wir eine leere Datei mit dem Namen __init__.pyan2. Außerdem legen wir noch zwei einfache Module in diesem Verzeichnis an: ein Modulnamens „a.py” mit folgendem Inhalt

def f1():print("Hello, f1 from module 'a' calling")

sowie ein weiteres Modul mit dem Name „b.py” und diesem Inhalt:

def foo():print("Hello, foo from module 'b' calling")

Nun sind wir in der Lage, unser Paket zu benutzen:

>>> from SimplePackage import a,b>>> a.f1()Hello, f1 from module 'a' calling>>> b.foo()Hello, foo from module 'b' calling

Versucht man, das Paket SimplePackage direkt, also mittels „import SimplePackage” zu im-portieren, erhält man eine Fehlermeldung. Dies liegt daran, dass die Module in einem Pa-ket nicht automatisch importiert werden:

>>> import SimplePackage>>> SimplePackage.a.f1()Traceback (most recent call last):File "<stdin>", line 1, in <module>

AttributeError: 'module' object has no attribute 'a'

Jetzt kann man das Initialisierungsverhalten von __init__.py ideal einsetzen. Wir fügen indiese bisher leere Datei die Zeile „from SimplePackage import a, b” ein und können nunobigen Versuch wiederholen.

>>> import SimplePackage>>> SimplePackage.a.f1()Hello, f1 from module 'a' calling

Wie wir gesehen haben, erhalten wir keine Fehlermeldung und können auch auf die Mo-dule wie gewünscht zugreifen.

1 In Linux oder Unix können wir dies mit dem Befehl „mkdir SimplePackage” tun.2 In Linux kann man dies mit einem touch-Kommando am einfachsten tum: „touch __init.py__”.

Page 110: 3446435476_Pytho

13 Flaches und tiefesKopieren

13.1 Einführung

BILD 13.1 Tiefsee

In diesem Kapitel geht es nun um das Kopierenvon Listen und vor allem um das Kopieren von ver-schachtelten Listen. Dabei kann es vor allem beiAnfängern, falls sie die genauen Zusammenhängenicht kennen, zu verblüffenden und verwirrendenErfahrungen kommen, die meist mit Fehlern im Pro-gramm einhergehen.

Im Folgenden werden wir die Identitätsfunktion id()benutzt, die ein Objekt als Parameter hat. id(obj) lie-fert die „Identität” des Objekts obj. Diese Identität,der Rückgabewert der Funktion, ist ein Integer, dereindeutig und konstant für dieses Objekt ist, solangees im Programmablauf existiert.

Betrachten wir folgenden Code:

>>> x = 3>>> y = x

Es stellt sich die Frage, was bei der Zuweisung „y = x” passiert? Bezeichnen x und y diegleiche Speicherzelle - oder besser das gleiche Objekt - oder wird eine eigene Speicherzelle,d.h. ein eigenes Objekt, für y angelegt, in das die Zahl 3 kopiert wird?

Ersteres ist der Fall, d.h. x und y teilen sich das gleiche Objekt. Bildlich kann man sich dieswie folgt veranschaulichen:

Page 111: 3446435476_Pytho

98 13 Flaches und tiefes Kopieren

Den Beweis, dass dies wirklich so ist, können wir mittels der id-Funktion führen. Wir zei-gen, dass die Identität von x und y gleich sind, d.h. sie „zeigen” bzw. teilen sich das gleicheObjekt:

$ python3Python 3.2.3 (default, Oct 19 2012, 19:53:57)[GCC 4.7.2] on linux2Type "help", "copyright", "credits" or "license" for more information.>>> x = 3>>> y = x>>> id(x) == id(y)True>>> print(id(x), id(y))137220832 137220832

Was passiert nun, wenn wir einer der beiden Variablen einen neuen Wert zuweisen, alsobeispielsweise die Zahl 4 der Variablen y:

>>> y = 4>>> id(x) == id(y)False>>> print(x,y)3 4>>>

Bildlich sieht die Situation nun also wie folgt aus:

Aber auch wenn das obige Verhalten ungewöhnlich im Vergleich zu anderen Programmier-sprachen wie C, C++, Perl etc. ist, so entsprechen die Resultate der Zuweisungen dennochunseren Erwartungen. Kritisch wird es jedoch, wenn wir veränderliche (mutable) Objektewie Listen und Dictionaries kopieren wollen. Python legt nur dann echte Kopien an, wennes unbedingt muss, d.h. dass es der Anwender, also der Programmierer, explizit verlangt.Ansonsten verhält sich Python ebenso wie im obigen einfachen Beispiel: Es gibt nur Zeigerauf das gleiche Objekt. Im Folgenden wollen wir dies im Detail darstellen und auf eini-ge Probleme eingehen, die beim nicht „korrekten” Kopieren von veränderlichen Objektenentstehen können, also z.B. beim Kopieren von Listen und Dictionaries.

Page 112: 3446435476_Pytho

13.2 Kopieren einer Liste 99

13.2 Kopieren einer Liste

>>> colours1 = ["red", "green"]>>> colours2 = colours1>>> print(colours1)['red', 'green']>>> print(colours2)['red', 'green']>>> print(id(colours1),id(colours2))3072482700 3072482700>>> colours2 = ["rouge", "vert"]>>> print(colours1)['red', 'green']>>> print(colours2)['rouge', 'vert']>>> print(id(colours1),id(colours2))3072482700 3072470444

BILD 13.2 Kopieren von Listen

Im obigen kleinen Code-Beispiel legen wir alsErstes eine flache colours1 an. Mit „flach” (Eng-lisch in diesem Zusammenhang: shallow) meinenwir, dass die Liste nicht verschachtelt ist, also kei-ne weiteren Unterlisten enthält. Anschließen wei-sen die Liste colours1 einer Variablen colours2zu. Mittels der print()-Funktion können wir unsüberzeugen, dass beide Variablen den gleichen Inhalt haben, und mittels id() sehen wir,dass beide Variablen auf das gleiche Listenobjekt zeigen.

Nun weisen wir colours2 eine neue Liste zu.

Es erstaunt wenig, dass die Werte von colours1 dabei unverändert bleiben. Wie im obigenBeispiel mit den Integer-Variablen wird ein neuer Speicherbereich für colours2 angelegt,wenn dieser Variablen eine komplett neue Liste (also ein neues Objekt) zugeordnet wird.

>>> colours1 = ["red", "green"]>>> colours2 = colours1>>> print(id(colours1),id(colours2))153810028 153810028>>> colours2[1] = "blue">>> print(id(colours1),id(colours2))153810028 153810028>>> print(colours1)['red', 'blue']>>> print(colours2)['red', 'blue']>>>

BILD 13.3 Kopieren von Listen

Im obigen Beispiel sind wir der Frage nachgegan-gen, was passiert, wenn wir einer Variablen nichtein neues Elements zuordnen, sondern nur eineinzelnes Element der Liste ändern.

Page 113: 3446435476_Pytho

100 13 Flaches und tiefes Kopieren

Um dies zu testen, weisen wir dem zweiten Element von colours2, also dem Element mitdem Index 1, einen neuen Wert zu. Viele wird es nun erstaunen, dass auch colours1 damitverändert wurde, obwohl man doch eine Kopie von colours1 gemacht zu haben glaubte.

Wenn wir uns die Speicherorte mittels der Funktion id() anschauen, sehen wir, dass beideVariablen weiterhin auf das selbe Listenobjekt zeigen.

13.3 Kopie mit TeilbereichsoperatorMit dem Teilbereichsoperator (slicing) kann man flache Listenstrukturen komplett kopie-ren, ohne dass es zu Nebeneffekten kommt, wie man im folgenden Beispiel sehen kann:

>>> liste1 = ['a','b','c','d']>>> liste2 = liste1[:]>>> liste2[1] = 'x'>>> print(liste2)['a', 'x', 'c', 'd']>>> print(liste1)['a', 'b', 'c', 'd']>>>

Sobald jedoch auch Unterlisten in der zu kopierenden Liste vorkommen, werden nur Zei-ger auf diese Unterlisten kopiert:

BILD 13.4 Kopieren von Listen

Dieses Verhalten beim Kopieren veran-schaulicht das nebenstehende Bild. Weistman nun zum Beispiel dem 0-ten Elementeiner der beiden Listen einen neuen Wertzu, führt dies nicht zu einem Nebeneffekt.Probleme gibt es erst, wenn man direkteines der beiden Elemente der Unterlisteverändert.

Um dies zu demonstrieren, ändern wir nun zwei Einträge in lst2:

>>> lst1 = ['a','b',['ab','ba']]>>> lst2 = lst1[:]>>> lst2[0] = 'c'>>> lst2[2][1] = 'd'>>> print(lst1)['a', 'b', ['ab', 'd']]

BILD 13.5 Kopieren von Listen

Man erkennt, dass man aber nicht nur dieEinträge in lst2 geändert hat, sondern auchden Eintrag von lst1[2][0]. Dies liegt daran,dass in beiden Listen, also lst1 und lst2, dasjeweils dritte Element nur ein Link auf einephysikalisch gleiche Unterliste ist. DieseUnterliste wurde nicht mit [:] mitkopiert.

Page 114: 3446435476_Pytho

13.4 Kopieren mit deepcopy aus dem Modul copy 101

13.4 Kopieren mit deepcopy aus demModul copy

BILD 13.6 Kopieren mit Deepcopy

Abhilfe für das eben beschriebene Pro-blem schafft das Modul „copy”. Dieses Mo-dul stellt die Methode „deepcopy” zur Ver-fügung, die das komplette Kopieren einernicht flachen Listenstruktur erlaubt.

Das folgende Skript kopiert unser obigesBeispiel nun mit Hilfe dieser Methode:

>>> from copy import deepcopy>>> lst1 = ['a','b',['ab','ba']]>>> lst2 = deepcopy(lst1)>>> lst2[2][1] = "d">>> lst2[0] = "c";>>> print(lst2)['c', 'b', ['ab', 'd']]>>> print(lst1)['a', 'b', ['ab', 'ba']]

Da es sich nun um eine vollständige Kopie ohne gemeinsame Referenzen handelt, wirkensich Änderungen „lst2” nicht auf „lst1 aus. Das Gleiche gilt natürlich auch umgekehrt, d.h.Änderungen an „lst1” wirken sich nicht auf „lst2” aus.

13.5 Deepcopy für Dictionariesdeepcopy aus dem Modul copy funktioniert für beliebige Python-Objekte, also beispiels-weise auch für Dictionaries:

>>> from copy import deepcopy>>> d = {"a":"1. Buchstabe", "b":"2. Buchstabe"}>>> d2 = deepcopy(d)>>> d2{'a': '1. Buchstabe', 'b': '2. Buchstabe'}>>> d{'a': '1. Buchstabe', 'b': '2. Buchstabe'}>>> d2["a"] = "Erster Buchstabe">>> d2{'a': 'Erster Buchstabe', 'b': '2. Buchstabe'}>>> d{'a': '1. Buchstabe', 'b': '2. Buchstabe'}

Achtung:

Die Methode copy der Klasse dict erzeugt nur flache Kopien eines Dictionarys, wie wir imFolgenden sehen können:

Page 115: 3446435476_Pytho

102 13 Flaches und tiefes Kopieren

>>> en_de = {"red" : "rot", "green" : "grün", "blue" : "blau"}>>> de_fr = {"rot" : "rouge", "grün" : "vert", "blau" : "bleu"}>>> d = {"en_de" : en_de, "de_fr" : de_fr }>>> d2 = d.copy()>>> d{'en_de': {'blue': 'blau', 'green': 'grün', 'red': 'rot'}, 'de_fr': {'grü

n': 'vert', 'blau': 'bleu', 'rot': 'rouge'}}>>> d2{'en_de': {'blue': 'blau', 'green': 'grün', 'red': 'rot'}, 'de_fr': {'grü

n': 'vert', 'blau': 'bleu', 'rot': 'rouge'}}>>>

Nun erzeugen wir mit deepcopy eine Kopie, und das Problem ist behoben:

>>> from copy import deepcopy>>> en_de = {"red" : "rot", "green" : "grün", "blue" : "blau"}>>> de_fr = {"rot" : "rouge", "grün" : "vert", "blau" : "bleu"}>>> d = {"en_de" : en_de, "de_fr" : de_fr }>>>>>> d2 = deepcopy(d)>>>>>> d["en_de"]["brown"] = "braun">>>>>> d{'en_de': {'blue': 'blau', 'brown': 'braun', 'green': 'grün', 'red': 'rot

'}, 'de_fr': {'grün': 'vert', 'blau': 'bleu', 'rot': 'rouge'}}>>> d2{'en_de': {'blue': 'blau', 'green': 'grün', 'red': 'rot'}, 'de_fr': {'grü

n': 'vert', 'blau': 'bleu', 'rot': 'rouge'}}>>>

Page 116: 3446435476_Pytho

14 Funktionen

14.1 Allgemein

BILD 14.1 Funktionen

Das Konzept einer Funktion gehört wohlzu den bedeutendsten der Mathematik.Auch in Programmiersprachen werdenFunktionen häufig eingesetzt, um mathe-matische Funktionen in Algorithmen um-zusetzen. Eine solche Funktion berechnetein oder mehrere Werte und ist vollständigvon den übergebenen Eingabewerten, densogenannten Parametern, abhängig.

Ganz allgemein gesehen stellt eine Funk-tion ein Strukturierungselement in einerProgrammiersprache dar, um eine Men-ge von Anweisungen zu gruppieren, damitman sie mehrmals im Programm verwen-den kann. Ohne Funktionen könnte manCode nur kopieren, um ihn wiederzuver-wenden. Dann müsste man den Code ent-sprechend anpassen. Spätere Änderungenmüssten dann an allen kopierten Stellennachgezogen werden, was natürlich sehr fehlerträchtig wäre. Die Benutzung von Funk-tionen erhöht die Verständlichkeit und Qualität eines Programms oder Skripts. Außerdemsenkt die Verwendung von Funktionen auch die Kosten für die Software-Entwicklung unddie Wartung der Software.

Funktionen kennt man unter unterschiedlichen Bezeichnungen in verschiedenen Pro-grammiersprachen. So kennt man sie auch als Subroutinen, Routinen, Prozeduren, Me-thoden und Unterprogramme.

Page 117: 3446435476_Pytho

104 14 Funktionen

14.2 Funktionen in PythonFunktionen werden mit dem Schlüsselwort def eingeleitet

def funktions-name(Parameterliste):Anweisung(en)

Die Parameterliste besteht aus einem oder mehr Bezeichnern, die durch Kommata ge-trennt sind. Die Parameter der Definition werden beim Aufruf der Funktion als Argumentebezeichnet. Meistens werden die beiden Begriffe jedoch fälschlicherweise wie Synonymeverwendet. Parameter können obligatorisch und optional sein. Die optionalen Parameter(0 oder mehr) folgen den obligatorischen.

Der Funktionskörper (function body), also die Anweisungen, die bei Aufruf der Funktionausgeführt werden, wird in Python durch eine eine homogene Einrückung markiert. Alsoebenso wie alle anderen Blöcke in Python. Die Funktionsdefinition ist beendet, wenn ei-ne Anweisung erfolgt, die wieder auf derselben Einrückungsstufe steht wie der Kopf derFunktion.

Der Funktionskörper kann ein oder mehrere return-Anweisungen enthalten. Diese könnensich an beliebiger Stelle innerhalb des Funktionskörpers befinden. Eine return-Anweisungbeendet den Funktionsaufruf, und das Ergebnis des Ausdrucks, der hinter der return-Anweisung steht, wird an die aufrufende Stelle zurückgeliefert. Falls kein Ausdruck demreturn folgt, wird der spezielle Wert None zurückgeliefert. Programmablauf: Falls der Funk-tionsaufruf das Ende des Funktionskörpers erreicht, ohne auf eine return-Anweisunggestoßen zu sein, endet der Funktionsaufruf, und es wird ebenfalls der Wert None zurück-gegeben.

Schauen wir uns ein einfaches Beispiel an:

def say_hello(name):return "Hello " + name

n = "Michael"n = say_hello(n)print(n)

name = "Kirstin"name = say_hello(name)print(name)

BILD 14.2 Funktionsaufrufe

Page 118: 3446435476_Pytho

14.3 Optionale Parameter 105

Im nebenstehenden Diagramm zeigen wir, was passiert, wenn man obiges Programm aus-führt. Beim ersten Teil des Programms handelt es sich um die Definition der Funktion. Siewird beim Lauf des Programms „übersprungen”. Als erste Anweisung wird die Zuweisungan die Variable n ausgeführt. Danach wird die Funktion say_hello mit dem Argument n auf-gerufen. Zurückgeliefert wird der String „Hello Michael”. Danach wird die Variable „name”auf „Kirstin” gesetzt. Dann wird wieder die Funktion say_hello aufgerufen, die dann denString „Hello Kirstin” zurückliefert.

Ein weiteres Beispiel:

def fahrenheit(T_in_celsius):""" returns the temperature

in degrees Fahrenheit """return (T_in_celsius * 9 / 5) + 32

for t in (22.6, 25.8, 27.3, 29.8):print(t, ": ", fahrenheit(t))

Die Ausgabe des obigen Skripts mit der Funktion fahrenheit:

22.6 : 72.6825.8 : 78.4427.3 : 81.1429.8 : 85.64

14.3 Optionale ParameterFunktionen können auch optionale Parameter haben. Man nennt sie auch Default-Para-meter. Dies sind Parameter, die beim Aufruf nicht angegeben werden müssen. In diesemFall werden dann Default-Werte für diese Parameter eingesetzt. Wir zeigen dies an einemBeispiel. Das folgende kleine Skript, das nicht sehr nützlich ist, begrüßt eine Person mitNamen. Falls beim Aufruf allerdings kein Name übergeben wird, druckt sie nur „Hello eve-rybody!”:

def Hello(name="everybody"):""" Greets a person """print("Hello " + name + "!")

Hello("Peter")Hello()

Die Ausgabe sieht wie folgt aus:

Hello Peter!Hello everybody!

Page 119: 3446435476_Pytho

106 14 Funktionen

14.4 DocstringDie erste Anweisung eines Funktionsrumpfs ist normalerweise eine Zeichenkette, die alsFunktionsname.__doc__ abfragbar ist. Diese Anweisung wird Docstring genannt. Beispiel:

def Hello(name="everybody"):""" Greets a person """print("Hello " + name + "!")

print("The docstring of the function Hello: " + Hello.__doc__)

Die Ausgabe:

The docstring of the function Hello: Greets a person

14.5 SchlüsselwortparameterDabei handelt es sich um eine alternative Möglichkeit, eine Funktion aufzurufen. DieFunktionsdefinition selbst ändert sich nicht.

Ein Beispiel:

def sumsub(a, b, c=0, d=0):return a - b + c - d

print(sumsub(12,4))print(sumsub(42,15,d=10))

Als Schlüsselwortparameter dürfen nur solche verwendet werden, die nicht bereits als Po-sitionsargumente verwendet wurden. Wir können den Vorteil im Beispiel sehen. Wenn wirkeine Schlüsselwortparameter hätten, müssten wir im zweiten Funktionsaufruf alle Argu-mente angeben, obwohl c nur den Default-Wert haben muss:

print(sumsub(42,15,0,10))

14.6 RückgabewerteIn unseren früheren Beispielen haben wir return-Anweisungen in der Funktion sumsub,aber nicht in Hello verwendet. Man sieht also, dass es nicht zwingend notwendig ist, ei-ne return-Anweisung in einer Funktion zu haben. Aber was wird zurückgeliefert, wenn wirkeine explizite Rückgabeanweisung haben? Wir wollen es uns an einem Beispiel anschau-en:

Page 120: 3446435476_Pytho

14.7 Mehrere Rückgabewerte 107

def no_return(x,y):c = x + y

res = no_return(4,5)print(res)

Wenn wir das obige kleine Skript starten, wird None gedruckt, d.h. der spezielle Wert Nonewird von der Funktion no_return zurückgeliefert. Das zeigt, dass eine Funktion, die ohnereturn-Anweisung endet, None zurückliefert. Dieser Wert wird aber auch zurückgeliefert,wenn eine return-Anweisung ohne nachfolgenden Ausdruck eine Funktion beendet, wiewir im folgenden Beispiel sehen:

def empty_return(x,y):c = x + yreturn

res = empty_return(4,5)print(res)

Ansonsten wird der Wert des Ausdrucks hinter dem return zurückgeliefert. Im nächstenBeispiel wird 9 zurückgeliefert:

def return_sum(x,y):c = x + yreturn c

res = return_sum(4,5)print(res)

14.7 Mehrere RückgabewerteEine Funktion kann genau einen Wert zurückliefern oder wir sollten besser sagen: genauein Objekt. Ein Objekt kann beispielsweise ein numerischer Wert wie eine ganze Zahl (In-teger) oder eine Nachkommazahl (float) sein, aber auch ein komplexes Objekt wie etwaeine Liste oder ein Dictionary. Wenn wir beispielsweise drei Integers zurückliefern wollen,haben wir die Möglichkeit, diese in ein Tupel oder eine Liste zu packen und dieses Listen-oder Tupel-Objekt als return-Wert zu übergeben. Das bedeutet, dass wir indirekt auf einesehr einfache Art und Weise beliebig viele Werte zurückliefern können.

Im folgenden Beispiel berechnet die Funktion fib_intervall die Fibonacci-Begrenzung füreine beliebige positive Zahl, d.h. sie liefert ein 2-Tupel zurück. Das erste Element ist diegrößte Fibonacci-Zahl, die kleiner oder gleich x ist, und die zweite Komponente ist diekleinste Fibonacci-Zahl größer oder gleich x. Der Rückgabewert wird direkt mittels „un-packing” in die Variablen lub und sup gespeichert:

def fib_intervall(x):""" returns the largest fibonaccinumber smaller than x and the lowest

Page 121: 3446435476_Pytho

108 14 Funktionen

fibonacci number higher than x"""if x < 0:

return -1(old,new, lub) = (0,1,0)while True:

if new < x:lub = new(old,new) = (new,old+new)

else:return (lub, new)

while True:x = int(input("Your number: "))if x <= 0:

break(lub, sup) = fib_intervall(x)print("Largest Fibonacci Number smaller than x: " + str(lub))print("Smallest Fibonacci Number larger than x: " + str(sup))

14.8 Lokale und globale Variablenin Funktionen

Variablen sind standardmäßig (by default) lokal in einer Funktion, in der sie definiert wer-den.

Allerdings kann auch auf globale Variablen innerhalb einer Funktion lesend zugegriffenwerden:

def f():print(s)

s = "Python"f()

Das obige Skript gibt „Python” aus.

Erweitern wir nun die Funktion f um die Zuweisung s = „Perl” vor der print-Anweisung:

def f():s = "Perl"print(s)

s = "Python"f()print(s)

Die Ausgabe lautet dann:

PerlPython

Page 122: 3446435476_Pytho

14.9 Beliebige Anzahl von Parametern 109

Daran erkennt man, dass die Variable s innerhalb der Funktion f lokal ist, denn die Variables des Hauptprogramms wurde nicht durch die Zuweisung in der Funktion verändert.

Interessant wird es nun, wenn man eine print-Anweisung vor die Zuweisung an s in derFunktion f einfügt:

def f():print(s)s = "Perl"print(s)

s = "Python"f()print(s)

Man erhält nun die folgende Fehlermeldung:

UnboundLocalError: local variable 's'referenced before assignment

Die Variable s ist in f() mehrdeutig, d.h. im ersten print von f() könnte es sich um die glo-bale Variable s mit dem Wert „Python” handeln. Anschließend definieren wir eine lokaleVariable mit dem Wert „Perl”.

Will man innerhalb einer Funktion auf eine globale Variable schreibend zugreifen, so mussman diese explizit als global deklarieren. Dies geschieht mittels der global-Anweisung:

def f():global sprint ss = "Perl"print s

s = "Python"f()print s

Die Ausgabe lautet:

PythonPerlPerl

14.9 Beliebige Anzahl von ParameternMan hat sehr häufig Fälle, in denen die Anzahl der beim Aufruf nötigen Parameter a priorinicht bekannt ist. Man kann dies in Python durch eine sogenannte Tupel-Referenz reali-sieren. Der letzte Parameter erhält einen * vorangestellt, was man keineswegs mit der ent-sprechenden C-Syntax verwechseln sollte.

Page 123: 3446435476_Pytho

110 14 Funktionen

Beispiel:

def arithmetic_mean(*args):sum = 0for x in args:

sum += xreturn sum

print(arithmetic_mean(45,32,89,78))print(arithmetic_mean(8989.8,78787.78,3453,78778.73))print(arithmetic_mean(45,32))print(arithmetic_mean(45))print(arithmetic_mean())

Ergebnisse:

244170009.3177450

14.10 ParameterübergabeEine Funktion oder eine Prozedur benötigt üblicherweise Informationen über die Umge-bung, aus der heraus sie aufgerufen wurde. Die Schnittstelle zwischen dieser Umgebungund der Funktion bzw. Prozedur, d.h. dem Funktions- bzw. Prozedurrumpf (body), bestehtaus speziellen Variablen, die man als Parameter bezeichnet. Indem man Parameter be-nutzt, kann man verschiedenste Objekte von „außen” innerhalb der Funktion benutzen.Die Syntax, wie man Parameter deklariert, und die Semantik, wie man die Argumente desAufrufs an die formalen Parameter der Funktion oder Prozedur weiterleitet, sind abhängigvon der jeweiligen Programmiersprache.

Im vorigen Abschnitt haben wir mehrfach die Begriffe Parameter und Argumente verwen-det. Bei einigen Lesern hat sich sicherlich der Eindruck eingestellt, dass die beiden Begrif-fe synonym sind. Auch in der Literatur werden diese Begriffe häufig synonym verwendet.Dennoch gibt es einen klar definierten Unterschied. Parameter stehen im Kopf der Funkti-on oder Prozedur, also in der Definition der Funktion oder Prozedur, während Argumentebeim Aufruf verwendet werden. Sie liefern die Werte für die formalen Parameter währendder Laufzeit des Programmes.

BILD 14.3 Funktionen

Python benutzt einen Mechanismus, den man als „Call byObject” (auch „Call by Object Reference” oder „Call by Sha-ring”) bezeichnet. Parameterübergabe: Wenn man unverän-derliche Argumente wie Integer, Strings oder Tupel an eineFunktion übergibt, verhält sich die Übergabe nach außenhin wie eine Wertübergabe. Die Referenz auf das unverän-derliche Objekt wird an den formalen Parameter der Funk-

Page 124: 3446435476_Pytho

14.10 Parameterübergabe 111

tion übertragen. Innerhalb der Funktion kann der Inhalt des Objekts nicht verändert wer-den, da es sich ja um unveränderliche Objekte handelt.

Anders verhält es sich jedoch, wenn man veränderliche Argumente überträgt. Auch sie wer-den per Referenz an die Funktionsparameter übertragen. Allerdings können einzelne Ele-mente eines solchen veränderlichen Objekts im Funktionsrumpf verändert werden. Wennwir beispielsweise eine Liste an eine Funktion übergeben, müssen wir zwei Fälle unter-scheiden: Die Elemente einer Liste können verändert werden, d.h. diese Änderung wirktsich auch auf den Gültigkeitsbereich aus, aus dem die Funktion aufgerufen wurde. Wirdallerdings ein komplett neues Element dem formalen Parameter zugewiesen, z.B. eine an-dere Liste, dann hat dies keine Auswirkungen auf die „alte” Liste, also auf die Liste, die beimAufruf übergeben worden ist.

Zuerst wollen wir einen Blick auf die Integer-Variablen werfen. Der Parameter innerhalbder Funktion ref_demo bleibt solange eine Referenz auf das Objekt, das übergeben wurde,wie keine Änderung am Parameter erfolgt. Sobald also ein neuer Wert an den Parameterüberwiesen wird, erzeugt Python eine neue Speicherstelle für das Objekt, und der formaleParameter zeigt nun auf diese Speicherstelle. Die Variable des Funktionsaufrufs wird da-durch nicht verändert, wie wir im folgenden Beispiel nachvollziehen können:

def ref_demo(x):print("x=",x," id=",id(x))x=42print("x=",x," id=",id(x))

Im obigen Beispiel haben wir die Identitätsfunktion id() benutzt, die ein Objekt als Para-meter hat. id(obj) liefert die „Identität” des Objekts obj. Diese Identität, der Rückgabewertder Funktion, ist ein Integer, der eindeutig und konstant für dieses Objekt ist, solange es imProgrammablauf existiert.

BILD 14.4 Funktionen

Parameterübergabe: Zwei Objekte, die gleichzeitig existie-ren, müssen verschiedene Identitäten haben. Zwei verschie-dene Objekte, die nicht-überlappende Lebensbereiche ha-ben, dürfen allerdings den gleichen Identitätswert haben.

Wenn man die Funktion ref_demo() des obigen Beispielsaufruft, so wie wir es im grünen Block weiter unten tun,können wir prüfen, was mit x passiert: Wir können imHauptprogramm (Gültigkeitsbereich main) sehen, dass xdie Identität 41902552 hat. In der ersten print-Anweisungder ref_demo()-Funktion wird das x aus dem main-Gültigkeitsbereich benutzt, denn wirsehen, dass wir den gleichen Identitätswert für x erhalten. Wenn wir jedoch den Wert 42dem x zuweisen, erhält x eine neue Identität, 41903752, das heißt eine separate Speicher-stelle, und das x in main bleibt unberührt, d.h. es hat weiterhin den Wert 9.

Dies bedeutet, dass sich Python zuerst wie Call-by-Reference verhält, aber sobald es einenWert zugewiesen bekommt, verhält es sich wie bei Call-by-Value. Es wird also eine lokaleSpeicherposition für den formalen Parameter x angelegt, und das x in main behält seinenursprünglichen Wert:

>>> x = 9>>> id(x)

Page 125: 3446435476_Pytho

112 14 Funktionen

9251936>>> ref_demo(x)x= 9 id= 9251936x= 42 id= 9252992>>> id(x)9251936>>>

14.11 NebeneffekteVon einem Funktionsaufruf erwartet man, dass die Funktion den korrekten Wert für dieArgumente zurückliefert und sonst keine Effekte verursacht, z.B. Ändern von Speicherzu-ständen. Auch wenn manche Programmierer bewusst Nebeneffekte zur Lösung ihrer Pro-bleme einsetzen, wird dies im Allgemeinen als schlechter Stil betrachtet. Schlimmer ist esjedoch, wenn Nebeneffekte auftreten, mit denen man nicht gerechnet hat, und dadurchFehler verursacht werden. Dies kann auch in Python passieren, z.B . dann, wenn Listenoder Dictionaries als Parameter übergeben werden.

Im folgenden Beispiel wird die Liste fib, die als aktueller Parameter an die Funktion s()übergeben wird, mit der Liste [47,11] verkettet.

>>> def s(liste):... print(id(liste))... liste += [47,11]... print(id(liste))...>>> fib = [0,1,1,2,3,5,8]>>> id(fib)24746248>>> s(fib)2474624824746248>>> id(fib)24746248>>> fib[0, 1, 1, 2, 3, 5, 8, 47, 11]

Man kann dies verhindern, wenn man statt einer Liste eine Kopie der Liste als Parameterübergibt. Mittels der Slicing-Funktion kann man ganz leicht eine Kopie erzeugen. So wirdin s(fib[:]) nicht die Liste fib, sondern eine komplette Kopie übergeben. Der Aufruf verän-dert also nicht den Wert von fib, wie man im folgenden interaktiven Programmstück sehenkann:

>>> def s(liste):... liste += [47,11]... print(liste)...>>> fib = [0,1,1,2,3,5,8]

Page 126: 3446435476_Pytho

14.12 Kommandozeilenparameter 113

>>> s(fib[:])[0, 1, 1, 2, 3, 5, 8, 47, 11]>>> fib[0, 1, 1, 2, 3, 5, 8]

14.12 KommandozeilenparameterNicht nur Funktionen, sondern auch einem Python-Skript kann man Argumente überge-ben. Man bezeichnet diese als Kommandozeilenparameter. Ruft man ein Python-Skriptaus einer Shell auf, werden die Argumente jeweils durch ein Leerzeichen voneinander ge-trennt hinter dem Skriptnamen aufgeführt. Im Python-Skript selber sind die Argumente,also die Kommandozeilenparameter, als Liste unter dem Namen sys.argv abrufbar. Zusätz-lich zu den Kommandozeilenparametern enthält diese Liste auch den Namen des aufru-fenden Skripts (Dateiname). Dieser steht als erster Eintrag in der Liste, also sys.argv[0].

Das Skript (argumente.py) listet sämtliche mitgegebenen Argumente in der Standardaus-gabe auf:

# Modul sys wird importiert:import sys

# Iteration über sämtliche Argumente:for eachArg in sys.argv:

print(eachArg)

Beispiel eines Aufrufs, falls das Skript unter argumente.py gespeichert wurde:

python argumente.py python kurs fuer anfaenger

Dieser Aufruf erzeugt folgende Ausgabe

argumente.pypythonkursfueranfaenger

14.13 Variable Anzahl von ParameternWir führen nun Funktionen ein, die mit einer beliebigen Anzahl von Argumenten aufge-rufen werden können. Diejenigen mit Programmiererfahrungen in C und C++ kennen dasKonzept als varargs.

Es folgen einige Definitionen, die für das Verständnis der weiteren Beispiele dieses Kapitelsnicht wichtig sind: Der Begriff Stelligkeit oder Arität bezeichnet in der Informatik die Para-

Page 127: 3446435476_Pytho

114 14 Funktionen

meteranzahl von Funktionen, Prozeduren oder Methoden. Als variadische Funktion be-zeichnet man in der Informatik Funktionen, Prozeduren oder Methoden mit unbestimm-ter Arität, also solche, deren Parameteranzahl nicht bereits in ihrer Deklaration festgelegtist.

Ein Sternchen „*” wird in Python benutzt, um eine variable Anzahl von Parametern zukennzeichnen. Dazu wird das Sternchen unmittelbar vor einen Variablennamen gestellt.

>>> def varpafu(*x): print(x)...>>> varpafu()()>>> varpafu(34,"Do you like Python?", "Of course")(34, 'Do you like Python?', 'Of course')>>>

Aus dem vorigen Beispiel lernen wir, dass die Argumente, die bei einem Funktionsaufrufvon varpafu übergeben werden, in einem Tupel gesammelt werden. Auf dieses Tupel kannals „normale” Variable im Rumpf (body) der Funktion zugegriffen werden. Ruft man dieFunktion ohne jegliche Argumente auf, so ist x ein leeres Tupel.

Manchmal ist es notwendig, dass man eine feste Anzahl von Positionsparametern benötigt,die von einer beliebigen Anzahl von Parametern gefolgt werden können. Dies ist möglich,aber die Positionsparameter müssen immer zuerst kommen.

Im folgenden Beispiel benutzen wir einen Positionsparameter „city”, der immer angegebenwerden muss, gefolgt von einer beliebigen Anzahl von weiteren Städten „other_cities”:

>>> def locations(city, *other_cities):... print(city, other_cities)...>>> locations("Berlin")Berlin ()>>> locations("Berlin","Freiburg","Stuttgart",... "Konstanz","Frankfurt")Berlin ('Freiburg', 'Stuttgart', 'Konstanz', 'Frankfurt')>>>

Wir wollen dies an einem sinnvolleren Beispiel veranschaulichen. Wir schreiben eineFunktion, die das arithmetische Mittel aus einer variablen Anzahl von Werten berechnet.

Lösung:

def arithmetic_mean(x, *l):""" The function calculates the arithmetic

mean of a non-empty arbitrary number ofnumbers """

sum = xfor i in l:

sum += i

return sum / (1.0 + len(l))

Page 128: 3446435476_Pytho

14.14 * in Funktionsaufrufen 115

Man mag sich fragen, warum wir sowohl einen Positionsparameter „x” und einen Parame-ter für eine variable Anzahl von Werten „*l” in unserer Funktionsdefinition benutzt haben.Die Idee besteht darin, dass wir erzwingen wollten, dass unsere Funktion immer mit einernichtleeren Anzahl von Argumenten aufgerufen wird. Dies ist notwendig, um eine Divisiondurch 0 zu vermeiden, was einen Fehler verursachen würde.

In der folgenden interaktiven Python-Sitzung lernen wir, wie wir diese Funktion benutzenkönnen. Dazu nehmen wir an, dass die Funktion arithmetic_mean in einer Datei mit demNamen statistics.py gespeichert worden ist.

>>> from statistics import arithmetic_mean>>> arithmetic_mean(4,7,9)6.666666666666667>>> arithmetic_mean(4,7,9,45,-3.7,99)26.71666666666667

Das funktioniert gut, aber die Sache hat einen Haken. Was ist, wenn jemand die Funktionmit einer Liste statt mit einer variablen Zahl von Zahlen aufrufen will?

Im Folgenden sehen wir, dass dann ein Fehler verursacht wird:

>>> l = [4,7,9,45,-3.7,99]>>> arithmetic_mean(l)Traceback (most recent call last):File "<stdin>", line 1, in <module>File "statistics.py", line 8, in arithmetic_meanreturn sum / (1.0 + len(l))

TypeError: unsupported operand type(s) for /: 'list' and 'float'

Die Lösung besteht in der Benutzung eines weiteren Sternchens:

>>> arithmetic_mean(*l)26.71666666666667>>>

14.14 * in FunktionsaufrufenEin Stern kann auch in einem Funktionsaufruf erscheinen, wie wir in der vorigen Übunggesehen haben: Die Semantik ist in diesem Fall „invers” zu der Verwendung eines Sterns inder Funktionsdefinition. Ein Argument wird entpackt und nicht gepackt. In anderen Wor-ten: Die Elemente einer Liste oder eines Tupels werden vereinzelt:

>>> def f(x,y,z):... print(x,y,z)...>>> p = (47,11,12)>>> f(*p)(47, 11, 12)

Page 129: 3446435476_Pytho

116 14 Funktionen

Es besteht wohl kaum die Notwendigkeit zu erwähnen, dass diese Art unsere Funktion auf-zurufen komfortabler ist als die folgende:

>>> f(p[0],p[1],p[2])(47, 11, 12)>>>

Zusätzlich, dass dieser Aufruf weniger komfortabel ist, ist die vorige Aufrufsart im allge-meinen Fall nicht anwendbar, d.h. wenn Listen „unbekannter” Längen verwendet werdensollen. „Unbekannt” bedeutet, dass die Länge erst während der Laufzeit bekannt ist undnicht, während man das Skript schreibt.

14.15 Beliebige SchlüsselwortparameterEs gibt auch einen Mechanismus für eine beliebige Anzahl von Schlüsselwortparametern.Um dies zu ermöglichen, wurde als Notation ein doppeltes Sternchen „**” eingeführt:

>>> def f(**args):... print(args)...>>> f(){}>>> f(de="Germnan",en="English",fr="French"){'fr': 'French', 'de': 'Germnan', 'en': 'English'}>>>

14.16 Doppeltes Sternchen imFunktionsaufruf

Das folgende Beispiel veranschaulicht die Verwendung von ** in einem Funktionsaufruf:

>>> def f(a,b,x,y):... print(a,b,x,y)...>>> d = {'a':'append', 'b':'block','x':'extract','y':'yes'}>>> f(**d)('append', 'block', 'extract', 'yes')

und nun in Kombination mit *:

>>> t = (47,11)>>> d = {'x':'extract','y':'yes'}>>> f(*t, **d)(47, 11, 'extract', 'yes')>>>

Page 130: 3446435476_Pytho

15 Rekursive Funktionen

15.1 Definition und Herkunft des Begriffs

BILD 15.1 Rekursion in der natürlichenSprache

Die Rekursion hat etwas mit Unendlichkeit zutun. Ich weiß, dass die Rekursion etwas mit Un-endlichkeit zu tun hat. Er denkt, dass ich weiß,dass die Rekursion etwas mit Unendlichkeit zutun hat. Sie ist sicher, dass er denkt, dass ichweiß, dass die Rekursion etwas mit Unendlich-keit zu tun hat. Wir glauben nicht, dass sie si-cher ist, dass er denkt, dass ich weiß, dass dieRekursion etwas mit Unendlichkeit zu tun hat.... Dieses sprachliche Spiel könnten wir belie-big fortsetzen. Dies ist ein Beispiel für Rekur-sion in der natürlichen Sprache. Die Rekursi-on ist nicht nur eine fundamentale Eigenschaftder natürlichen Sprache, sondern der mensch-lichen kognitiven Fähigkeiten. Unser ganzes Denken basiert auf rekursiven Denkprozes-sen. Der Linguist Noam Chomsky hatte einen Formalismus eingeführt, um sprachlicheAusdrücke mit Hilfe einer Metasprache formal zu definieren. Diese Definition definiert re-kursiv eine Grammatik. Diese formalen Sprachen von Chomsky werden auch zur Definiti-on und Spezifikation von Programmiersprachen in der Informatik benutzt. Die Beweisme-thode der „Vollständigen Induktion” in der Mathematik ist auch eine Art von Rekursion.

Das Adjektiv „rekursiv” stammt vom lateinischen Verb „recurrere”, was „zurücklaufen”bedeutet. Das ist genau das, was eine rekursive Definition oder eine rekursive Funktionmacht: Sie läuft gewissermaßen zurück auf sich selbst, indem sie sich selbst aufruft. Jedem,der sich bereits ein wenig mit Mathematik oder Informatik beschäftigt hat, dürfte wohl dieFakultät begegnet sein. Vor allem, wenn man bereits eine Programmiersprache erlernt hat.Die Fakultätsfunktion ist das Standardbeispiel für die Einführung der Rekursion in nahe-zu jedem Tutorial. Die Gründe liegen auf der Hand: Der mathematische Hintergrund istleicht zu verstehen, und sie lässt sich besonders leicht rekursiv programmieren. Also fol-gen wir der Tradition und widmen uns auch in unserem Python-Tutorial dieser Funktion.Mathematisch wird die Fakultät wie folgt definiert:

n! = n * (n-1)!, if n > 1 and f(1) = 1

Page 131: 3446435476_Pytho

118 15 Rekursive Funktionen

15.2 Definition der RekursionDie Rekursion ist eine Programmiertechnik oder ein Programmierkonzept, indem eineFunktion sich selbst ein oder mehrmals in ihrem Funktionskörper (body) aufruft. EineFunktionsdefinition, die die Bedingungen der Rekursion erfüllt, nennen wir eine rekursiveFunktion.

Abbruchbedingung: Eine rekursive Funktion muss terminieren, damit man sie in einemProgramm benutzen kann. Eine rekursive Funktion terminiert, wenn mit jedem Rekursi-onsschritt das Problem reduziert wird und sich in Richtung der Abbruchbedingung bewegt,d.h. dem Fall, in dem die Funktion sich nicht mehr selbst aufruft. So liefert beispielswei-se die Fakultätsfunktion für das Argument 1 den Wert 1 zurück, ohne dass ein rekursiverAufruf notwendig ist. (Anmerkung für Mathematiker: Das Abbruchkriterium in einer re-kursiven Funktion entspricht der Induktionsverankerung bei der vollständigen Induktion!)Eine rekursive Funktion kann in eine Endlosschleife führen, wenn das Abbruchkriteriumnicht erreicht wird.

Beispiel für die Fakultät:

4! = 4 * 3!3! = 3 * 2!2! = 2 * 1

Ersetzt man die ausgerechneten Werte in der jeweiligen Ursprungsgleichung, erhält manfür 4! folgenden Ausdruck:

4! = 4 * 3 * 2 * 1

Ganz allgemein könnten wir sagen: Rekursion ist eine Methode in der Informatik, in derdie Lösung für ein Problem auf die Lösung kleinerer Instanzen des Problems zurückgeführtwird.

15.3 rekursive Funktionen in PythonAls Beispiel für eine rekursive Funktion in Python wählen wir eine rekursive Implementie-rung der Fakultätsfunktion in Python. Man sieht, dass das Python-Skript ebenso elegantwie die mathematische Funktion ist:

def faculty(n):if n == 1:

return 1else:

return n * faculty(n-1)

Wenn wir nachvollziehen wollen, wie die Funktion funktioniert, können wir zwei print()-Funktionen ergänzen:

def faculty(n):

Page 132: 3446435476_Pytho

15.4 Die Tücken der Rekursion 119

print("faculty has been called with n = " + str(n))if n == 1:

return 1else:

res = n * faculty(n-1)print("intermediate result for ", n, " * faculty(" ,n-1, "): ",

res)return res

print(faculty(5))

Obiges Python-Skript liefert folgende Ausgaben:

faculty has been called with n = 5faculty has been called with n = 4faculty has been called with n = 3faculty has been called with n = 2faculty has been called with n = 1intermediate result for 2 * faculty( 1 ): 2intermediate result for 3 * faculty( 2 ): 6intermediate result for 4 * faculty( 3 ): 24intermediate result for 5 * faculty( 4 ): 120120

Die Fakultätsfunktion lässt sich natürlich auch iterativ schreiben:

def iterative_faculty(n):result = 1for i in range(2,n+1):

result *= ireturn result

15.4 Die Tücken der Rekursion

BILD 15.2 Fibonacci-Rechtecke und goldenerSchnitt

In diesem Abschnitt geht es um ein spe-zielles Problem bei der Rekursion. Ruft ei-ne Funktion sich in einem Ausdruck wie-derholt auf, also mehr als einmal, erhal-ten wir ein exponentielles Laufzeitverhal-ten. Am Beispiel der Fibonacci-Zahlen de-monstrieren wir, wie dieses exponentielleLaufzeitverhalten zustande kommt.

Die Fibonacci-Zahlen sind eine unendli-che Zahlenfolge. Deshalb werden die Fibo-nacci-Zahlen auch als Fibonacci-Folge be-zeichnet.

Page 133: 3446435476_Pytho

120 15 Rekursive Funktionen

Im Folgenden sehen Sie die ersten Glieder der Fibonacci-Folge:

0,1,1,2,3,5,8,13,21,34,55,89, ...

Versuchen Sie doch einmal, das Konstruktionsprinzip dieser Folge zu erkennen.

Es sollte hoffentlich nicht zu schwer gewesen sein. Jede Zahl, außer den beiden ersten,ergibt sich aus der Summe der beiden Vorgänger.

Formale Definition der Fibonacci-Zahlen:

Fn = Fn-1 + Fn-2wobei gilt: F0 = 0 und F1 = 1

Die Fibonacci-Zahlen sind nach dem Mathematiker Leonardo von Pisa, besser bekannt alsFibonacci, benannt. In seinem Buch „Liber Abaci”, das im Jahre 1202 veröffentlicht wurde,führte er diese Folge als Übungsaufgabe ein. In dieser Übung geht es um Kaninchen. SeineFolge beginnt mit F1 = 1, während in modernen Mathematikbüchern und Büchern überProgrammiersprachen die Folge meistens mit dem Wert für das Argument 0 startet, also F0= 0. Dies ist aber gewissermaßen nur Geschmackssache, denn ob man bei 0 oder 1 startet,hat keine Auswirkungen auf die weiteren Folgeglieder.

Die Fibonacci-Zahlen resultieren also aus einem „künstlichen” Kaninchenproblem, das diefolgenden Bedingungen erfüllt:

■ Die Anfangspopulation wird von einem Kaninchenpaar gebildet.

■ Ein neugeborenes Kaninchenpaar kann sich erst am Ende des ersten Monats paaren undwirft am Ende des zweiten Monates ein weiteres Paar.

■ Ansonsten wirft jedes Kaninchenpaar jeweils ein weiteres Kaninchenpaar pro Monat. Siesind unsterblich.

Die Fibonacci-Zahlen geben also die Anzahl der Paare am Ende eines bestimmten Monatsan.

15.5 Fibonacci-Folge in PythonNun kommen wir endlich wieder zu Python und den rekursiven Funktionen zurück. Die Fi-bonacci-Zahlen lassen sich sehr leicht als rekursive Python-Funktion realisieren. Die rekur-sive Python-Implementierung spiegelt ziemlich exakt die rekursive mathematische Funk-tion wieder:

def fib(n):if n == 0:

return 0elif n == 1:

return 1else:

return fib(n-1) + fib(n-2)

Page 134: 3446435476_Pytho

15.5 Fibonacci-Folge in Python 121

Eine iterative Funktion zur Berechnung der Fibonacci Zahlen lässt sich ebenso leicht inPython bewerkstelligen, wie wir im folgenden Skript sehen können:

def fibi(n):a, b = 0, 1for i in range(n):

a, b = b, a + breturn a

Benutzt man die beiden Funktionen fib() und fibi(), stellt man sehr schnell fest, dass sie eindeutlich unterschiedliches Laufzeitverhalten an den Tag legen. Bei kleinen Werten, z.B. n= 10, 11 oder 15, hat man rein gefühlsmäßig, also ohne exakte Zeitmessung den Eindruck,dass fibi() und fib() genauso schnell wären. Aber bei Werten ab n = 30 sieht es deutlichanders aus. fib() braucht nun sekundenlang, während fibi() weiterhin „sofort” mit dem Er-gebnis da ist. Um dieses Gefühl mit soliden Messwerten zu untermauern, haben wir einSkript geschrieben, mit dem wir die Zeiten messen können:

from timeit import Timerfrom fibo import fib

t1 = Timer("fib(10)","from fibo import fib")

for i in range(1,41):s = "fib(" + str(i) + ")"t1 = Timer(s,"from fibo import fib")time1 = t1.timeit(3)s = "fibi(" + str(i) + ")"t2 = Timer(s,"from fibo import fibi")time2 = t2.timeit(3)print("n=%2d, fib: %8.6f, fibi: %7.6f, percent: %10.2f" % (i,

time1, time2, time1/time2))

time1 ist die Zeit in Sekunden, die drei Aufrufe von fib(n) benötigen, und time2 ist entspre-chend die Zeit für drei Aufrufe der iterativen Funktion fibi(). Wenn wir uns die Ergebnisseanschauen, sehen wir, dass fib(20) etwa 14 Millisekunden benötigt, während fibi(20) fürdrei Aufrufe gerade 0,011 Millisekunden benötigt. fib(20) ist also etwa 1300 Mal schnellerals fib(20). fib(40) braucht bereits 215 Sekunden, also mehr als 3,5 Minuten, während fibi(4)sich mit 0,016 Millisekunden begnügt.

n= 1, fib: 0.000004, fibi: 0.000005, percent: 0.81n= 2, fib: 0.000005, fibi: 0.000005, percent: 1.00n= 3, fib: 0.000006, fibi: 0.000006, percent: 1.00n= 4, fib: 0.000008, fibi: 0.000005, percent: 1.62n= 5, fib: 0.000013, fibi: 0.000006, percent: 2.20n= 6, fib: 0.000020, fibi: 0.000006, percent: 3.36n= 7, fib: 0.000030, fibi: 0.000006, percent: 5.04n= 8, fib: 0.000047, fibi: 0.000008, percent: 5.79n= 9, fib: 0.000075, fibi: 0.000007, percent: 10.50n=10, fib: 0.000118, fibi: 0.000007, percent: 16.50n=11, fib: 0.000198, fibi: 0.000007, percent: 27.70n=12, fib: 0.000287, fibi: 0.000007, percent: 41.52

Page 135: 3446435476_Pytho

122 15 Rekursive Funktionen

n=13, fib: 0.000480, fibi: 0.000007, percent: 69.45n=14, fib: 0.000780, fibi: 0.000007, percent: 112.83n=15, fib: 0.001279, fibi: 0.000008, percent: 162.55n=16, fib: 0.002059, fibi: 0.000009, percent: 233.41n=17, fib: 0.003439, fibi: 0.000011, percent: 313.59n=18, fib: 0.005794, fibi: 0.000012, percent: 486.04n=19, fib: 0.009219, fibi: 0.000011, percent: 840.59n=20, fib: 0.014366, fibi: 0.000011, percent: 1309.89n=21, fib: 0.023137, fibi: 0.000013, percent: 1764.42n=22, fib: 0.036963, fibi: 0.000013, percent: 2818.80n=23, fib: 0.060626, fibi: 0.000012, percent: 4985.96n=24, fib: 0.097643, fibi: 0.000013, percent: 7584.17n=25, fib: 0.157224, fibi: 0.000013, percent: 11989.91n=26, fib: 0.253764, fibi: 0.000013, percent: 19352.05n=27, fib: 0.411353, fibi: 0.000012, percent: 34506.80n=28, fib: 0.673918, fibi: 0.000014, percent: 47908.76n=29, fib: 1.086484, fibi: 0.000015, percent: 72334.03n=30, fib: 1.742688, fibi: 0.000014, percent: 123887.51n=31, fib: 2.861763, fibi: 0.000014, percent: 203442.44n=32, fib: 4.648224, fibi: 0.000015, percent: 309461.33n=33, fib: 7.339578, fibi: 0.000014, percent: 521769.86n=34, fib: 11.980462, fibi: 0.000014, percent: 851689.83n=35, fib: 19.426206, fibi: 0.000016, percent: 1216110.64n=36, fib: 30.840097, fibi: 0.000015, percent: 2053218.13n=37, fib: 50.519086, fibi: 0.000016, percent: 3116064.78n=38, fib: 81.822418, fibi: 0.000015, percent: 5447430.08n=39, fib: 132.030006, fibi: 0.000018, percent: 7383653.09n=40, fib: 215.091484, fibi: 0.000016, percent: 13465060.78

Was ist faul an unserer rekursiven Implementierung?

Schauen wir uns den Berechnungsbaum an, d.h. die Reihenfolge, in der die Funktionsauf-rufe erfolgen. Statt fib() schreiben wir allerdings vereinfachend f():

Wir können sehen, dass der Unterbaum zur Berechnung von f(2) (also fib(3)) dreimal auf-taucht und der Unterbaum zur Berechnung von f(3) zweimal. Wenn man sich vorstellt, f(6)zu berechnen, erkennt man, dass f(4) zweimal aufgerufen wird, f(3) dreimal und so wei-ter. Das bedeutet, dass alle Berechnungen immer wieder durchgeführt werden, da bereitsberechnete Werte nicht gespeichert werden. Sie müssen also immer wieder aufs Neue be-rechnet werden.

Page 136: 3446435476_Pytho

15.5 Fibonacci-Folge in Python 123

Wir können eine „Erinnerung” für unsere rekursive Version implementieren. Dazu nutzenwir ein Dictionary, in dem wir bereits berechnete Werte speichern:

memo = {0:0, 1:1}def fibm(n):

if not n in memo:memo[n] = fibm(n-1) + fibm(n-2)

return memo[n]

Diese Technik, also das Speichern von Ergebnisse zur späteren Wiederverwendung, be-zeichnet man als Memoisation.

Wir schauen uns wieder die benötigten Zeiten im Vergleich zu fibi() an:

from timeit import Timerfrom fibo import fib

t1 = Timer("fib(10)","from fibo import fib")

for i in range(1,41):s = "fibm(" + str(i) + ")"t1 = Timer(s,"from fibo import fibm")time1 = t1.timeit(3)s = "fibi(" + str(i) + ")"t2 = Timer(s,"from fibo import fibi")time2 = t2.timeit(3)print("n=%2d, fib: %8.6f, fibi: %7.6f, percent: %10.2f" % (i,

time1, time2, time1/time2))

Wir können sehen, dass unsere neue Funktionsversion sogar schneller ist als die iterative.Vor allem die großen Argumente haben einen besonderen Vorteil von der Memoisation:

n= 1, fib: 0.000011, fibi: 0.000015, percent: 0.73n= 2, fib: 0.000011, fibi: 0.000013, percent: 0.85n= 3, fib: 0.000012, fibi: 0.000014, percent: 0.86n= 4, fib: 0.000012, fibi: 0.000015, percent: 0.79n= 5, fib: 0.000012, fibi: 0.000016, percent: 0.75n= 6, fib: 0.000011, fibi: 0.000017, percent: 0.65n= 7, fib: 0.000012, fibi: 0.000017, percent: 0.72n= 8, fib: 0.000011, fibi: 0.000018, percent: 0.61n= 9, fib: 0.000011, fibi: 0.000018, percent: 0.61n=10, fib: 0.000010, fibi: 0.000020, percent: 0.50n=11, fib: 0.000011, fibi: 0.000020, percent: 0.55n=12, fib: 0.000004, fibi: 0.000007, percent: 0.59n=13, fib: 0.000004, fibi: 0.000007, percent: 0.57n=14, fib: 0.000004, fibi: 0.000008, percent: 0.52n=15, fib: 0.000004, fibi: 0.000008, percent: 0.50n=16, fib: 0.000003, fibi: 0.000008, percent: 0.39n=17, fib: 0.000004, fibi: 0.000009, percent: 0.45n=18, fib: 0.000004, fibi: 0.000009, percent: 0.45n=19, fib: 0.000004, fibi: 0.000009, percent: 0.45n=20, fib: 0.000003, fibi: 0.000010, percent: 0.29n=21, fib: 0.000004, fibi: 0.000009, percent: 0.45

Page 137: 3446435476_Pytho

124 15 Rekursive Funktionen

n=22, fib: 0.000004, fibi: 0.000010, percent: 0.40n=23, fib: 0.000004, fibi: 0.000010, percent: 0.40n=24, fib: 0.000004, fibi: 0.000011, percent: 0.35n=25, fib: 0.000004, fibi: 0.000012, percent: 0.33n=26, fib: 0.000004, fibi: 0.000011, percent: 0.34n=27, fib: 0.000004, fibi: 0.000011, percent: 0.35n=28, fib: 0.000004, fibi: 0.000012, percent: 0.32n=29, fib: 0.000004, fibi: 0.000012, percent: 0.33n=30, fib: 0.000004, fibi: 0.000013, percent: 0.31n=31, fib: 0.000004, fibi: 0.000012, percent: 0.34n=32, fib: 0.000004, fibi: 0.000012, percent: 0.33n=33, fib: 0.000004, fibi: 0.000013, percent: 0.30n=34, fib: 0.000004, fibi: 0.000012, percent: 0.34n=35, fib: 0.000004, fibi: 0.000013, percent: 0.31n=36, fib: 0.000004, fibi: 0.000013, percent: 0.31n=37, fib: 0.000004, fibi: 0.000014, percent: 0.29n=38, fib: 0.000004, fibi: 0.000014, percent: 0.29n=39, fib: 0.000004, fibi: 0.000013, percent: 0.31n=40, fib: 0.000004, fibi: 0.000014, percent: 0.29

15.6 Aufgaben

1. Aufgabe:Schreibe eine rekursive Version der Funktion f(n) = 3 * n, also eine Funktion, die dieVielfachen von 3 berechnet.Lösung: Lösungen zu Kapitel 15 (Rekursive Funktionen), Seite 393

2. Aufgabe:Schreibe eine rekursive Python-Funktion, welche die Summe der ersten n ganzenZahlen zurückliefert.(Hinweis: Diese Funktion sieht ähnlich wie die Fakultätsfunktion aus!)Lösung: Lösungen zu Kapitel 15 (Rekursive Funktionen), Seite 393

3. Aufgabe:Schreiben Sie eine Funktion, die das Pascalsche Dreieck implementiert:

11 1

1 2 11 3 3 1

1 4 6 4 11 5 10 10 5 1

Lösung: Lösungen zu Kapitel 15 (Rekursive Funktionen), Seite 393

Page 138: 3446435476_Pytho

15.6 Aufgaben 125

4. Aufgabe:Die Fibonacci-Zahlen verbergen sich auch innerhalb des Pascalschen Dreiecks. Wennman die fett markierten Zahlen im folgenden Pascalschen Dreieck aufsummiert, erhältman die 7. Fibonacci-Zahl:

11 1

1 2 11 3 3 *1

1 4 *6 4 11 *5 10 10 5 1

*1 6 15 20 15 6 1

Schreibe ein rekursives Programm, d.h. rekursive Funktion, die die Fibonacci-Zahlenaus einem Pascalschen Dreieck heraus berechnet.Lösung: Lösungen zu Kapitel 15 (Rekursive Funktionen), Seite 394

5. Aufgabe:Schreibe eine rekursive Funktion in Python für das Sieb des Eratosthenes. Das Siebdes Eratosthenes ist ein einfacher Algorithmus zur Berechnung aller Primzahlen bis zueiner bestimmten natürlichen Zahl. Dieser Algorithmus geht auf den alten griechischenMathematiker Eratosthenes zurück. Algorithmus zum Finden aller Primzahlen kleinereiner gegebenen natürlichen Zahl n:

1. Erzeuge eine Liste der natürlichen Zahlen von 2 bis n: 2, 3, 4, ..., n.2. Starte mit einem Zähler i mit dem Wert 2, d.h. die erste Primzahl.3. Beginnend mit i+i, inkrementiere jeweils um i und lösche alle so erhaltenen Zahlen

aus der Liste, falls vorhanden, also die Zahlen 2*i, 3*i, 4*i, und so weiter.4. Finde die erste auf i folgende Zahl in der Liste. Dies ist die nächste Primzahl.5. Setze i auf den im letzten Schritt gefundenen Wert.6. Wiederhole die Schritte 3, 4 und 5, bis i größer als n ist. (Als Verbesserung: Es

genügt, bis zur Quadratwurzel von n zu gehen)7. Alle Zahlen, die noch in der Liste sind, sind Primzahlen.

Wenn wir uns den obigen Algorithmus genauer anschauen, sehen wir, dass wir ineffi-zient sind. So versuchen wir zum Beispiel, die Vielfachen von 4 zu entfernen, obwohldie bereits durch die Vielfachen von 2 entfernt worden sind. Man erkennt, dass esgenügt, die Vielfachen der Primzahlen bis zu der Quadratwurzel von n zu entfernen.Diese Menge kann man rekursiv konstruieren.Lösung: Lösungen zu Kapitel 15 (Rekursive Funktionen), Seite 394

Page 139: 3446435476_Pytho

126 15 Rekursive Funktionen

6. Aufgabe:Schreibe eine rekursive Funktion find_index(), die für einen Wert n den Index ausder Fibonacci-Folge zurückliefert, falls n eine Fibonacci-Zahl ist, und ansonsten, al-so wenn n kein Element der Folge ist, -1 zurückliefert.Für eine Fibonacci-Zahl n gilt also:fib(find_index(n)) == nLösung: Lösungen zu Kapitel 15 (Rekursive Funktionen), Seite 395

7. Aufgabe:Die Summe der Quadrate zweier aufeinander folgender Fibonacci-Zahlen ist ebenfallswieder eine Fibonacci-Zahl. Man sieht dies zum Beispiel bei 2 und 3, denn 2*2 + 3*3ergibt 13, was Fib(7) entspricht.Benutze die Funktion aus der vorigen Aufgabe, um die Position der Summe der Qua-drate aus zwei aufeinander folgenden Fibonacci-Zahlen in der Folge zu finden.Mathematische Erklärung:Seien a und b zwei aufeinander folgende Fibonacci-Zahlen. Die Fibonacci-Folge diemit „a” startet, sieht wie folgt aus:

0 a1 b2 a + b3 a + 2b4 2a + 3b5 3a + 5b6 5a + 8b

Wir erkennen, dass die Fibonacci-Zahlen als Faktoren von a und b erscheinen. Dasn-te Element dieser Folge kann mit folgender Formel berechnet werden:

F(n) = Fib(n-1)* a + Fib(n) * b

Daraus folgt, dass für eine natürliche Zahl n mit n > 1 Folgendes gilt:

Fib(2*n + 1) = Fib(n)**2 + Fib(n+1)**2

Lösung: Lösungen zu Kapitel 15 (Rekursive Funktionen), Seite 396

Page 140: 3446435476_Pytho

16 Globale und lokaleVariablen

16.1 Einführung

BILD 16.1 Mond und Erde

Python behandelt globale und lokale Variablen aufeigenwillige Art. Während in vielen anderen Pro-grammiersprachen Variablen automatisch als glo-bal gelten, wenn man sie nicht explizit als lo-kal deklariert hat, ist dies in Python genau andersherum. Die zugrunde liegende Idee besteht darin,dass die Benutzung von globalen Variablen gene-rell als schlechter Programmierstil betrachtet wird,weil dadurch viele Fehler und Nebeneffekte auftre-ten können. In den meisten Fällen, in denen manversucht ist, eine globale Variable zu verwenden,kann man den gewünschten Effekt besser mittels ei-nes Funktionsparameters realisieren oder durch dieRückgabe eines Werts mit der return-Anweisung. Wie auch in vielen anderen Fällen wirdhier durch das Design von Python ein guter Programmierstil gewissermaßen erzwungen.

Das bedeutet, dass jede Variable, die man innerhalb einer Funktion definiert, automatischeinen lokalen Gültigkeitsbereich hat. Und das heißt wiederum, dass was immer man mitdieser Variable innerhalb der Funktion anstellt, keinen Einfluss auf andere Variablen au-ßerhalb der Funktion hat, auch wenn diese den gleichen Namen haben. Der Funktions-rumpf ist also der Gültigkeitsbereich einer solchen Variablen.

Um keine Missverständnisse aufkommen zu lassen: Variablen müssen nicht deklariert wer-den, wie dies in anderen Sprachen wie C und Java üblich ist. Variablen werden in Pythonimplizit deklariert, wenn man sie definiert, d.h. ihnen einen Wert zuweist. Eine Variableerhält automatisch den richtigen Datentyp.

Page 141: 3446435476_Pytho

128 16 Globale und lokale Variablen

16.2 Globale und lokale Variablenin Funktionen

Im folgenden Beispiel zeigen wir, wie globale Variablen innerhalb des Funktionsrumpfseiner Funktion benutzt werden können, allerdings nur „lesend”, also ohne den Wert zu än-dern:

def f():print(s)

s = "I love Paris in the summer!"f()

Die Variable s wird definiert, indem ihr die Zeichenkette „I love Paris in the summer!” zu-geordnet wird. Diese Definition erfolgt vor dem Funktionsaufruf f(). Der Funktionsrumpfvon f() besteht nur aus der „print(s)”-Anweisung. Weil es keine lokale Variable s gibt, d.h.keine Zuweisung an s innerhalb des Funktionsrumpfs von f, wird der Wert der globalenVariablen s benutzt. Dieser Wert kann natürlich nicht verändert werden, wie wir weiter un-ten in diesem Kapitel sehen werden. Es wird also der String „I love Paris in the summer!”ausgegeben.

Es stellt sich aber die Frage, was passiert, wenn wir den Wert von s innerhalb der Funktionvon f() verändern. Wird dies eine Auswirkung auf die globale Variable s haben? Wir testendies im folgenden kleinen Skript:

def f():s = "I love London!"print(s)

s = "I love Paris!"f()print(s)

Starten wir das Skript, erhalten wir folgende Ausgabe:

$ python3 global_lokal2.pyI love London!I love Paris!

Wie sieht es aber aus, wenn wir das erste Beispiel mit dem zweiten Beispiel kombinieren,d.h. wenn wir also zuerst auf s mittels print zugreifen in der Hoffnung, den globalen Wertzu erhalten, und dann s einen neuen Wert zuweisen? Indem wir s einen Wert zuweisenkönnten, machten wir s zu einer lokalen Variable. Dadurch gäbe es s innerhalb des Funk-tionsrumpfs sowohl als globale als auch als lokale Variable. Python lässt diese Mehrdeutig-keit nicht zu, und es kommt zu einer Fehlermeldung, wie wir im folgenden entsprechendangepassten Beispiel sehen können:

def f():print(s)s = "I love London!"

Page 142: 3446435476_Pytho

16.2 Globale und lokale Variablen in Funktionen 129

print(s)

s = "I love Paris!"f()print(s)

Rufen wir das Programm auf, erhalten wir folgende Fehlermeldung:

$ python3 global_lokal3.pyTraceback (most recent call last):File "global_lokal3.py", line 7, in <module>f()

File "global_lokal3.py", line 2, in fprint(s)

UnboundLocalError: local variable 's' referenced before assignment

Eine Variable kann nicht sowohl lokal als auch global innerhalb des gleichen Blocks, hierder Funktionsrumpf, sein. Deswegen betrachtete Python s als eine lokale Variable inner-halb des Rumpfs. Da nun auf diese lokale Variable zugegriffen wird, bevor sie definiertworden ist – sie also zum Zeitpunkt der print-Anweisung noch keinen Wert erhalten hat–, erfolgt die Fehlermeldung.

Man kann jedoch auf globale Variablen „schreibend” innerhalb einer Funktion zugreifen.Dazu muss man sie jedoch explizit mittels des Schlüsselworts global als global deklarieren.Wir demonstrieren dies im folgenden Beispiel:

def f():global sprint(s)s = "Zur Zeit nicht, aber Berlin ist auch toll!"print(s)

s = "Gibt es einen Kurs in Paris?"f()print(s)

Als Ausgabe erhalten wir:

$ python3 global_lokal4.pyGibt es einen Kurs in Paris?Zur Zeit nicht, aber Berlin ist auch toll!Zur Zeit nicht, aber Berlin ist auch toll!

Im folgenden Beispiel wollen wir nun noch zeigen, dass man auf lokale Funktionsvariablenvon außerhalb nicht zugreifen kann:

def f():s = "I am globally not known"print(s)

f()print(s)

Page 143: 3446435476_Pytho

130 16 Globale und lokale Variablen

Wenn man dieses Skript startet, erhält man folgende Ausgabe mit Fehlermeldung:

$ python3 global_lokal5.pyI am globally not knownTraceback (most recent call last):File "global_lokal5.py", line 6, in <module>print(s)

NameError: name 's' is not defined

Das folgende Beispiel zeigt eine wilde Kombination von lokalen und globalen Variablenund Funktionsparametern, um die obigen Sachverhalte nochmals per Beispiel zu vertiefen:

def foo(x, y):global aa = 42x,y = y,xb = 33b = 17c = 100print(a,b,x,y)

a,b,x,y = 1,15,3,4foo(17,4)print(a,b,x,y

Die Ergebnisse sollten mit den bisherigen Erläuterungen keine Überraschungen beinhal-ten:

$ python3 global_lokal6.py42 17 4 1742 15 3 4

Page 144: 3446435476_Pytho

17 Alles über Strings . . .

17.1 ... fast alles

BILD 17.1 Alles Strings

In den vorigen Kapitel haben wir bereits Strings ken-nengelernt und einiges über sie erfahren. Nun möch-ten wir Ihnen weitere Details über Strings bieten, abernicht alle, wie es der Titel andeutet. Hier geht es nurum Textstrings in Python und nicht um die aus derPhysik, der Musik oder die Bademode.

Die ersten Computer wurden entwickelt, um nume-rische Probleme zu lösen. Damals konnten sich vie-le noch nicht einmal vorstellen, dass man mit ei-nem Computer nicht nur rechnen, sondern auch kom-plexe Textverarbeitung betreiben könnte. Schließlichstammt das Wort Computer auch vom lateinischen„computare”, was im Deutschen „berechnen” bedeu-tet. Wenn man nur an die Suchmaschinen alleinedenkt, sieht man, dass heutzutage ein Großteil der Pro-bleme auf Texten also Strings basieren.

So ist es nicht verwunderlich, dass Python insbesondere für den Datentyp string mächtigeWerkzeuge bereitstellt, damit man mit Programmen automatisch Textdateien bearbeitenkann.

Einen guten Überblick über die Klasse „str” können wir uns mit der help-Funktion ver-schaffen, also help(str). Mit dir(str) erhalten wir die Namen der vorhandenen Methodenund Attribute. Insgesamt immerhin 74:

>>> dir(str)['__add__', '__class__', '__contains__', '__delattr__', '__doc__', '

__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__

', 'capitalize', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', '

Page 145: 3446435476_Pytho

132 17 Alles über Strings . . .

isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate','upper', 'zfill']

>>> len(dir(str))74>>>

17.2 Aufspalten von Zeichenketten

BILD 17.2 Zerlegung von Strings

Nehmen wir an, wir haben einen String,der eine Zeile, einen Satz oder vielleichtein ganzes Buch enthält. Um einen sol-chen String zu bearbeiten, ist es sehr häu-fig nötig, ihn in kleinere Einheiten zu zer-legen, z.B. in einzelne Wörter. Zerlegt maneinen solchen String überall dort, wo Leer-zeichen, Tabs oder ganz allgemeine nichtdruckbare Zeichen stehen, hat man ei-ne Zerlegung in Wörter, wenn man da-von absieht, dass an manchen Wörternnoch Satzzeichen wie Kommas oder Punk-te „kleben”. Eine solche Funktionalität gibtes in fast allen modernen Programmier-sprachen. Python bietet für diesen Zweckmehrere String-Methoden.

■ split([sep[, maxsplit]])

■ rsplit([sep[, maxsplit]])

■ splitlines([keepends])

■ partition(sep)

■ rpartition(sep)

17.2.1 split

Die Methode split kann ohne Parameter aufgerufen werden. Wir demonstrieren die Ar-beitsweise von split mit einer bissigen Definition von Ambrose Bierce1:

>>> mammon = "The god of the world's leading religion. The chief templeis in the holy city of New York."

1 aus „The Devil’s Dictionary” (Des Teufels Wörterbuch) von Ambrose Bierce

Page 146: 3446435476_Pytho

17.2 Aufspalten von Zeichenketten 133

>>> mammon.split()['The', 'god', 'of', 'the', "world's", 'leading', 'religion.', 'The', '

chief', 'temple', 'is', 'in', 'the', 'holy', 'city', 'of', 'New', 'York.']

>>>

Man kann erkennen, dass split den String in einzelne Komponenten aufgespalten und da-für Leerzeichen als Trenner verwendet hat. Was man nicht erkennen kann, ist eine andereEigenschaft des Default-Verhaltens von split. Es werden auch Teilstrings, die nur aus Leer-zeichen, Tabs (’\n’) oder anderen sogenannten Whitespaces, also nicht druckbaren Zei-chen bestehen, zu einer Trennstelle zusammengefasst:

>>> mammon = "The god \t \t of the world's \t leading religion. \n\r Thechief temple is in the holy city of New York."

>>> mammon.split()['The', 'god', 'of', 'the', "world's", 'leading', 'religion.', 'The', '

chief', 'temple', 'is', 'in', 'the', 'holy', 'city', 'of', 'New', 'York.']

>>>

Die split-Methode verfügt noch über einen optionalen Parameter „sep”2. Mit diesem Pa-rameter können wir das Standardverhalten von split ändern, d.h. ein neues Trennzeichendefinieren.

Folgende Adressen mit Telefonnummer3 könnten aus einer Excel-Datei stammen und sol-len nun in ihre einzelnen Komponenten zerlegt werden.

Frank;Meyer;Radolfzell;07732/43452Peter;Rabe;Konstanz;07531/70021Ottmar;Huber;Rosenheim;08031/7877-0Anna;Rabe;Radolfzell;07732/2343Oskar;Lindner;Konstanz;07531/890Anna;List;München;089/3434544Anna;list;München;089/3434544Franziska;Huber;Rosenheim;08031/787878Sarah;Rabe;Konstanz;07531/343454

Die Daten sind in der Datei „adressen.txt” gespeichert. Das folgende Python-Programm4

liest diese Datei Zeile für Zeile ein, spaltet die Zeilen an den Strichpunkten und gibt dieZeilen in der Form „Nachname Vorname, Ort, Telefonnummer” wieder aus:

fh = open("addresses.txt", encoding="iso-8859-1")

for address in fh:address = address.strip()(lastname, firstname, city, phone) = address.split(';')print(firstname + " " + lastname + ", " + city + ", " + phone)

2 eine Abkürzung für separator3 Mögliche Ähnlichkeiten mit real existierenden Personen bzw. Telefonnummern sind rein zufällig und nicht

beabsichtigt.4 Sie finden es unter dem Namen „split_addresses.py” in unserem Beispielverzeichnis.

Page 147: 3446435476_Pytho

134 17 Alles über Strings . . .

Das Programm liefert folgende Ausgabe:

bernd@saturn:~/bodenseo/python/beispiele$ python3 split_addresses.pyMeyer Frank, Radolfzell, 07732/43452Rabe Peter, Konstanz, 07531/70021Huber Ottmar, Rosenheim, 08031/7877-0Rabe Anna, Radolfzell, 07732/2343Lindner Oskar, Konstanz, 07531/890List Anna, München, 089/3434544List Anna, München, 089/3434544Huber Franziska, Rosenheim, 08031/787878Rabe Sarah, Konstanz, 07531/343454bernd@saturn:~/bodenseo/python/beispiele$

Nehmen wir an, dass wir lediglich den Vornamen und den Nachnamen aus den Zeilen derobigen Datei benötigen. Dann ist die Aufspaltung zwischen Ort und Telefonnummer un-nötig. Für diesen Zweck stellt die Methode split den optionalen Parameter „maxsplit” zurVerfügung. Die Wert von „maxsplit” bestimmt, wie oft ein String aufgespaltet werden soll,beginnend von links:

>>> s = "red,green,blue,yellow,pink,brown,black,white">>> for i in range(1,8):... x = s.split(",",i)... print(len(x), x)...2 ['red', 'green,blue,yellow,pink,brown,black,white']3 ['red', 'green', 'blue,yellow,pink,brown,black,white']4 ['red', 'green', 'blue', 'yellow,pink,brown,black,white']5 ['red', 'green', 'blue', 'yellow', 'pink,brown,black,white']6 ['red', 'green', 'blue', 'yellow', 'pink', 'brown,black,white']7 ['red', 'green', 'blue', 'yellow', 'pink', 'brown', 'black,white']8 ['red', 'green', 'blue', 'yellow', 'pink', 'brown', 'black', 'white']>>>

17.2.2 Standardverhalten und „maxsplit”

Man möchte gerne die Anzahl der Aufsplittung begrenzen, aber gleichzeitig will man dieStandardeinstellung für den Trenner erhalten. Dies ist eine Problemstellung, die häufig auf-tritt und häufig falsch gelöst wird:

Viele kommen zuerst auf folgende Idee, die sich aber sofort als falsch erweist. Man ver-sucht, „maxsplit” als Schlüsselwortparameter zu benutzen, aber die Fehlermeldung weisteinen darauf hin, dass split keine Schlüsselwortparameter kennt:

>>> s = "red\tgreen blue\nyellow \t pink brown">>> s.split(maxsplit=2)Traceback (most recent call last):File "<stdin>", line 1, in <module>

TypeError: split() takes no keyword arguments>>>

Page 148: 3446435476_Pytho

17.2 Aufspalten von Zeichenketten 135

Den Eingabe für den Parameter einfach leer zu lassen und zu hoffen, dass Python dannautomatisch den Default-Wert einsetzt, funktioniert auch nicht. Dies ist übrigens nie mög-lich, also auch nicht in anderen Methoden und Funktionen:

>>> s = "red\tgreen blue\nyellow \t pink brown">>> s.split(,2)File "<stdin>", line 1s.split(,2)

^SyntaxError: invalid syntax>>>

Die nächste „Lösung” funktioniert zwar syntaktisch, löst aber unser Problem nur teilweise.Man setzt an die erste Stelle für den Separator ein Leerzeichen ein:

>>> s = "red green blue yellow pink brown">>> s.split(" ",2)['red', 'green', 'blue yellow pink brown']>>> s = "red green blue yellow pink brown">>> s.split(" ",2)['red', '', 'green blue yellow pink brown']>>> s = "red\tgreen blue\nyellow \t pink brown">>> s.split(" ",2)['red\tgreen', 'blue\nyellow', '\t pink brown']>>>

Man sieht, dass es nur im ersten Fall funktioniert hatte, d.h. wenn alle Wörter nur durchgenau ein Leerzeichen getrennt sind. Gibt es mehrere Leerzeichen, Tab-Zeichen oder einNewline-Zeichen, funktioniert es nicht mehr.

Nun kommen wir endlich zu der korrekten Lösung. Will man einen Parameter nicht an-geben und möchte haben, dass der Default-Wert genommen wird, dann gibt man an derentsprechenden Position einfach den speziellen Wert „None” ein. Damit können wir alleobigen Beispiele korrekt aufspalten:

>>> s = "red green blue yellow pink brown">>> s.split(None,2)['red', 'green', 'blue yellow pink brown']>>> s = "red green blue yellow pink brown">>> s.split(None,2)['red', 'green', 'blue yellow pink brown']>>> s = "red\tgreen blue\nyellow \t pink brown">>> s.split(None,2)['red', 'green', 'blue\nyellow \t pink brown']>>>

Page 149: 3446435476_Pytho

136 17 Alles über Strings . . .

17.2.3 rsplit

rsplit ohne den Parameter „maxsplit” zu betrachten, macht wenig Sinn. rsplit spaltet einenString von rechts beginnend auf. Gibt man für maxsplit keinen Parameter an, sind die Er-gebnisse von split und rsplit nicht unterscheidbar.

>>> s.split(",")['red', 'green', 'blue', 'yellow', 'pink', 'brown', 'black', 'white']>>> s.rsplit(",")['red', 'green', 'blue', 'yellow', 'pink', 'brown', 'black', 'white']>>>

Ganz anders sieht es aus, wenn wir mit „maxsplit” arbeiten. Zur Demonstration des Unter-schieds wiederholen wir das vorletzte Beispiel mit rsplit:

>>> s.rsplit(",")['red', 'green', 'blue', 'yellow', 'pink', 'brown', 'black', 'white']>>>>>> s = "red,green,blue,yellow,pink,brown,black,white">>> for i in range(1,8):... x = s.rsplit(",",i)... print(len(x), x)...2 ['red,green,blue,yellow,pink,brown,black', 'white']3 ['red,green,blue,yellow,pink,brown', 'black', 'white']4 ['red,green,blue,yellow,pink', 'brown', 'black', 'white']5 ['red,green,blue,yellow', 'pink', 'brown', 'black', 'white']6 ['red,green,blue', 'yellow', 'pink', 'brown', 'black', 'white']7 ['red,green', 'blue', 'yellow', 'pink', 'brown', 'black', 'white']8 ['red', 'green', 'blue', 'yellow', 'pink', 'brown', 'black', 'white']>>>

Es bringt einen großen Zeitvorteil, wenn man konsequent „maxsize” benutzt, wenn mannur wenige Elemente eines Strings von links oder rechts braucht. Im folgenden Programmsammeln wir jeweils das erste Wort von jeder Zeile des Romans Ulysses. In der Funktion;;splitter_total” splitten wir jeweils die komplette Zeile, während wir in der Funktion „split-ter_maxsplit” nur das erste Wort abspalten:

import time

def splitter_total():fh = open("ulysses.txt")res = []for line in fh:

x = line.split()if len(x):

res.append([0])return res

def splitter_maxsplit():fh = open("ulysses.txt")res = []

Page 150: 3446435476_Pytho

17.2 Aufspalten von Zeichenketten 137

for line in fh:x = line.split(None, 1)if len(x):

res.append([0])return res

for i in range(5):t1 = time.time()splitter_total()t1 = time.time() - t1

t2 = time.time()splitter_maxsplit()t2 = time.time() - t2

print(t1, t2, t1/t2)

Starten wir das Programm, erkennen wir aus der Ausgabe, dass die Version mit „maxsplit”ziemlich stabil in jedem Lauf um etwa den Faktor 1,5 schneller ist:

bernd@saturn:~/bodenseo/python/beispiele$ python3 splitter_test.py0.06152200698852539 0.03992414474487305 1.54097244616431970.06169414520263672 0.04128098487854004 1.49449305495394040.059509992599487305 0.039869070053100586 1.49263558242586240.06016898155212402 0.04076218605041504 1.4760980060712760.05954909324645996 0.03962898254394531 1.502665206718968bernd@saturn:~/bodenseo/python/beispiele$

17.2.4 Folge von Trennzeichen

Wir hatten gezeigt, dass mehrere Leerzeichen, Tabs und so weiter beim Standardverhaltenvon split und rsplit als ein einziger Trenner aufgefasst werden. Wie sieht es aber aus, wennman den Trenner explizit als Parameter angibt?

>>> s = "red green blue yellow pink brown">>> s.split()['red', 'green', 'blue', 'yellow', 'pink', 'brown']>>> s.split(" ")['red', '', '', '', 'green', '', 'blue', 'yellow', 'pink', 'brown']>>> s = "red;;;green;blue;;yellow;pink;brown">>> s.split(";")['red', '', '', 'green', 'blue', '', 'yellow', 'pink', 'brown']>>>

Wir sehen, dass das Zusammenfassen mehrerer Trenner zu einem Trenner nur beim Stan-dardtrenner von split funktioniert.

Page 151: 3446435476_Pytho

138 17 Alles über Strings . . .

Dieses Verhalten ist sinnvoll, wenn man beispielsweise an Dateien im CSV-Format5 denkt.Befinden sich zwischen zwei Trennern keine Zeichen, so heißt dies, dass dieses Datenfeldleer ist.

So wie beispielsweise in den folgenden Adresszeilen. In der zweiten Zeile fehlt der Vorna-me, in der vierten Zeile fehlt der Ort und in der fünften Zeile die Telefonnummer:

Frank;Meyer;Radolfzell;07732/43452;Rabe;Konstanz;07531/70021Ottmar;Huber;Rosenheim;08031/7877-0Anna;Rabe;;07732/2343Oskar;Lindner;Konstanz;Anna;List;München;089/3434544Anna;List;München;089/3434544Franziska;Huber;Rosenheim;08031/787878Sarah;Rabe;Konstanz;07531/343454

Wenden wir split(";") darauf an, erhalten wir jeweils Listen der Länge 4. Fehlende Einträgesind mit leerem String markiert. Mit dem Programm „split_addresses_4.py” können wirdieses Verhalten überprüfen:

fh = open("addresses2.txt", encoding="iso-8859-1")

for address in fh:address = address.strip()elements = address.split(';')print(elements)

Starten wir dieses Skript, erhalten wir die erwartete Ausgabe:

bernd@saturn:~/bodenseo/python/beispiele$ python3 split_addresses_4.py['Frank', 'Meyer', 'Radolfzell', '07732/43452']['', 'Rabe', 'Konstanz', '07531/70021']['Ottmar', 'Huber', 'Rosenheim', '08031/7877-0']['Anna', 'Rabe', '', '07732/2343']['Oskar', 'Lindner', 'Konstanz', '']['Anna', 'List', 'München', '089/3434544']['Anna', 'list', 'München', '089/3434544']['Franziska', 'Huber', 'Rosenheim', '08031/787878']['Sarah', 'Rabe', 'Konstanz', '07531/343454']bernd@saturn:~/bodenseo/python/beispiele$

17.2.5 splitlines

splitlines ist eine String-Methode, die einen Text mit Zeilenbegrenzern, also „\n” unter Un-ix, „\r” beim Mac und „\r\n” unter Windows. Zeilenenden können auch von unterschied-

5 Der Name des Dateiformats CSV steht für Comma-separated values. Es handelt sich um ein Datenaus-tauschformat, in dem häufig Kommas zur Trennung von Datenfeldern (Spalten) in einer Zeile verwendetwerden. Statt Kommas können natürlich auch andere Zeichen wie beispielsweise ein Strichpunkt zur Tren-nung benutzt werden. Die Dateinamenserweiterung lautet überlicherweise .csv.

Page 152: 3446435476_Pytho

17.3 Zusammenfügen von Stringlisten mit join 139

lichen Betriebssystemen in einem String gemischt sein, und splitlines erkennt sie als Zei-lenende. Im folgenden Beispiel wird „\r\n” korrekterweise als Windows-Zzeilenende er-kannt, und „\n\r” hinter line2 werden als Unix- und als Max-Zeilenende erkannt, d.h. nachline2 kommt also eine Leerzeile, die als leerer String in die Ergebnisliste eingeht:

>>> s = "line1\nline2\n\rline3\r\nline4\rline5\n">>> s.splitlines()['line1', 'line2', '', 'line3', 'line4', 'line5']>>> s.splitlines(True)['line1\n', 'line2\n', '\r', 'line3\r\n', 'line4\r', 'line5\n']

17.2.6 partition

Mit der Methode „partition” wird ein String an der ersten Stelle von links nach rechts auf-gespaltet, an der der Trennstring „sep” steht. Das Ergebnis ist ein Tupel, dessen erste Kom-ponente der Teilstring vor dem sep ist, die zweite Komponente der String sep ist und diedritte Komponente der nach dem Vorkommen von sep stehende Teilstring:

>>> spruch = "Eine Katze, die jagt, hungert nicht!">>> spruch.partition(",")('Eine Katze', ',', ' die jagt, hungert nicht!')>>> spruch.rpartition(",")('Eine Katze, die jagt', ',', ' hungert nicht!')

17.3 joinjoin ist eine String-Methode.

Allgemeine Syntax:

s.join(iterable)

Zurückgeliefert wird die Konkatenation der Elemente des iterierbaren Objekts „iterable”.Die Elemente von „iterable” werden dabei so zusammengefügt, dass zwischen den Ele-menten jeweils der String „s” eingefügt wird. „s” ist dabei das String-Objekt, auf den dieMethode „join” angewendet wird.

Beispiele:

>>> x = ["Python","Perl","Java"]>>> "-".join(x)'Python-Perl-Java'>>> x = "48772">>> ".".join(x)'4.8.7.7.2'

Page 153: 3446435476_Pytho

140 17 Alles über Strings . . .

17.4 Suchen von TeilstringsFür die Suche von Teilstrings gibt es verschiedene Möglichkeiten in Python:

17.4.1 „in” oder „not in”

„in” ist ideal, um die reine Existenz eines Substrings zu testen, wenn man nicht daran in-teressiert zu wissen, wo der Unterstring vorkommt.

>>> rhyme = 'Little Jack Horner sat in a corner,\nEating a Christmas pie;\nHe put in his thumb and pulled out a plum,\nAnd said, "What a goodboy am I!" '

>>> "corner" in rhymeTrue>>> "corner" not in rhymeFalse>>>

17.4.2 s.find(substring[, start[, end]])

Mit der Stringmethode „find” kann man prüfen, ob ein String „substring” in einem String„s” vorkommt. „find” liefert eine -1 zurück, wenn „substring” nicht vorkommt, ansonstenliefert sie die Position zurück, ab der „substring” in „s” beginnt:

>>> rhyme = 'Little Jack Horner sat in a corner,\nEating a Christmas pie;\nHe put in his thumb and pulled out a plum,\nAnd said, "What a goodboy am I!" '

>>> pos = rhyme.find("orner")>>> pos13>>> pos = rhyme.find("Jill")>>> pos-1>>>

Gefunden wird das „orner” in dem Wort „Horner”. Der Name „Jill” kommt nicht in unseremString vor, deshalb liefert „find” ein -1 zurück.

Mit den optionalen Parametern „start” und „end” kann man die Suche innerhalb des String„s” einschränken. Wird nur ein Wert für „start” angegeben, wird in dem Teilstring s[start:]gesucht. Wird sowohl ein Wert für „start” und „end” angegeben, dann wird in dem Teil-string s[start:end] gesucht. Wir verdeutlichen dies in folgendem Beispiel:

>>> s = "annxbny">>> s.find("x")3>>> s.find("x",2)3

Page 154: 3446435476_Pytho

17.4 Suchen von Teilstrings 141

>>> s.find("x",2,3)-1>>>

17.4.3 s.rfind(substring[, start[, end]])

Die Stringmethode „rfind” funktioniert analog zu „find”, allerdings erfolgt die Suche vonrechts, also vom Ende des Strings:

>>> rhyme = 'Little Jack Horner sat in a corner,\nEating a Christmas pie;\nHe put in his thumb and pulled out a plum,\nAnd said, "What a goodboy am I!" '

>>> pos = rhyme.rfind("orner")>>> pos29>>>

Gefunden wird das „orner” in dem Wort „corner”. Der Name „Jill” kommt nicht in unseremString vor, deshalb liefert „find” ein -1 zurück.

17.4.4 s.index(substring[, start[, end]])

Funktioniert genau wie „find”, außer wenn „substring” nicht in „s” vorkommt. In diesemFall wird ein Ausnahmefehler generiert:

>>> rhyme = 'Little Jack Horner sat in a corner,\nEating a Christmas pie;\nHe put in his thumb and pulled out a plum,\nAnd said, "What a goodboy am I!" '

>>> pos = rhyme.index("orner")>>> pos13>>> rhyme.index("orner") == rhyme.find("orner")True>>> rhyme.index("Jill")Traceback (most recent call last):File "<stdin>", line 1, in <module>

ValueError: substring not found>>>

17.4.5 s.rindex(substring[, start[, end]])

Funktioniert analog zu „index” und „rfind”.

>>> rhyme = 'Little Jack Horner sat in a corner,\nEating a Christmas pie;\nHe put in his thumb and pulled out a plum,\nAnd said, "What a goodboy am I!" '

Page 155: 3446435476_Pytho

142 17 Alles über Strings . . .

>>> rhyme.rindex("orner")29>>> rhyme.rindex("Jill")Traceback (most recent call last):File "<stdin>", line 1, in <module>

ValueError: substring not found>>>

17.4.6 s.count(substring[, start[, end]])

Zählt die Vorkommen eines Teilstrings „substring” in s. „start” und „end” verhalten sichgenau wie bei „find” und „index”:

>>> rhyme = 'Little Jack Horner sat in a corner,\nEating a Christmas pie;\nHe put in his thumb and pulled out a plum,\nAnd said, "What a goodboy am I!" '

>>> rhyme.count("Jill")0>>> rhyme.count("orner")2>>> rhyme.count("Horner")1>>> rhyme.count("in")3>>>

17.5 Suchen und ErsetzenEine häufige Aufgabe, wenn man mit Texten arbeitet, besteht darin, dass man eine Zei-chenkette durch eine andere ersetzen will bzw. muss. Dies gilt auch für Bereiche, die nichtsmit dem Programmieren zu tun haben: So beispielsweise beim Erstellen von Briefen oderRechnungen. Man benutzt einen alten Brief und tauscht nur Name und Adresse des Emp-fängers aus.

Aber zurück zu Python und zum Programmieren: Zum Ersetzen eines Teilstrings „old” ineinem String „s” durch einen anderen Teilstring „new” gibt es die Methode replace:

s.replace(old, new[, count])

Ohne Angabe des optionalen Parameters „count” werden alle Vorkommen von old durchnew ersetzt. Mit dem optionalen Parameter „count” kann man steuern, wieviele Vorkom-men von old durch new ersetzt werden sollen.

Beispiel:

>>> ch = "Drei Chinesen mit dem Kontrabass">>> ch.replace("e","a")'Drai Chinasan mit dam Kontrabass'

Page 156: 3446435476_Pytho

17.6 Nur noch Kleinbuchstaben oder Großbuchstaben 143

>>> ch.replace("e","a").replace("i","a")'Draa Chanasan mat dam Kontrabass'>>> ch.replace("e","a").replace("i","a").replace("o","a")'Draa Chanasan mat dam Kantrabass'>>>

17.6 Kleinbuchstaben undGroßbuchstaben

Man kann einen String mit Hilfe der Stringmethode „upper” komplett in Großbuchstabenwandeln. Ebenso kann man einen String mit Hilfe der Stringmethode „lower” komplett inKleinbuchstaben wandeln:

>>> ch = "Drei Chinesen mit dem Kontrabass">>> ch.lower()'drei chinesen mit dem kontrabass'>>> ch.upper()'DREI CHINESEN MIT DEM KONTRABASS'>>>

17.7 capitalize und title„capitalize” ist eine Funktion, die alle Buchstaben außer dem Anfangsbuchstaben einesStrings in Kleinbuchstaben wandelt. Der Anfangsbuchstabe wird in einen Großbuchstabengewandelt, falls es sich um einen Kleinbuchstaben handelt.

„title” wandelt alle Buchstaben, die nicht Anfangsbuchstaben eines Wortes sind in Klein-buchstaben und alle Anfangsbuchstaben eines Wortes in einen Großbuchstaben um.

>>> ch = "Drei Chinesen mit dem Kontrabass">>> ch.capitalize()'Drei chinesen mit dem kontrabass'>>> ch.title()'Drei Chinesen Mit Dem Kontrabass'>>> ch = "Drei Chinesen mit dem kONTRABASS">>> ch.title()'Drei Chinesen Mit Dem Kontrabass'>>> ch.capitalize()'Drei chinesen mit dem kontrabass'>>>

Page 157: 3446435476_Pytho

144 17 Alles über Strings . . .

17.8 Stripping StringsHäufig kommt es vor, dass unerwünschte Zeichen am Anfang oder am Ende eines Stringsstehen. Meistens sind es „Whitespaces“, wie Zeilenende, Tabs usw., die auf der rechten Sei-te eines String stehen. Um diese Zeichen loszuwerden, gibt es die Stringmethoden „strip”,„lstrip” und „rstrip”.

Allgemeine Syntax:

■ s.strip([chars])unerwünschte Zeichen werden auf beiden Seiten des Strings entfernt

■ s.lstrip([chars])unerwünschte Zeichen werden nur auf der linken Seite des Strings entfernt

■ s.rstrip([chars])unerwünschte Zeichen werden nur auf der rechten Seite des Strings entfernt

Beispiele:

>>> s = " \t \n \rMorgen kommt der Weihnachtsmann \t\n">>> s.strip()'Morgen kommt der Weihnachtsmann'>>> s.lstrip()'Morgen kommt der Weihnachtsmann \t\n'>>> s.rstrip()' \t \n \rMorgen kommt der Weihnachtsmann'>>>>>> s = "69023 Frankfurt">>> s.strip("0123456789 ")'Frankfurt'>>>

17.9 Strings ausrichten■ str.center(laenge[, fillchar])

Der String str wird mit fillchar bzw Leerzeichen, falls kein fillchar gegeben ist, gleicher-maßen von links und rechts auf die Länge laenge gebracht.

■ str.ljust(laenge[, fillchar])Der String str wird mit fillchar bzw Leerzeichen, falls kein fillchar gegeben ist, links aufdie Länge laenge gebracht.

■ str.rjust(laenge[, fillchar])analog zu ljust von rechts.

■ str.zfill(laenge)Spezialfall für numerische Werte. Der String wird rechts ausgerichtet und von links mitNullen gefüllt.

>>> s = "Hallo">>> s.center(15)

Page 158: 3446435476_Pytho

17.10 String-Tests 145

' Hallo '>>> s.ljust(15)'Hallo '>>> s.rjust(15)' Hallo'>>>>>> z = "123.99">>> z.zfill(10)'0000123.99'>>>

17.10 String-Tests■ s.isalnum()

True, wenn alle Zeichen in s Buchstaben oder Ziffern sind.

>>> for word in ("mp3", "Hallo", "hello", "343", "767.43"):... print("%6s : %s" % (word, word.isalnum()))...

mp3 : TrueHallo : Truehello : True343 : True

767.43 : False>>>

■ s.isalpha()True, wenn alle Zeichen in s Buchstaben sind.

>>> for word in ("mp3", "Hallo", "hello", "343", "767.43"):... print("%6s : %s" % (word, word.isalpha()))...

mp3 : FalseHallo : Truehello : True343 : False

767.43 : False>>>

■ s.isdigit()True, wenn alle Zeichen in s Ziffern sind.

>>> for word in ("mp3", "Hallo", "hello", "343", "767.43"):... print("%6s : %s" % (word, word.isdigit()))...

mp3 : FalseHallo : Falsehello : False

Page 159: 3446435476_Pytho

146 17 Alles über Strings . . .

343 : True767.43 : False>>>

■ s.islower()True, wenn alle Buchstaben in s Kleinbuchstaben sind.

>>> for word in ("mp3", "Hallo", "hello", "343", "767.43"):... print("%6s : %s" % (word, word.islower()))...

mp3 : TrueHallo : Falsehello : True343 : False

767.43 : False>>>

■ s.isupper()True, wenn alle Buchstaben in s Großbuchstaben sind.

>>> for word in ("mp3", "Hallo", "hello", "343", "767.43", "HALLO", "MP3"):

... print("%6s : %s" % (word, word.isupper()))

...mp3 : False

Hallo : Falsehello : False343 : False

767.43 : FalseHALLO : TrueMP3 : True

>>>

■ s.isspace()True, wenn alle Zeichen in s Whitespaces sind.

>>> for word in ("mp3", "Hallo", "hello", "343", "767.43", " \t\n", ""):

... print("%6s : %s" % (word, word.isspace()))

...mp3 : False

Hallo : Falsehello : False343 : False

767.43 : False

: True: True

>>>

■ s.istitle()True, wenn alle Wörter in s groß geschrieben sind.

Page 160: 3446435476_Pytho

17.11 Aufgaben 147

>>> for word in ("mp3", "Hallo", "hello", "343", "767.43", "HALLO", "MP3"):

... print("%6s : %s" % (word, word.istitle()))

...mp3 : False

Hallo : Truehello : False343 : False

767.43 : FalseHALLO : FalseMP3 : False

>>>

17.11 Aufgaben

1. Aufgabe:Folgende Adressendatei (siehe addresses_mixed.txt) ist gegeben:

Frank,Meyer,Radolfzell;07732/43452Peter,Rabe,Konstanz;07531/70021Ottmar,Huber,Rosenheim;08031/7877-0Anna,Rabe,Radolfzell;07732/2343Oskar,Lindner,Konstanz;07531/890Anna,List,München;089/3434544Anna,List,München;089/3434544Franziska,Huber,Rosenheim;08031/787878Sarah,Rabe,Konstanz;07531/343454

Schreiben Sie ein Python-Programm, das diese Adressen zeilenweise in der Form„Vorname Nachname, Ort, Telefonnummer” ausgibt.Lösung: Lösungen zu Kapitel 17 (Alles über Strings . . . ), Seite 397

2. Aufgabe:Für die folgende Aufgabe benutzen wir wieder die Daten der Datei „adressen.txt”:

Frank;Meyer;Radolfzell;07732/43452Peter;Rabe;Konstanz;07531/70021Ottmar;Huber;Rosenheim;08031/7877-0Anna;Rabe;Radolfzell;07732/2343Oskar;Lindner;Konstanz;07531/890Anna;List;München;089/3434544Anna;List;München;089/3434544Franziska;Huber;Rosenheim;08031/787878Sarah;Rabe;Konstanz;07531/343454

Page 161: 3446435476_Pytho

148 17 Alles über Strings . . .

Schreiben Sie ein Python-Programm, das aus dieser Adressendatei eine Liste mitZweiertupels erstellt, die jeweils aus dem ersten und letzten Element einer Adresse,also dem Vornamen und der Telefonnummer, bestehen:

[('Frank', '07732/43452'), ('Peter', '07531/70021'), ('Ottmar','08031/7877-0'), ('Anna', '07732/2343'), ('Oskar','07531/890'), ('Anna', '089/3434544'), ('Anna','089/3434544'), ('Franziska', '08031/787878'), ('Sarah','07531/343454')]

Lösung: Lösungen zu Kapitel 17 (Alles über Strings . . . ), Seite 397

3. Aufgabe:Der folgende String enthält Zeilenenden, die sowohl vom Mac als auch von Windowsund Unix bzw. Linux kommen:

Hat der alte Hexenmeister\nSich doch einmal wegbegeben!\r\nUndnun sollen seine Geister\r

Auch nach meinem Willen leben.\n

Bringen Sie diesen String mittels Python in eine Linux/Unix-Form, d.h. dass jede Zeilenur von einem \n beendet wird.Lösung: Lösungen zu Kapitel 17 (Alles über Strings . . . ), Seite 398

4. Aufgabe:Schreiben Sie eine Funktion, die die Position des n-ten Vorkommens eines Strings„sub” in einem anderen String ausgibt. Falls „sub” nicht vorkommt, soll -1 zurückgelie-fert werden.

>>> from findnth import findnth>>> findnth("abc xyz abc xyz abc xyz", "xyz", 1)4>>> findnth("abc xyz abc xyz abc xyz", "xyz", 2)12>>> findnth("abc xyz abc xyz abc xyz", "xyz", 3)20>>>

Lösung: Lösungen zu Kapitel 17 (Alles über Strings . . . ), Seite 398

5. Aufgabe:Schreiben Sie eine Funktion, die das n-te Vorkommen eines Strings „sub” durch einenString „replacement” ersetzt.Die Funktion soll als Ergebnis den veränderten String zurückliefern, bzw. falls replace-ment nicht im String vorkommt, soll der unveränderte Original-String zurückgegebenwerden.Lösung: Lösungen zu Kapitel 17 (Alles über Strings . . . ), Seite 399

Page 162: 3446435476_Pytho

18 Ausnahme-behandlung

BILD 18.1Ausnahmebehandlung

Fehler werden häufig in anderen Sprachen mit Fehlerrückga-bewerten oder globalen Statusvariablen behandelt.

Traditionelle Fehlerbehandlung bzw. Fehlervermeidung wirdmeistens in bedingten Anweisungen behandelt, so wie im fol-genden Codefragment, in dem eine Division durch 0 verhin-dert werden soll:

if y != 9:z = x / y

Eleganter geht es mit den in Python vorhandenen Ausnahme-behandlungen.

Sie sind Thread-sicher und können leicht bis in die höchste Programmebene weitergege-ben oder an einer beliebigen anderen Ebene der Funktionsaufruffolge behandelt werden.

Der Python-Ansatz legt den Einsatz von Ausnahmen nahe, wann immer eine Fehlerbedin-gung entstehen könnte.

Die Ausnahmebehandlung (Exception Handling) ist ein Mittel, mit Fehlern umzugehen.Damit ist es möglich, Syntaxfehler zur Laufzeit abzufangen und zu behandeln.

Das folgende Skript implementiert unter Verwendung einer Ausnahmebehandlung einerobuste Eingabeaufforderung, die solange wartet, bis eine Zahl eingegeben wurde:

while True:try:

zahl = raw_input("Zahl eingeben: ")zahl = int(zahl)break

except ValueError as e:print("error message: ", e)print "Error. Keine Zahl!"

Funktionsweise:

■ Ausführung der try-Klausel (Anweisungen zwischen den Schlüsselworten try und except)

■ Die except-Klausel wird übersprungen, wenn keine Ausnahme auftritt.

■ Tritt eine Ausnahme während der Ausführung der try-Klausel auf, wird der Rest der Klau-sel übersprungen. Stimmt der Typ der Ausnahme mit dem Schlüsselwort von exceptüberein, wird die except-Klausel ausgeführt.

Page 163: 3446435476_Pytho

150 18 Ausnahmebehandlung

■ Tritt eine Ausnahme auf, die nicht mit der Ausnahme in der except-Klausel überein-stimmt, wird sie nach außen an weitere try-Anweisungen weitergereicht. Wenn keineBehandlung erfolgt, so ist es eine unbehandelte Ausnahme.

Ein Beispiel zum Abfangen von Divisionen durch die Zahl 0:

>>> zahlen = [3.7832, 8.5, 1.9, 0, 4.5]>>> for x in zahlen:... try:... print(x, 1.0/x)... except ZeroDivisionError:... print(str(x) + " hat kein inverses Element")...3.7832 0.26432649608796798.5 0.117647058823529411.9 0.52631578947368420 hat kein inverses Element4.5 0.2222222222222222>>>

18.1 Die optionale else-KlauselDie try ... except-Anweisung hat eine optionale else-Klausel, die nach allen except-Klauselnstehen muss. Dort befindet sich Code, der ausgeführt wird, wenn die try-Klausel keine Aus-nahme auslöst.

Im Folgenden verlangen wir solange die Eingabe eines Dateinamens, bis sich dieser zumLesen öffnen lässt. Der else-Teil der try-Anweisung wird nur ausgeführt, wenn es keinenAusnahmefehler gegeben hat. Deshalb dürfen wir dann auch auf das Datei-Handle f zu-greifen:

while True:filename = input("Dateiname: ")try:

f = open(filename, 'r')except IOError:

print(filename, " lässt sich nicht öffnen")else:

print(filename, ' hat ', len(f.readlines()), ' Zeilen ')f.close()break

Starten wir obiges Programm, erhalten wir folgende Ausgaben:

bernd@saturn:~/bodenseo/python/beispiele$ python3 try_else.pyDateiname: ulisses.txtulisses.txt lässt sich nicht öffnenDateiname: ulysses.txtulysses.txt hat 33025 Zeilenbernd@saturn:~/bodenseo/python/beispiele$

Page 164: 3446435476_Pytho

18.2 Fehlerinformationen über sys.exc_info 151

18.2 Fehlerinformationen übersys.exc_info

Die genauen Fehlerinformationen kann man sich mit der Methode exc_info des sys-Moduls anzeigen lassen:

import sys

try:i = int("Hallo")

except:(type, value, traceback) = sys.exc_info()print("Unexpected error:")print("Type: ", type)print("Value: ", value)print("traceback: ", traceback)raise

Die Ausgabe des obigen Programms:

bernd@saturn:~/bodenseo/python/beispiele$ python3 exception_sys_exc_info.py

Unexpected error:Type: <class 'ValueError'>Value: invalid literal for int() with base 10: 'Hallo'traceback: <traceback object at 0xb728834c>Traceback (most recent call last):File "exception_sys_exc_info.py", line 4, in <module>i = int("Hallo")

ValueError: invalid literal for int() with base 10: 'Hallo'bernd@saturn:~/bodenseo/python/beispiele$

18.3 Abfangen mehrerer ExceptionsEs können auch mehrere Exceptions nach einer try-Anweisung abgefangen werden, wiewir im folgenden Beispiel zeigen:

import sys

try:f = open('integers.txt')s = f.readline()i = int(s.strip())

except IOError as err:(errno, strerror) = err.args

Page 165: 3446435476_Pytho

152 18 Ausnahmebehandlung

print("I/O error({0}): {1}".format(errno, strerror))

except ValueError:print("No valid integer in line.")

except:print("Unexpected error:", sys.exc_info()[0])raise

18.4 except mit mehrfachen AusnahmenEine einzelne except-Anweisung kann auch gleichzeitig mehrere Fehler abfangen, die ver-schiedenen Fehlerarten werden dann in einem Tupel gelistet, wie wir im folgenden Beispielsehen:

try:f = open('integers.txt')s = f.readline()i = int(s.strip())

except (IOError, ValueError):print("An I/O error or a ValueError occurred")

except:print("An unexpected error occurred")raise

18.5 Exceptions generierenMan kann auch selbst Exceptions generieren:

>>> raise SyntaxError("Sorry, mein Fehler!")Traceback (most recent call last):File "<stdin>", line 1, in <module>

SyntaxError: Sorry, mein Fehler!>>>

18.6 FinalisierungsaktionBisher haben wir die try-Anweisungen immer nur im Zusammenspiel mit except-Klauselnbenutzt. Aber es gibt noch eine andere Möglichkeit für try-Anweisungen. Die try-Anweisungkann von einer finally-Klausel gefolgt werden.

Page 166: 3446435476_Pytho

18.6 Finalisierungsaktion 153

Man bezeichnet diese Form auch als Finalisierungs- oder Terminierungsaktionen, weil sieimmer unter allen Umständen ausgeführt werden müssen, und zwar unabhängig davon,ob eine Ausnahme im try-Block aufgetreten ist oder nicht.

try:x = float(input("Your number: "))inverse = 1.0 / x

finally:print("Ich werde immer ausgegeben, ob Fehler oder nicht")

print("Mich sieht man nur, wenn es keinen Fehler gab!")

In den folgenden Programmläufen demonstrieren wir einen Fehlerfall und einen Durch-lauf ohne Ausnahmen:

bernd@saturn:~/bodenseo/python/beispiele$ python3 finally_exception.pyYour number: 42Ich werde immer ausgegeben, ob Fehler oder nichtMich sieht man nur, wenn es keinen Fehler gab!bernd@saturn:~/bodenseo/python/beispiele$ python3 finally_exception.pyYour number: 0Ich werde immer ausgegeben, ob Fehler oder nichtTraceback (most recent call last):File "finally_exception.py", line 3, in <module>inverse = 1.0 / x

ZeroDivisionError: float division by zerobernd@saturn:~/bodenseo/python/beispiele$

Page 167: 3446435476_Pytho

19 ObjektorientierteProgrammierung

19.1 Einführung

BILD 19.1 Früchte

Auch wenn Python ohne Wenn und Abereine objektorientierte Programmierspra-che ist, haben wir es bisher mit voller Ab-sicht in den vorhergehenden Kapiteln un-seres Tutorials vermieden, auf die objekt-orientierte Programmierung (OOP) einzu-gehen. Wir haben OOP ausgelassen, weilwir davon überzeugt sind, dass es einfa-cher ist und mehr Spaß macht, wenn manPython zu lernen beginnt, ohne dass manüber die Details der OOP Bescheid wissenmuss.

Aber auch wenn wir die objektorientierte Programmierung vermieden haben, so war siedennoch in unseren Übungen und Beispielen präsent. Wir haben Objekte und Methodenvon Klassen benutzt, ohne eigentlich von ihrer Existenz zu wissen. In diesem Kapitel gebenwir nun eine ordentliche Einführung in den objektorientierten Ansatz von Python. OOP isteine der mächtigsten Programmiermöglichkeiten von Python, aber, wie wir gesehen ha-ben, muss man sie dennoch nicht nutzen, d.h. man kann auch mächtige und effizienteProgramme ohne OOP-Techniken schreiben.

Auch wenn viele Programmierer und Informatiker die OOP für eine moderne Errungen-schaft halten, so gehen ihre Wurzeln bis in die 1960er-Jahre zurück. Die erste Program-miersprache, die Objekte verwendete, war „Simula 67” von Ole-Johan Dahl und KirstenNygard.

Das Grundkonzept der objektorientierten Programmierung besteht darin, Daten und de-ren Funktionen (Methoden), – d.h. Funktionen, die auf diese Daten angewendet werdenkönnen –, in einem Objekt zusammenzufassen und nach außen zu kapseln, sodass Metho-den fremder Objekte diese Daten nicht manipulieren können. Objekte werden über Klas-sen definiert. Eine Klasse ist eine formale Beschreibung, wie ein Objekt beschaffen ist, d.h.welche Attribute und welche Methoden sie hat. Eine Klasse darf nicht mit einem Objektverwechselt werden. Statt Objekt spricht man auch von einer Instanz einer Klasse.

Page 168: 3446435476_Pytho

156 19 Objektorientierte Programmierung

19.2 Die Kuchenklasse

BILD 19.2 Kuchenklasse

Bei Einführungen in die objektorientier-te Programmierung wird häufig und ger-ne auf Beispiele aus dem Alltag zurück-gegriffen. Dabei handelt es sich meistensum Beispiele, die zwar helfen, objektori-entierte Konzepte zu verdeutlichen, aberdiese Beispiele lassen sich dann nicht inProgrammiercode wandeln. So auch indiesem Beispiel einer „Kuchenklasse”. Be-trachten wir das Rezept eines Erdbeerku-chens. Ein solches Rezept kann man prin-zipiell als eine Klasse ansehen. Das heißt, das Rezept bestimmt, wie eine Instanz der Klassebeschaffen sein muss. Backt jemand einen Kuchen nach diesem Objekt, dann schafft ereine Instanz oder ein Objekt dieser Klasse. Es gibt dann verschiedene Methoden, diesenKuchen zu verarbeiten oder zu verändern. Ein nette Methode stellt übrigens in diesem Bei-spiel „aufessen” dar. Ein Erdbeerkuchen gehört in eine übergeordnete Klasse „Kuchen”, dieihre Eigenschaften, z.B. dass ein Kuchen sich als Nachtisch nutzen lässt, an Unterklassenwie Erdbeerkuchen, Rührkuchen, Torten und so weiter vererbt.

19.3 ObjekteDer zentrale Begriff in der objektorientierten Programmierung ist der des Objekts. Ein Ob-jekt bezeichnet in der OOP die Abbildung eines realen Gegenstandes mit seinen Eigen-schaften und Verhaltensweisen (Methoden) in ein Programm. Anders ausgedrückt: Ein Ob-jekt kann immer durch zwei Dinge beschrieben werden:

■ was es tun kann oder was wir in einem Programm mit ihm tun können,

■ was wir über es wissen.

Objekte sind Instanzen oder Exemplare einer Klasse. Die Begriffe Objekt und Instanz wer-den meist synonym gebraucht und bezeichnen den gleichen „Gegenstand”. Objekte oderInstanzen werden mittels Konstruktoren erzeugt. Konstruktoren sind spezielle Methodenzur Erzeugung von Instanzen einer Klasse. Zum Entfernen oder Löschen von Instanzen gibtes die Destruktor-Methode.

19.4 KlasseEine Klasse ist ein abstrakter Oberbegriff für die Beschreibung der gemeinsamen Struk-tur und des gemeinsamen Verhaltens von realen Objekten (Klassifizierung). Reale Objektewerden auf die für die Software wichtigen Merkmale abstrahiert. Die Klasse dient als Bau-plan zur Abbildung von realen Objekten in Software-Objekte, die sogenannten Instanzen.

Page 169: 3446435476_Pytho

19.5 Kapselung von Daten 157

BILD 19.3 Kontenklasse

Die Klasse fasst hierfür notwendige Eigenschaften (Attribute) und zur Manipulation derEigenschaften notwendige Methoden zusammen. Klassen stehen häufig in Beziehung zu-einander. Man hat beispielsweise eine Oberklasse (Kuchen), und aus dieser leitet sich eineandere Klasse ab (Erdbeerkuchen). Diese abgeleitete Klasse erbt bestimmte Eigenschaftenund Methoden der Oberklasse.

19.5 Kapselung von DatenEin weiterer wesentlicher Vorteil der OOP besteht in der Kapselung von Daten. Der Zugriffauf Eigenschaften darf nur über Zugriffsmethoden erfolgen. Diese Methoden können Plau-sibilitätstests enthalten, und sie (oder „nur” sie) besitzen „Informationen” über die eigent-liche Implementierung. So kann z.B. eine Methode zum Setzen des Geburtsdatums prüfen,ob das Datum korrekt ist und sich innerhalb eines bestimmten Rahmens bewegt, z.B. Gi-rokonto für Kinder unter 14 nicht möglich oder Kunden über 100 Jahre unwahrscheinlich.

BILD 19.4 Datenkapselung

Page 170: 3446435476_Pytho

158 19 Objektorientierte Programmierung

19.6 VererbungIn unserem Beispiel erkennt man leicht, dass eine Klasse „Konto” einer realen Bank nichtgenügen kann. Es gibt verschiedene Arten von Konten: Girokonto, Sparkonto, usw. Aber al-len verschiedenen Konten sind bestimmte Eigenschaften und Methoden gemeinsam. Bei-spielsweise wird jedes Konto eine Kontonummer, einen Kontoinhaber und einen Konto-stand aufweisen. Gemeinsame Methoden: Einzahlen und Auszahlen Es gibt also so etwaswie ein Grundkonto, von dem alle anderen Konten „erben”. Die Vererbung dient also da-zu, unter Zugrundelegung von existierenden Klassen neue zu schaffen. Eine neue Klassekann dabei sowohl als eine Erweiterung als auch als eine Einschränkung der ursprüngli-chen Klasse entstehen.

BILD 19.5 Vererbung

19.7 Klassen in PythonEine Klasse besteht aus zwei Teilen: dem Kopf und einem Körper. Der Kopf besteht meistnur aus einer Zeile: das Schlüsselwort class, gefolgt von einem Leerzeichen, einem belie-bigen Namen, einer Liste von Oberklassen in Klammern und als letztes Zeichen ein Dop-pelpunkt. Hat man keine spezielle Oberklasse, wie es in unseren anfänglichen Beispielenimmer der Fall sein wird, dann gibt man als Oberklasse die Standard-Basisklasse objectan.

Der Körper einer Klasse besteht aus einer eingerückten Folge von Anweisungen, die manauch durch eine pass-Anweisung ersetzen kann. Jetzt wissen wir genug, um eine einfachePython-Klasse definieren zu können:

class Konto(object):pass

Anstelle von pass stünde bei einer „richtigen” Klasse die Definition der Attribute und Me-thoden dieser Klasse. Aber auch, wenn diese fehlen, können wir für unsere obige „sinnlose”Klasse bereits Objekte definieren:

Page 171: 3446435476_Pytho

19.8 Methoden 159

>>> class Konto(object):... pass...>>> x = Konto()>>> y = Konto()>>> z = x>>> print(x)<__main__.Konto object at 0xb725a2ec>

Wie wir sehen, können wie sogar eine Klasse ausdrucken. Die Ausgabe sagt uns, dass x eineInstanz der Klasse Konto im Namensraum __main__ ist. Die Zahl nach dem at gibt denSpeicherort dieser Klasse an.

19.8 MethodenEine Methode unterscheidet sich äußerlich nur in zwei Aspekten von einer Funktion:

■ Sie ist eine Funktion, die innerhalb einer class-Definition definiert ist.

■ Der erste Parameter einer Methode ist immer eine Referenz self auf die Instanz, von dersie aufgerufen wird.

Der Parameter self erscheint nur bei der Definition einer Methode. Beim Aufruf wird ernicht angegeben.

Beispiel mit Methode:

class Konto(object):def ueberweisen(self, ziel, betrag):

passdef einzahlen(self, betrag):

passdef auszahlen(self, betrag):

passdef kontostand(self):

pass

Obige Klasse können wir bereits benutzen, auch wenn wir natürlich kaum etwas Sinnvollesdamit anstellen können. Wir speichern sie unter dem Namen konto.py und führen folgendeAnweisungen in der interaktiven Python-Shell aus:

$ python3Python 3.2.3 (default, Oct 19 2012, 19:53:57)[GCC 4.7.2] on linux2Type "help", "copyright", "credits" or "license" for more information.>>> from konto import Konto>>> x = Konto()>>> x.einzahlen(12300000)>>>

Page 172: 3446435476_Pytho

160 19 Objektorientierte Programmierung

19.9 KonstruktorDie Methode __init__ wird aufgerufen, sobald ein Objekt einer Klasse in Python instanziiertwird. Meistens wird __init__ als der Konstruktor einer Klasse bezeichnet. Normalerweise,d.h. in anderen objektorientierten Sprachen, versteht man aber unter einem Konstruktoreine spezielle Methode, mit der ein Objekt konstruiert wird. In Python ist jedoch ein Objektbereits existent bzw. konstruiert, wenn die Methode __init__ aufgerufen wird. Die Methode__init__ wird dazu benutzt, wie der Name vermuten lässt, ein Objekt der Klasse zu initiali-sieren.

Die __init__-Methode wird im Prinzip wie jede andere Methode definiert:

def __init__(self,inhaber,kontonummer,kontostand,kontokorrent=0):

self.Inhaber = inhaberself.Kontonummer = kontonummerself.Kontostand = kontostandself.Kontokorrent = kontokorrent

Auch diese Klasse wollen wir in der interaktiven Shell ausprobieren:

$ python3Python 3.2.3 (default, Oct 19 2012, 19:53:57)[GCC 4.7.2] on linux2Type "help", "copyright", "credits" or "license" for more information.>>> from konto import Konto>>> x = Konto()Traceback (most recent call last):File "<stdin>", line 1, in <module>

TypeError: __init__() takes at least 4 arguments (1 given)>>> x = Konto("Bill Boe", 234322, 1000)

Wir erkennen, dass wir bei der Definition eines Objektes nun mindestens drei Parame-ter angeben müssen. Versucht man ein Objekt ohne Angabe von Argumenten zu generie-ren, erhält man eine Fehlermeldung, dass das Objekt mindestens vier Argumente („takes atleast 4 arguments”) erwarte, aber nur einen („1 given”) erhalten habe. Der scheinbare Wi-derspruch, dass wir kein Argument übergeben haben, und der Meldung „1 given” kommtdaher, dass das Argument „self” automatisch generiert wird.

19.10 DestruktorFür eine Klasse kann man auch einen Destruktor __del__ definieren. Wenn man eine In-stanz einer Klasse mit del löscht, wird der Destruktor aufgerufen. Allerdings nur, falls eskeine weitere Referenz auf diese Instanz gibt. Destruktoren werden selten benutzt, da man

Page 173: 3446435476_Pytho

19.11 Lauffähige Version der Kontoklasse 161

sich normalerweise nicht um das Aufräumen im Speicher kümmern muss. Im Folgendensehen wir ein Beispiel mit Konstruktor und Destruktor:

class Greeting:def __init__(self, name):

self.name = namedef __del__(self):

print("Destruktor gestartet")def SayHello(self):

print("Guten Tag", self.name)

Diese Klasse wird nun interaktiv benutzt:

$ python3Python 3.2.3 (default, Oct 19 2012, 19:53:57)[GCC 4.7.2] on linux2Type "help", "copyright", "credits" or "license" for more information.>>> from greeting import Greeting>>> x1 = Greeting("Bernd")>>> x2 = x1>>> del x1>>> del x2Destruktor gestartet

19.11 Lauffähige Version der Kontoklasse

class Konto(object):

def __init__(self, inhaber, kontonummer,kontostand,kontokorrent=0):

self.Inhaber = inhaberself.Kontonummer = kontonummerself.Kontostand = kontostandself.Kontokorrent = kontokorrent

def ueberweisen(self, ziel, betrag):if(self.Kontostand - betrag < -self.Kontokorrent):

# Deckung nicht genuegendreturn False

else:self.Kontostand -= betragziel.Kontostand += betragreturn True

def einzahlen(self, betrag):self.Kontostand += betrag

Page 174: 3446435476_Pytho

162 19 Objektorientierte Programmierung

def auszahlen(self, betrag):self.Kontostand -= betrag

def kontostand(self):return self.Kontostand

Speichert man obigen Code konto.py ab, kann man mit der Klasse in einer python-Shellwie folgt arbeiten:

$ python3Python 3.2.3 (default, Oct 19 2012, 19:53:57)[GCC 4.7.2] on linux2Type "help", "copyright", "credits" or "license" for more information.>>> from konto import Konto>>> konto1 = Konto("Jens",70711,1957.17)>>> konto2 = Konto("Maria",73813,4142.18)>>> konto1.kontostand()1957.17>>> konto1.einzahlen(42.83)>>> konto1.kontostand()2000.0>>> konto2.kontostand()4142.18>>> konto1.ueberweisen(konto2, 857.82)True>>> konto1.kontostand()1142.1799999999998>>> konto2.kontostand()kuchenkl5000.0>>>

19.12 Public-AttributeLeider hat unsere Klasse Konto() noch einen kleinen Schönheitsfehler. Man kann von au-ßen direkt auf die Attribute zugreifen, was dem Prinzip der Datenkapselung widerspricht:

>>> konto1.Kontostand1142.1799999999998>>> konto2.Kontostand5000.0>>> konto2.Kontonummer73813>>> konto2.Kontostand = 1000000>>> konto2.Kontostand1000000

Der einfache lesende Zugriff stellt natürlich im obigen Beispiel kein Problem dar, aberwenn jemand direkt den Kontostand verändert, hat das gravierende Folgen für unsere

Page 175: 3446435476_Pytho

19.13 Datenkapselung und die Benutzung von Public- Protected- und Private-Attributen 163

Bank. Selbst wenn der Kontostand korrekterweise wirklich eine Million nach einer Ein-zahlung betragen sollte, so wären bestimmte andere Größen der Buchführung, z.B. derGesamtbestand aller Konten, nicht mehr synchron. Normalerweise müsste ja bei jederEinzahlung diese Größe auch angepasst werden, was wir in unserem Beispiel nicht getanhaben. Die Methode Einzahlen und Überweisen müsste auch beispielsweise Maßnahmenbzgl. des Geldwäschegesetzes ergreifen, z.B. Warnmeldungen ausgeben, wenn eine Ein-zahlung über einen bestimmten Betrag geht. All dies wäre außer Kraft gesetzt, wenn eindirekter Zugriff auf die Attribute möglich ist.

19.13 DatenkapselungNormalerweise sind alle Attribute einer Klasseninstanz öffentlich, d.h. von außen zugäng-lich. Python bietet einen Mechanismus, um dies zu verhindern. Die Steuerung erfolgt nichtüber irgendwelche speziellen Schlüsselwörter, sondern über die Namen, d.h. ein einfacher,dem eigentlichen Namen vorangestellten Unterstrich für protected-Attribute und ein zwei-facher vorgestellter Unterstrich für private-Attribute, wie man der folgenden Tabelle ent-nehmen kann:

Namen Bezeichnung Bedeutungname Public Attribute ohne führende Unterstriche sind

sowohl innerhalb einer Klasse als auchvon außen les- und schreibbar.

_name Protected Man kann zwar auch von außen lesendund schreibend zugreifen, aber der Ent-wickler macht damit klar, dass man dieseMember nicht benutzen sollte.

__name Private Sind von außen nicht sichtbar und nichtbenutzbar.

Unsere Konto-Beispielklasse sieht mit „private”-Attributen wie folgt aus:

class Konto(object):

def __init__(self, inhaber, kontonummer,kontostand,kontokorrent=0):

self.__Inhaber = inhaberself.__Kontonummer = kontonummerself.__Kontostand = kontostandself.__Kontokorrent = kontokorrent

def ueberweisen(self, ziel, betrag):if(self.__Kontostand - betrag < -self.__Kontokorrent):

# Deckung nicht genuegendreturn False

else:self.__Kontostand -= betrag

Page 176: 3446435476_Pytho

164 19 Objektorientierte Programmierung

ziel.__Kontostand += betragreturn True

def einzahlen(self, betrag):self.__Kontostand += betrag

def auszahlen(self, betrag):self.__Kontostand -= betrag

def kontostand(self):return self.__Kontostand

Wir testen nun in der interaktiven Python-Shell, ob wir wirklich nicht auf die private-Attribute zugreifen können:

$bernd@saturn:~/bodenseo/python/examples$ python3Python 3.2.3 (default, Oct 19 2012, 19:53:57)[GCC 4.7.2] on linux2Type "help", "copyright", "credits" or "license" for more information.>>> from konto import Konto>>> konto = Konto("Jens",70711,2013.00)>>> konto.__KontostandTraceback (most recent call last):File "<stdin>", line 1, in <module>

AttributeError: 'Konto' object has no attribute '__Kontostand'

Eigentlich würde man die Fehlermeldung erwarten, dass man auf das Attribut __Konto-stand nicht zugreifen darf, da es private ist. Stattdessen kommt die obige Meldung, die sotut, als gäbe es kein Attribut __Kontostand in der Klasse Konto.

19.14 Statische MemberBisher hatte jedes Objekt einer Klasse seine eigenen Attribute und Methoden, die sich vondenen anderer Objekte unterschieden. Man bezeichnet dies als „nicht-statisch” oder dyna-misch, da sie für jedes Objekt einer Klasse dynamisch erstellt werden. Wie kann man aberz.B. die Anzahl der verschiedenen Instanzen einer Klasse zählen? In unserer Konto()-Klasseentspräche dies der Anzahl der verschiedenen Konten. Statische Attribute werden außer-halb des Konstruktors direkt im class-Block definiert. Es ist Usus, die statischen Memberdirekt unterhalb der class-Anweisung zu positionieren.

Im folgenden Beispiel zeigen wir, wie man Instanzen mittels einer Klassenvariablen zählenkann. Dazu erhöhen wir die Variable counter bei der Initialisierung jeder neuen Instanz.Wird eine Instanz gelöscht, wird die Methode __del__ aufgerufen, in der in unserem Bei-spiel die Klassenvariable counter um 1 vermindert wird:

Page 177: 3446435476_Pytho

19.14 Statische Member 165

19.14.1 __del__

class C(object):counter = 0

def __init__(self):C.counter += 1

def __del__(self):C.counter -= 1

if __name__ == "__main__":x = C()print("Anzahl der Instanzen: " + str(C.counter))y = C()print("Anzahl der Instanzen: " + str(C.counter))del xprint("Anzahl der Instanzen: " + str(C.counter))del yprint("Anzahl der Instanzen: " + str(C.counter))

Starten wir das obige Programm, erhalten wir folgende Ausgabe:

$ python3 counter.pyAnzahl der Instanzen: 1Anzahl der Instanzen: 2Anzahl der Instanzen: 1Anzahl der Instanzen: 0

Allerdings gibt es hierbei einen Haken. Nehmen wir an, dass wir die Variablen nicht explizitmittels __del__ zerstören, also die letzten vier Zeilen löschen:

class C(object):counter = 0

def __init__(self):C.counter += 1

def __del__(self):C.counter -= 1

if __name__ == "__main__":x = C()print("Anzahl der Instanzen: " + str(C.counter))y = C()print("Anzahl der Instanzen: " + str(C.counter))

erhalten wir eine Fehlermeldung:

$ python3 counter.pyAnzahl der Instanzen: 1Anzahl der Instanzen: 2

Page 178: 3446435476_Pytho

166 19 Objektorientierte Programmierung

Exception AttributeError: "'NoneType' object has no attribute 'counter'"in <bound method C.__del__ of <__main__.C object at 0xb71af20c>>ignored

Exception AttributeError: "'NoneType' object has no attribute 'counter'"in <bound method C.__del__ of <__main__.C object at 0xb71af1ac>>ignored

Die Fehlermeldungen resultieren daher, dass die Methode __del__ in diesem Fall erst auf-gerufen wird, wenn die Klasse bereits gelöscht ist.

Eine Lösung des Problems besteht darin, die globalen Variablen mit einem Unterstrich zuversehen. Variablen, die mit einem Unterstrich beginnen, werden zuerst gelöscht, wennein Programm endet.

class C(object):counter = 0

def __init__(self):C.counter += 1

def __del__(self):C.counter -= 1

if __name__ == "__main__":_x = C()print("Anzahl der Instanzen: " + str(C.counter))_y = C()print("Anzahl der Instanzen: " + str(C.counter))

Obiges Skript liefert nun die folgende Ausgabe:

$ python3 counter.pyAnzahl der Instanzen: 1Anzahl der Instanzen: 2

19.15 PropertiesIn der folgenden Klasse P wollen wir zeigen, wie man korrekt im Sinne der Datenkapselungauf private Attribute zugreift. In den meisten Fällen muss man ein Attribut lesen und än-dern können. Dazu muss man dann zwei Methoden schreiben: eine, um einen Attributwertlesen zu können (in unserem Fall „getX()”), und eine, um diesen Wert ändern zu können(in unserem Fall „setX()”). Diese Abfragemethoden werden allgemein auch als „getter” unddie Änderungsmethoden als „setter” bezeichnet.

class P(object):def __init__(self,x):

self.__x = x

def getX(self):

Page 179: 3446435476_Pytho

19.15 Properties 167

return self.__x

def setX(self, x):self.__x = x

In der folgenden interaktiven Beispielsitzung zeigen wir, wie man mit diesen Methodenarbeitet:

>>> from p import P>>> a = P(19)>>> a.getX()19>>> a.setX(42)>>> b = a.getX() * 0.56>>> b23.520000000000003>>>

Deutlich bequemer wäre es natürlich, wenn wir direkt

b = a.__x * 0.56

statt

b = a.getX() * 0.56

schreiben könnten. Dies geht jedoch nicht, weil __x ein private-Attribut ist, und wir wollenja keinesfalls das Konzept der Datenkapselung aufweichen oder verletzen. Mit den Pro-perties bietet Python ein Sprachkonstrukt, was einem einen leichteren Zugriff auf private-Attribute ermöglicht.

Wir erweitern obiges Beispiel um eine Property x:

class P(object):def __init__(self,x):

self.__x = x

def getX(self):return self.__x

def setX(self, x):self.__x = x

x = property(getX, setX)

Das erste Argument von property muss dem „getter”, also in unserem Fall der MethodegetX(), entsprechen. Das zweite Argument enthält den Namen der „setter”-Methode, al-so „setX()” in unserem Beispiel. Jetzt können wir x benutzen, als wäre es eine öffentlicheVariable:

>>> from p import P>>> a = P(19)>>> b = P(10)>>> c = a.x + b.x>>> c

Page 180: 3446435476_Pytho

168 19 Objektorientierte Programmierung

29>>>

Ohne Zweifel ist dies in der Benutzung angenehmer. Dennoch erfolgen alle Zugriffe auf dasprivate Attribut _x nur über die korrekten Zugriffsmethoden, also setX() und getX().

Möchte man lediglich einen lesenden Zugriff auf ein privates Attribut erlauben, dann ruftman die Property nur mit einem Argument, also der „getter”-Methode auf:

class P(object):def __init__(self,x):

self.__x = x

def getX(self):return self.__x

def setX(self, x):self.__x = x

x = property(getX)

Wir können in obiger Beispielanwendung sehen, dass wir lesend zugreifen können. Versu-chen wir jedoch, der Property x etwas zuzuweisen, erhalten wir eine Fehlermeldung.

>>> from p import P>>> a.x = 42Traceback (most recent call last):File "<stdin>", line 1, in <module>

AttributeError: can't set attribute>>>

19.16 Dynamische und statische AttributeBisher hatte jedes Objekt einer Klasse seine eigenen Attribute und Methoden, die sich vondenen anderer Objekte unterschieden. Man bezeichnet dies als „nicht-statisch” oder „dy-namisch”, da sie für jedes Objekt einer Klasse dynamisch erstellt werden. Wie kann manjedoch Informationen speichern, die sich nicht auf ein bestimmtes Objekt beziehen, son-dern für die ganze Klasse relevant sind? Eine solche Information wäre beispielsweise dieAnzahl aller Objekte einer Klasse, also in unserem Fall die Anzahl der verschiedenen Kon-ten der Klasse Konto.

Wie wir bereits gesehen haben, werden Instanzattribute innerhalb der __init__-Methodeangelegt, wohingegen statische Attribute außerhalb dieser Methode direkt im class-Blockdefiniert werden. Außerdem muss jedem Klassenattribut ein Initialwert zugewiesen wer-den. Es ist Usus, die statischen Member direkt unterhalb der class-Anweisung zu positio-nieren. Statische Attribute werden auch als Klassenattribute bezeichnet, weil sie wie bereitsgesagt, Eigenschaften bezeichnen, die für die ganze Klasse gelten und nicht nur für einzel-ne Objekte der Klasse. Ein Klassenattribut existiert pro Klasse nur einmal, wird also nureinmal angelegt. Instanzattribute werden für jedes Objekt angelegt.

Page 181: 3446435476_Pytho

19.17 Vererbung 169

Wir erläutern dies in einem kleinen Beispiel:

class P(object):__counter = 0;

def __init__(self,x):self.__x = xP.__counter += 1

def __del__(self):P.__counter -=1

def getCounter():return P.__counter

Wie das Zählen funktioniert, zeigen wir in der folgenden interaktiven Sitzung:

>>> from p_counter import P>>> x = P(42)>>> P.getCounter()1>>> y = P(44)>>> P.getCounter()2>>> z = x>>> P.getCounter()2>>> del x>>> P.getCounter()2>>> del y>>> P.getCounter()1>>> del z>>> P.getCounter()0>>>

19.17 Vererbung19.17.1 Oberbegriffe und Oberklassen

Jeder kennt die Oberbegriffe aus der natürlichen Sprache. Wenn wir von Fahrzeugen re-den, dann können dies Autos, Lastwagen, Motorräder, Busse, Züge, Schiffe, Flugzeuge oderauch Fahrräder und Roller sein. Autos kann man auch wieder nach verschiedenen Kriterienklassifizieren, z.B. nach der Antriebsart, Benziner, mit Diesel oder mit Strom angetriebeneAutos. In der objektorientierten Programmierung spricht man hierbei von Vererbung undvon Ober- und Unterklassen.

Page 182: 3446435476_Pytho

170 19 Objektorientierte Programmierung

BILD 19.6 Vererbung vonPersoneneigenschaften

Ein weiteres Beispiel eines Oberbegriffes stelltPerson dar. Häufig werden auch Klassen mo-delliert, die Eigenschaften von Personen inDatenstrukturen abbilden. Allgemeine Datenfür Personen sind Name, Vorname, Wohn-ort, Geburtsdatum, Geschlecht und so weiter.Wahrscheinlich würde man jedoch nicht Da-ten wie Steuernummer, Religionszugehörigkeitoder Ähnliches ausnehmen. Nehmen wir nunan, dass man diese Klasse für die Firmenbuch-haltung benutzen will. Man merkt schnell, dass nun die Information über die Religionszu-gehörigkeit, die Steuerklasse oder Kinderzahl fehlt, um das Nettogehalt korrekt zu berech-nen. Ebenso sieht es bei der Kundenklasse aus. Hier machen Information wie Religionszu-gehörigkeit, Steuerklasse oder Kinderzahl keinen Sinn, aber zum Beispiel die bevorzugteZahlungsmethode oder Ähnliches. Es macht also Sinn, die Personenklasse als Oberklassefür die Angestellten-, Kunden- und Lieferantenklasse einzuführen.

Ganz allgemein kann man sagen, dass die Vererbung eine Beziehung zwischen einer all-gemeinen Klasse (einer Oberklasse oder einer Basisklasse) und einer spezialisierten Klasse(der Unterklasse, manchmal auch Subklasse genannt) definiert.

19.17.2 Vererbung in Python

Bisher haben wir im Prinzip immer Unterklassen definiert. In der Kopfzeile hatten wir im-mer eine Basisklasse angegeben, nämlich „object”. Also beispielsweise bei der Definitionunserer Kontoklasse:

class Konto(object):pass

Jede Klasse erbt in Python von der Klasse „object”.

In der Einführung zur objektorientierten Programmierung hatten wir bereits allgemeinüber mögliche Unterklassen einer allgemeinen Klasse „Konto” gesprochen. Eine reale Bankhat viele verschiedene Kontenarten, so wie beispielsweise Sparbücher und Girokonten. EinSparbuch ebenso wie ein Girokonto erben also von der Basisklasse „Konto”, und ihre Kopf-zeilen enthalten den Namen der Basisklasse.

class Sparbuch(Konto):pass

class Girokonto(Konto):pass

Einige Konzepte der Vererbung möchten wir jedoch nicht an einem praktischen Beispielwie der Kontenklasse, sondern an einer abstrakten Oberklasse mit abstrakten Unterklassendemonstrieren.

Wir definieren eine Klasse A mit einer __init__-Methode und einer Methode m:

Page 183: 3446435476_Pytho

19.17 Vererbung 171

class A(object):

def __init__(self):self.content = "42"print("__init__ von A wurde ausgeführt")

def m(self):print("m von A wurde aufgerufen")

class B(A):pass

if __name__ == "__main__":x = A()x.m()y = B()y.m()

Ruft man diese Klasse direkt auf, erhalten wir folgende Ausgabe:

$ python3 inheritance1.py__init__ von A wurde ausgeführtm von A wurde aufgerufen__init__ von A wurde ausgeführtm von A wurde aufgerufen

Die Ergebnisse der ersten beiden Anweisungen hinter der if-Anweisung stellen nichts Neu-es dar. Aber wenn wir ein Objekt der Klasse B erzeugen, wie dies in der Anweisung „y = B()”geschieht, dann können wir an der Ausgabe erkennen, dass die __init__-Methode der Klas-se A ausgeführt wird, da B keine eigene __init__-Methode besitzt. Ebenso erkennen wir,dass beim Aufruf der Methode m für das Objekt y der Klasse B die Methode m von A aufge-rufen wird, denn B hat keine Methode m.

Was passiert, wenn wir die Klasse B nun auch mit einer __init__-Methode und einer Me-thode m ausstatten? Wir testen dies im folgenden Beispiel. Die Klasse A und unsere Testsbleiben gleich, wir ändern lediglich die Klasse B wie folgt:

class B(A):def __init__(self):

self.content = "43"print("__init__ von B wurde ausgeführt")

def m(self):print("m von B wurde aufgerufen")

Rufen wir das veränderte Skript nun auf, erhalten wir die folgende Ausgabe:

$ python3 inheritance2.py__init__ von A wurde ausgeführtm von A wurde aufgerufen__init__ von B wurde ausgeführtm von B wurde aufgerufen

Page 184: 3446435476_Pytho

172 19 Objektorientierte Programmierung

Wir erkennen nun, dass die __init__-Methode von B und nicht mehr die von A beim Erzeu-gen einer Instanz von B aufgerufen wird. Außerdem wird nun die Methode m von B stattder Methode m von A benutzt. Man bezeichnet dies als Überschreiben1 einer Methode.Man darf dies nicht mit Überladen verwechseln, was wir später behandeln werden.

Im folgenden Beispiel ändern wir nun die Instanzattribute der Klassen A und B. Wir habenin A ein Attribut self.contentA und in B eines mit dem Namen self.contentB.

class A(object):

def __init__(self):self.contentA = "42"print("__init__ von A wurde ausgeführt")

def m(self):print("m von A wurde aufgerufen")

class B(A):def __init__(self):

self.contentB = "43"print("__init__ von B wurde ausgeführt")

def m(self):print("m von B wurde aufgerufen")

if __name__ == "__main__":x = A()x.m()y = B()y.m()print("self.contentB von y: " + str(y.contentB))print("self.contentA von y: " + str(y.contentA))

Starten wir obiges Programm, sind eigentlich nur die beiden letzten Print-Anweisungenvon Interesse.

$ python3 inheritance3.py__init__ von A wurde ausgeführtm von A wurde aufgerufen__init__ von B wurde ausgeführtm von B wurde aufgerufenself.contentB von y: 43Traceback (most recent call last):File "inheritance3.py", line 25, in <module>print("self.contentA von y: " + str(y.contentA))

AttributeError: 'B' object has no attribute 'contentA'

Wie erwartet erhalten wir für self.contentB den Wert 43 als Ausgabe. Da die __init__-Methode der Klasse A nicht aufgerufen wird, sollte es uns nicht wundern, dass wir beim

1 engl. overwriting

Page 185: 3446435476_Pytho

19.17 Vererbung 173

Versuch, self.contentA auszugeben, einen AttributeError mit dem Text ,B’ object has noattribute ,contentA’ erhalten, denn es gibt ja kein Attribut self.contentB in den Instanzenvon B.

Wir können uns auch vorstellen, dass wir spezielle Attribute, die wir bei der Initialisierungeines Objektes einer Basisklasse einführen, auch in der Unterklasse verwenden wollen. Al-so in unserem Beispiel hätten wir vielleicht gerne das Attribut contentA. Wir können dieserreichen, indem wir in der __init__-Methode von B die __init__-Methode von A aufrufen.Dies geschieht durch die Anweisung „A.__init__(self)”, d.h. Name der Basisklasse plus Auf-ruf der __init__-Methode. Die Klasse A und die Tests bleiben gleich. Wir müssen lediglichdie __init__-Methode in B wie folgt ändern:

class B(A):def __init__(self):

A.__init__(self)self.contentB = "43"print("__init__ von B wurde ausgeführt")

Ruft man das gesamte Programm mit obiger Änderung auf, sehen wir, dass unser Attri-buteError verschwunden ist. Außerdem erkennen wir, dass die Instanz von y sowohl einAttribut contentB als auch ein Attribut contentA enthält:

$ python3 inheritance4.py__init__ von A wurde ausgeführtm von A wurde aufgerufen__init__ von A wurde ausgeführt__init__ von B wurde ausgeführtm von B wurde aufgerufenself.contentB von y: 43self.contentA von y: 42

Dieses Vorgehen lässt sich analog auf andere Methoden übertragen. Im Folgenden rufenwir in m von B auch die Methode m von A auf:

def m(self):A.m(self)print("m von B wurde aufgerufen")

Wir testen dieses Skript, das unter dem Namen inheritance5.py abgespeichert ist, in derinteraktiven Python-Shell:

>>> from inheritance5 import A, B>>> y = B()__init__ von A wurde ausgeführt__init__ von B wurde ausgeführt>>> y.m()m von A wurde aufgerufenm von B wurde aufgerufen>>>

Page 186: 3446435476_Pytho

174 19 Objektorientierte Programmierung

19.18 Mehrfachvererbung

19.18.1 Theorie

In unseren bisherigen Klassenbeispielen erbte eine Unterklasse immer nur von einer Basis-klasse. Es gibt aber Problemstellungen, in denen es sinnvoll ist, wenn eine Klasse von zweioder mehr Basisklassen erben kann. Man spricht in diesen Fällen von Mehrfachvererbung,wenn eine Klasse von mehr als einer Basisklasse erbt.

Syntaktisch realisiert man dies wie folgt: Soll eine Klasse von mehreren Basisklassen erben,gibt man die Basisklassen durch Kommata getrennt in die Klammern hinter dem Klassen-namen an:

class UnterKlasse(Basis1, Basis2, Basis3, ...):pass

Wie bei der einfachen Vererbung können Attribute und Methoden mit gleichen Namen inder Unterklasse und in Basisklassen vorkommen. Die obigen Basisklassen Basis1, Basis2usw. können natürlich ihrerseits wieder von anderen Basisklassen geerbt haben. Dadurcherhält man eine Vererbungshierarchie, die man sich als einen Baum vorstellen kann, denVererbungsbaum.

BILD 19.7 Vererbungsbaum bei Mehrfachvererbung

Die Frage stellt sich nun, wie Python auf der Suche nach einem Attribut- oder Methoden-namen vorgeht. Auch wenn es in Wirklichkeit komplizierter ist, kann man es sich so vor-stellen:

Zuerst erfolge eine Tiefensuche2, dann wird von links nach rechts3 gesucht, wobei nichtzweimal in derselben Klasse gesucht wird, wenn sich die Klassenhierarchie dort überlappt.Falls ein Attribut nicht in Unterklasse gefunden wird, wird anschließend in Basis1 gesucht,dann rekursiv in den Basisklassen von Basis1, also in unserem Beispiel Basis1.1, Basis1.2und Basis1.3. Wenn auch in all diesen Klassen nichts gefunden wurde, geht die Suche inBasis2 weiter, dann rekursiv in die Basisklassen von Basis2 usw.

Dies wollen wir an folgendem Beispiel demonstrieren:

2 englisch: depth-first search, DFS3 left-to-right search

Page 187: 3446435476_Pytho

19.18 Mehrfachvererbung 175

class B11(object):def m(self):

print("This is method m of class B11")

class B12(object):def m(self):

print("This is method m of class B12")

class B21(object):def m(self):

print("This is method m of class B21")

class B1(B11,B12):def m(self):

print("This is method m of class B1")

class B2(B21):def m(self):

print("This is method m of class B2")

class U(B1,B2):def m(self):

print("This is method m of class U")

x = U()x.m()B1.m(x)B12.m(x)

Startet man dieses Programm, sieht man, dass die Methode m von U aufgerufen wird, wennman m auf ein Objekt von U anwendet. Außerdem sehen wir, dass wir mit B1.m(x) dieMethode m von B1 dennoch auf ein Objekt von U anwenden können:

$ python3 mehrfachvererbung.pyThis is method m of class UThis is method m of class B1This is method m of class B12

Im Folgenden entfernen wir die Methode m aus der Klasse U und ersetzen den Klassen-rumpf durch die pass-Anweisung:

class B11(object):def m(self):

print("This is method m of class B11")

class B12(object):def m(self):

print("This is method m of class B12")

class B21(object):

Page 188: 3446435476_Pytho

176 19 Objektorientierte Programmierung

def m(self):print("This is method m of class B21")

class B1(B11,B12):def m(self):

print("This is method m of class B1")

class B2(B21):def m(self):

print("This is method m of class B2")

class U(B1,B2):pass

x = U()x.m()

Nach dem anfangs Gesagten müsste nun Python die erste, also die am weitesten links ste-hende Klasse der Basisklassen durchsuchen. Starten wir das Programm, sehen wir, dassnun wirklich m von B1 aufgerufen wird:

$ python3 mehrfachvererbung.pyThis is method m of class B1

Entfernt man nun die Methode m auch noch aus B1 und ersetzt auch hier den Rumpf durchpass, sieht man, dass nun die Methode m von B11 aufgerufen wird. Entfernen wir nun auchin B11 die Methode m, so wird m aus B12 aufgerufen. Wir können nun so weiter fortfahren:Entfernen wir m aus B12, wird anschließend m aus B2 aufgerufen.

19.18.2 Diamond-Problem

BILD 19.8 Diamond-Problem

Bei dem Diamond-Problem4 handelt es sichum ein Mehrdeutigkeitsproblem, was durchMehrfachvererbung in der objektorientier-ten Programmierung entstehen kann. Eskann auftreten, wenn eine Klasse D auf zweiverschiedenen Vererbungspfaden über eineKlasse B und eine Klasse C von der gleichenBasisklasse A abstammt. Falls D eine Metho-de, sagen wir „m”, aufruft, für die gilt:

■ m wird in A definiert

■ B überschreibt m

■ C überschreibt m

■ m wird nicht in D überschrieben.

4 englisch: diamond problem oder „deadly diamond of death”

Page 189: 3446435476_Pytho

19.18 Mehrfachvererbung 177

Die Frage, die sich dann stellt: Von welcher Klasse wird m vererbt?

In Python hängt es von der Reihenfolge der Klassen bei der Definition von D ab:

Falls D als class D(B,C): definiert ist, dann kommt m aus B; falls D ansonsten alsclass D(C,B): definiert ist, dann kommt m aus C.

Sicherlich fragen Sie sich noch, woher der Name „Diamond-Problem” kommt: Zeichnetman die Vererbungsbeziehungen zwischen den Klassen in einem Diagramm, so sieht dasErgebnis wie eine Raute aus, was im Englischen auch als „diamond” bezeichnet wird.

19.18.3 Beispiel: CalendarClock

BILD 19.9 CalendarClock

Bisher haben wir die Vererbung an sehrabstrakten und theoretischen Klassen er-klärt. Wir möchten nun an einem prakti-schen Beispiel die Vererbung oder genauersogar das Prinzip der Mehrfachvererbungdemonstrieren. Wir schreiben eine Klasse„CalendarClock”, die eine Uhr implemen-tiert, die Uhrzeit und Datum implemen-tiert.

Zunächst implementieren wir zwei von-einander unabhängige Klassen: eine Klas-se „Clock” und eine Klasse „Calendar”.

Die Klasse Clock simuliert das sekundenmäßige Ticken einer Uhr. Im Kapitel 8 (Verzwei-gungen) hatten wir in einer Übungsaufgabe das Ticken, also das Vorwärtsschreiten der Uhrum eine Sekunde, bereits simuliert (siehe Seite 58). Der Code der Lösung dieser Übungs-aufgabe (siehe Seite 385) befindet sich nun in unserer Methode tick().

Die komplette Klasse „Clock”:

"""Die Klasse Clock dient der logischen Simulation einer Uhr.Die Uhrzeit kann mit einer Methode sekundenweise weiterbewegtwerden."""

class Clock(object):

def __init__(self, h, min, sec):"""Die Parameter h, min, sec müssen Ganzzahlen sein,und es muss gelten:0 <= h < 240 <= min < 600 <= sec < 60"""

self.set_Clock(h,min,sec)

Page 190: 3446435476_Pytho

178 19 Objektorientierte Programmierung

def set_Clock(self, h, min, sec):"""Die Parameter h, min, sec müssen Ganzzahlen sein,und es muss gelten:0 <= h < 240 <= min < 600 <= sec < 60"""

if type(h) == int and 0 <= h and h < 24:self.hour = h

else:raise TypeError("Stunden müssen Ganzzahlen zwischen 0 und 23

sein!")if type(min) == int and 0 <= min and min < 60:

self.minute = minelse:

raise TypeError("Minuten müssen Ganzzahlen zwischen 0 und 59sein!")

if type(sec) == int and 0 <= sec and sec < 60:self.second = sec

else:raise TypeError("Sekunden müssen Ganzzahlen zwischen 0 und 59

sein!")

def __str__(self):"""Diese Methode überlädt die eingebaute Funktion str(),d.h. es wird eine Methode zur Verfügung gestellt,um ein Objekt der Klasse Clock in einen String zu wandeln.Die Methode __str__ wird auch von der Print-Funktiongenutzt, um ein Objekt der Klasse Clock auszugeben."""

return "{0:02d}:{1:02d}:{2:02d}".format(self.hour,self.minute,self.second)

def tick(self):"""Die interne Uhrzeit eines Objekts, d.h. die Stunden-,Minuten- und Sekunden-Attribute, werden um eine Sekundeweitergerechnet.

Beispiele:>>> x = Clock(12,59,59)>>> print(x)12:59:59>>> x.tick()>>> print(x)13:00:00

Page 191: 3446435476_Pytho

19.18 Mehrfachvererbung 179

>>> x.tick()>>> print(x)13:00:01"""

if self.second == 59:self.second = 0if self.minute == 59:

self.minute = 0if self.hour == 23:

self.hour = 0else:

self.hour += 1else:

self.minute += 1else:

self.second += 1

if __name__ == "__main__":x = Clock(12,59,59)print(x)x.tick()print(x)y = str(x)print(type(y))

Ruft man das Modul clock standalone auf, erhält man folgende Ausgabe:

$ python3 clock.py12:59:5913:00:00<class 'str'>

Was passiert, wenn jemand statt Ganzzahlen Fließkommazahlen oder Strings eintippt?Oder falls jemand eine Minutenzahl eingibt, die die Zahl 59 übersteigt? Wir wollen dieseund andere Fehlerfälle in der folgenden interaktiven Python-Shell-Sitzung testen:

>>> from clock import Clock>>> x = Clock(7.7,45,17)Traceback (most recent call last):File "<stdin>", line 1, in <module>File "clock.py", line 18, in __init__

self.set_Clock(h,min,sec)File "clock.py", line 32, in set_Clockraise TypeError("Stunden müssen Ganzzahlen zwischen 0 und 23 sein!")

TypeError: Stunden müssen Ganzzahlen zwischen 0 und 23 sein!>>> x = Clock(24,45,17)Traceback (most recent call last):File "<stdin>", line 1, in <module>File "clock.py", line 18, in __init__

Page 192: 3446435476_Pytho

180 19 Objektorientierte Programmierung

self.set_Clock(h,min,sec)File "clock.py", line 32, in set_Clockraise TypeError("Stunden müssen Ganzzahlen zwischen 0 und 23 sein!")

TypeError: Stunden müssen Ganzzahlen zwischen 0 und 23 sein!>>> x = Clock(23,60,17)Traceback (most recent call last):File "<stdin>", line 1, in <module>File "clock.py", line 18, in __init__

self.set_Clock(h,min,sec)File "clock.py", line 36, in set_Clockraise TypeError("Minuten müssen Ganzzahlen zwischen 0 und 59 sein!")

TypeError: Minuten müssen Ganzzahlen zwischen 0 und 59 sein!>>> x = Clock("23","60","17")Traceback (most recent call last):File "<stdin>", line 1, in <module>File "clock.py", line 18, in __init__

self.set_Clock(h,min,sec)File "clock.py", line 32, in set_Clockraise TypeError("Stunden müssen Ganzzahlen zwischen 0 und 23 sein!")

TypeError: Stunden müssen Ganzzahlen zwischen 0 und 23 sein!>>> x = Clock(23,17)Traceback (most recent call last):File "<stdin>", line 1, in <module>

TypeError: __init__() takes exactly 4 arguments (3 given)>>>

Unser Ziel ist ja eine Klasse „CalendarClock”, die eine Uhrzeitfunktion mit Kalenderfunk-tion kombiniert. Dazu benötigen wir noch eine Klasse „Calendar”. Ähnlich wie die tick-Methode bei der Clock-Klasse brauchen wir auch bei der Calendar-Klasse eine Methode,um den Kalender weiterzubewegen oder „weiterzublättern”. Wir nennen diese Methode„advance”. Sie zählt zu einem gegebenen Kalenderdatum einen Tag hinzu. Dabei müssenwir die Anzahl der Tage für die bestimmten Monate berücksichtigen, also z.B. nach dem 30.April kommt der 1. Mai, und nach dem 31. Januar kommt der 1. Februar. Apropos Februar,hier haben wir ein weiteres Problem, denn wir müssen nun wissen, ob es sich um ein Da-tum in einem Schaltjahr handelt oder nicht. Auch bei der Schaltjahrberechnung könnenwir von bereits getaner Arbeit profitieren: Die Aufgabenstellung und die Beschreibung, wieman Schaltjahre ermittelt, befindet sich auf der Seite 58. Die dazugehörige Lösung befin-det sich auf Seite 383). In unserer Klasse implementieren wir diese Funktionalität unterdem Namen „leapyear” als Klassenmethode, die . Für die Anzahl der Monate benutzen wireine Liste als Klassenattribut.

"""Die Klasse Calendar implementiert einen Kalender.Ein Kalenderdatum kann auf ein bestimmtes Datum gesetzt werdenoder kann um einen Tag weitergeschaltet werden."""

class Calendar(object):

months = [31,28,31,30,31,30,31,31,30,31,30,31]

Page 193: 3446435476_Pytho

19.18 Mehrfachvererbung 181

def leapyear(jahr):"""Die Methode leapyear liefert True zurück, wenn jahrein Schaltjahr ist, und False, wenn nicht."""

if jahr % 4 == 0:if jahr % 100 == 0:

if jahr % 400 == 0:schaltjahr = True

else:schaltjahr = False

else:schaltjahr = True

else:schaltjahr = False

return schaltjahr

def __init__(self, d, m, y):"""d, m, y have to be integer values and year has to bea four digit year number"""

self.set_Calendar(d,m,y)

def set_Calendar(self, d, m, y):"""d, m, y have to be integer values and year has to bea four digit year number"""

if type(d) == int and type(m) == int and type(y) == int:self.day = dself.month = mself.year = y

else:raise TypeError("d, m, y müssen ganze Zahlen sein!")

def __str__(self):"""Diese Methode überlädt die eingebaute Funktion str(),d.h. es wird eine Methode zur Verfügung gestellt,um ein Objekt der Klasse Calendar in einen String zu wandeln.Die Methode __str__ wird auch von der Print-Funktiongenutzt, um ein Objekt der Klasse Calendar auszugeben."""

Page 194: 3446435476_Pytho

182 19 Objektorientierte Programmierung

return "{0:02d}.{1:02d}.{2:4d}".format(self.day,self.month,self.year)

def advance(self):"""setzt den Kalender auf den nächsten Tagunter Berücksichtigung von Schaltjahren"""

max_days = Calendar.months[self.month-1]if self.month == 2 and Calendar.leapyear(self.year):

max_days += 1if self.day == max_days:

self.day= 1if self.month == 12:

self.month = 1self.year += 1

else:self.month += 1

else:self.day += 1

if __name__ == "__main__":x = Calendar(31,12,2012)print(x, end=" ")x.advance()print("nach advance: ", x)print("2012 war ein Schaltjahr:")x = Calendar(28,2,2012)print(x, end=" ")x.advance()print("nach advance: ", x)x = Calendar(28,2,2013)print(x, end=" ")x.advance()print("nach advance: ", x)print("1900 war kein Schaltjahr: Zahl durch 100, aber nicht durch 400

teilbar:")x = Calendar(28,2,1900)print(x, end=" ")x.advance()print("nach advance: ", x)print("2000 war ein Schaltjahr, weil die Zahl durch 400 teilbar ist

:")x = Calendar(28,2,2000)print(x, end=" ")x.advance()print("nach advance: ", x)

Page 195: 3446435476_Pytho

19.18 Mehrfachvererbung 183

Obiges Skript liefert folgende Ausgabe, wenn man es selbständig startet:

$ python3 calendar.py31.12.2012 nach advance: 01.01.20132012 war ein Schaltjahr:28.02.2012 nach advance: 29.02.201228.02.2013 nach advance: 01.03.20131900 war kein Schaltjahr: Zahl durch 100, aber nicht durch 400 teilbar:28.02.1900 nach advance: 01.03.19002000 war ein Schaltjahr, weil die Zahl durch 400 teilbar ist:28.02.2000 nach advance: 29.02.2000

In unserem Test haben wir keine Fehlertests eingebaut. Wir sehen aber im Code der Me-thode set_Calendar, dass wir einen Fehler erheben, wenn einer der Werte für das Datumkeine Ganzzahl ist. Wir testen die Fehlerfälle in der interaktiven Python-Shell:

>>> from calendar import Calendar>>> x = Calendar(31,12,2012)>>> x = Calendar("31",12,2012)Traceback (most recent call last):File "<stdin>", line 1, in <module>File "calendar.py", line 34, in __init__

self.set_Calendar(d,m,y)File "calendar.py", line 47, in set_Calendarraise TypeError("d, m, y müssen ganze Zahlen sein!")

TypeError: d, m, y müssen ganze Zahlen sein!>>> x = Calendar(12,2012)Traceback (most recent call last):File "<stdin>", line 1, in <module>

TypeError: __init__() takes exactly 4 arguments (3 given)>>>

Nun geht es darum, eine Klasse CalendarClock zu implementieren, die eine Uhr mit inte-griertem Kalender repräsentiert. Die Klasse CalendarClock erbt von der Klasse Clock undder Klasse Calendar. Die Methode tick von Clock wird von CalendarClock überschrieben.Die neue tick-Methode muss jedoch auf die tick-Methode von Clock zugreifen. Man kanndiese jedoch nicht mit self.tick() aufrufen, da dies die tick-Methode von CalendarClock re-präsentiert. Der Aufruf erfolgt deshalb mit Clock.tick(self). Die advance-Methode von Ca-lendar kann jedoch direkt mit self.advance() aufgerufen werden. Natürlich wäre der Aufrufauch mit Calendar.advance(self) möglich und richtig.

"""Modul, das die Klasse CalendarClock implementiert."""

from clock import Clockfrom calendar import Calendar

class CalendarClock(Clock, Calendar):"""

Die Klasse CalendarClock implementiert eine Uhr mit integrierterKalenderfunktion.

Page 196: 3446435476_Pytho

184 19 Objektorientierte Programmierung

Die Klasse erbt sowohl von der Klasse Clock als auch von derKlasse Calendar.

"""

def __init__(self,day, month, year, hour, minute, second):"""Zur Initialisierung der Uhrzeit wird der Konstruktor der Clock-

Klasseaufgerufen.Zur Initialisierung des Kalenders wird der Konstruktor der

Calendar-Klasseaufgerufen.

CalendarClock enthält dann die vereinigten Attribute derClock- und Calendar-Klasse:

self.day, self.month, self.year, self.hour, self.minute, self.second

"""

Clock.__init__(self,hour, minute, second)Calendar.__init__(self,day, month, year)

def tick(self):"""Die Position der Uhr wird um eine Sekunde weiterbewegt,der Kalender wird, falls Mitternacht überschritten wird,um einen Tag weiterbewegt."""

previous_hour = self.hourClock.tick(self)if (self.hour < previous_hour):

self.advance()

def __str__(self):"""Erzeugt die Stringdarstellung eines CalendarClock-Objekts"""s = "{0:02d}.{1:02d}.{2:4d}".format(self.day,

self.month,self.year)

s += " " + "{0:02d}:{1:02d}:{2:02d}".format(self.hour,self.minute,self.second)

return s

if __name__ == "__main__":

x = CalendarClock(31,12,2013,23,59,59)

Page 197: 3446435476_Pytho

19.19 Polymorphie 185

print("One tick from ",x, end=" ")x.tick()print("to ", x)

x = CalendarClock(28,2,1900,23,59,59)print("One tick from ",x, end=" ")x.tick()print("to ", x)

x = CalendarClock(28,2,2000,23,59,59)print("One tick from ",x, end=" ")x.tick()print("to ", x)

x = CalendarClock(7,2,2013,13,55,40)print("One tick from ",x, end=" ")x.tick()print("to ", x)

Ruft man obiges Modul standalone auf, erhält man folgende Ausgabe, die gleichzeitig auchdie Arbeitsweise nochmals ein wenig erklärt:

$ python3 calendar_clock.pyOne tick from 31.12.2013 23:59:59 to 01.01.2014 00:00:00One tick from 28.02.1900 23:59:59 to 01.03.1900 00:00:00One tick from 28.02.2000 23:59:59 to 29.02.2000 00:00:00One tick from 07.02.2013 13:55:40 to 07.02.2013 13:55:41

19.19 PolymorphieEin wesentliches Konzept der objektorientierten Programmierung stellt die Polymorphiedar. Der Wort Polymorphie oder Polymorphismus stammt aus dem griechischen und be-deutet Vielgestaltigkeit. Polymorphismus bezeichnet bei Methoden die Möglichkeit, dassman bei gleichem Namen Methoden mit verschiedenen Parametern aufrufen kann. Manspricht dann vom Überladen von Methoden. In Python kann man auch Operatoren undStandardfunktionen überladen, worauf wir im nächsten Abschnitt eingehen.

Methoden und Funktionen in Python haben bereits eine implizite Polymorphie wegen desdynamischen Typkonzepts von Python.

Betrachten wir die folgende Python-Funktion:

def f(x, y):print("values: ", x, y)

f(42,43)f(42, 43.7)f(42.3,43)f(42.0, 43.9)

Page 198: 3446435476_Pytho

186 19 Objektorientierte Programmierung

Die Funktion f rufen wir mit verschiedenen Typ-Paarungen auf. Beim ersten Aufruf mit(int, int), dann mit (int, float), dann (float, int) und im vierten Aufruf mit (float, float). Ingetypten Programmiersprachen wie C++ müssten wir entsprechend dieser Typ-Paarungenf in vier Varianten definieren. Ein C++-Programm, das dem obigen Python-Programm ent-spricht, könnte wie folgt aussehen:

#include <iostream>using namespace std;

void f(int x, int y ) {cout << "values: " << x << ", " << x << endl;

}

void f(int x, double y ) {cout << "values: " << x << ", " << x << endl;

}

void f(double x, int y ) {cout << "values: " << x << ", " << x << endl;

}

void f(double x, double y ) {cout << "values: " << x << ", " << x << endl;

}

int main(){

f(42, 43);f(42, 43.7);f(42.3,43);f(42.0, 43.9);

}

Bei der Funktion f unseres Python-Programms ist der Polymorphismus nicht nur aufInteger- und Float-Werte beschränkt. Wir können die Funktion auf alle Typen und Klassenanwenden, die sich mittels print drucken lassen:

>>> def f(x,y):... print("values: ", x, y)...>>> f([3,5,6],(3,5))values: [3, 5, 6] (3, 5)>>> f("A String", ("A tuple", "with Strings"))values: A String ('A tuple', 'with Strings')>>> f({2,3,9}, {"a"=3.4,"b"=7.8, "c"=9.04})File "<stdin>", line 1f({2,3,9}, {"a"=3.4,"b"=7.8, "c"=9.04})

^SyntaxError: invalid syntax>>> f({2,3,9}, {"a":3.4,"b":7.8, "c":9.04})values: {9, 2, 3} {'a': 3.4, 'c': 9.04, 'b': 7.8}>>>

Page 199: 3446435476_Pytho

19.20 Operator-Überladung 187

19.20 Operator-ÜberladungDie Parameterüberladung ist uns bereits häufig im Laufe dieses Buches begegnet, ohnedass es uns vielleicht bewusst war. Wir haben beispielsweise das Plus-Zeichen „+” sowohlfür die Addition von verschiedenen numerischen Werten, also Float- und Integerwerten,aber auch für die Konkatenation von Strings benutzt:

BILD 19.10 Überladen des+-Operators

>>> a = 3 + 4>>> a7>>> 3 + 5.5438.543>>> s = "Hello">>> print(s + " World")Hello World>>>

Python erlaubt es auch, dass wir auch für eigene Klassen den „+”-Operator überladen kön-nen. Um dies tun zu können, müssen wir jedoch den internen Mechanismus verstehen, derdie Überladung bewirkt. Für jedes Operatorzeichen gibt es eine spezielle Methode. Für „+”lautet der Name der Methode beispielsweise __add__, und für „-” lautet der Name __sub__.Bei __add__ und __sub__ handelt es sich um binäre Operatoren, weshalb die Methodenauch zwei Parameter benötigen: „self” und „other”. Steht in einem Skript

x + y

und sind x und y von der Klasse K, dann ruft Python die Methode __add__ mit

x.__add__(y)

auf, falls es eine solche Methode in der Klasse K gibt, ansonsten erfolgt die Fehlermeldung

Traceback (most recent call last):File "<stdin>", line 1, in <module>

TypeError: unsupported operand type(s) for +: 'K' and 'K'

Binäre Operatoren:

Operator Methode+ object.__add__(self, other)- object.__sub__(self, other)

* object.__mul__(self, other)// object.__floordiv__(self, other)/ object.__div__(self, other)% object.__mod__(self, other)** object.__pow__(self, other[, modulo])« object.__lshift__(self, other)» object.__rshift__(self, other)& object.__and__(self, other)

Page 200: 3446435476_Pytho

188 19 Objektorientierte Programmierung

Operator Methode^ object.__xor__(self, other)| object.__or__(self, other)

Erweiterte Zuweisungen:

Operator Methode+= object.__iadd__(self, other)-= object.__isub__(self, other)*= object.__imul__(self, other)/= object.__idiv__(self, other)//= object.__ifloordiv__(self, other)%= object.__imod__(self, other)**= object.__ipow__(self, other[, modulo])«= object.__ilshift__(self, other)»= object.__irshift__(self, other)&= object.__iand__(self, other)^= object.__ixor__(self, other)|= object.__ior__(self, other)

Unäre Operatoren:

Operator Methode- object.__neg__(self)+ object.__pos__(self)abs() object.__abs__(self)~ object.__invert__(self)complex() object.__complex__(self)int() object.__int__(self)long() object.__long__(self)float() object.__float__(self)oct() object.__oct__(self)hex() object.__hex__(self

19.21 Standardklassen als BasisklassenStatt selbstdefinierter Klassen kann man auch Standardklassen wie beispielsweise int,float, dict oder list als Basisklasse verwenden, um neue Klassen abzuleiten. Man kannbeispielsweise zusätzliche Methoden für Standardklassen definieren.

Page 201: 3446435476_Pytho

19.22 Aufgaben 189

Die Verarbeitung von Stapelspeichern5 wird in Python, wie wir gesehen haben, mittels ap-pend und pop realisiert. Üblicherweise, d.h. in anderen Programmiersprachen, werdenmeistens die Funktionen push6 und pop7 verwendet.

Wir wollen im Folgenden Beispiel die Klasse „list” um eine Methode „push” erweitern, diegenau das Gleiche macht wie append:

class Plist(list):

def __init__(self, l):list.__init__(self, l)

def push(self, item):self.append(item)

if __name__ == "__main__":x = Plist([3,4])x.push(47)print(x)

19.22 Aufgaben

1. Aufgabe:In dieser Aufgabe geht es um eine Roboterklasse. Uns interessiert nicht das Ausse-hen und die Beschaffenheit dieses Roboters. Wir interessieren uns lediglich für seinePosition in einer imaginären „Landschaft”. Diese Landschaft soll zweidimensional sein.Sie kann durch ein Koordinatensystem beschrieben werden.

BILD 19.11 Roboter

5 auch Kellerspeicher genannt, im Englischen stack6 deutsch: einkellern7 deutsch: auskellern

Page 202: 3446435476_Pytho

190 19 Objektorientierte Programmierung

Ein Roboter unserer Klasse hat also zwei Attribute, die die x- und die y-Koordinatedarstellen. Es empfiehlt sich natürlich, diese Informationen in einer 2er-Liste zusam-menzufassen, also beispielsweise position = [3,4], wobei dann 3 der x-Position und 4der y-Position entspricht. Der Roboter ist in eine der vier Richtungen „west”, „south”,„east” oder „north” orientiert. Wir brauchen also noch ein Attribut mit dieser Orientie-rung. Außerdem können wir noch ein weiteres Attribut mit dem Namen des Robotersdefinieren.Die genaue Beschreibung der benötigten Methoden können Sie der folgenden help-Beschreibung entnehmen:

Help on module robot:

NAMErobot

DESCRIPTIONRoboterklasse zur Positionsbeschreibung und Veränderung von Objektenin einem zweidimensionalen Koordinatensystem

CLASSESbuiltins.object

Robot

class Robot(builtins.object)| Methods defined here:|| __init__(self, x=0, y=0, orientation='north', name='marvin')|| __str__(self)| Stringdarstellung einer Instanz|| getName(self)| Liefert den Namen des Roboters zurück|| getOrientation(self)| Ein Aufruf von x.getOrientation() für einen Roboter x liefert| dessen aktuelle Orientierung zurück, also eine der Richtungen| "west", "south", "east" oder "north".|| getPosition(self)| Liefert eine 2er-Liste mit [x,y] zurück.|| move(self, distance)| Methode zum Bewegen eines Roboters in Richtung seiner| aktuellen Orientierung.|| Wird ein Roboter x mit x.move(10) aufgerufen und ist dieser| Roboter östlich orientiert, also x.getPosition() == ,,east''| und ist beispielsweise [3,7] die aktuelle Position des| Roboters, dann bewegt er sich 10 Felder östlich und befindet| sich anschließend in Position [3,17].|| Falls eine unbekannte Orientierung übergeben wird, wird der| Roboter nicht bewegt.|| newOrientation(self, o)

Page 203: 3446435476_Pytho

19.22 Aufgaben 191

| Mit der Methode newOrientation ändern wir die Orientierung| des Roboters.|| o has to be in {"north","south","west","east"}|| rename(self, name)| Damit kann man dem Roboter einen neuen Namen geben.|| setPosition(self, pos)| Damit kann man den Roboter auf eine neue Position im| Koordinatensystem positionieren.|| pos muss eine Liste oder ein Tupel mit zwei Elementen sein| Ansonsten wird nichts getan.|| ----------------------------------------------------------------| Data descriptors defined here:|| __dict__

| dictionary for instance variables (if defined)|| __weakref__

| list of weak references to the object (if defined)

FILE/home/data/bodenseo/python/beispiele/robot.py

(END)

Lösung: 1. Aufgabe, Seite 400

2. Aufgabe:

BILD 19.12 Neandertal-Arithmetik

Neanderthal- oder Bierde-ckelarithmetik: Üblicher-weise zählen und rechnenwir im Dezimalsystem.Informatiker und Program-mierer haben manchmalauch Kontakt mit dem Bi-närsystem. Aber es gibtauch eine Berufsgruppe,die von Berufs wegen miteinem unären Zahlsystemrechnet. Ja richtig, in derGastronomie. In der fol-genden Aufgabe geht esum die Bierdeckelnotation

oder auch Bierdeckelarithmetik. Es könnte auch die Art gewesen sein, wie unsere inHöhlen lebenden Vorfahren mit Zahlen umgegangen sind.Das Unärsystem ist ein Zahlensystem, in dem lediglich eine Ziffer vorhanden ist, dieman üblicherweise mit einem senkrechten Strich bezeichnet. Also der Strich auf dem

Page 204: 3446435476_Pytho

192 19 Objektorientierte Programmierung

Bierdeckel. Man könnte aber ebenso gut eine Null, eine Eins oder irgendein anderesZeichen verwenden.Es gilt:

7dezi mal = |||||||unär

Schreiben Sie eine Klasse CaveInt, die die vier Grundrechenarten für die Neander-talarithmetik definiert. Auch wenn die Höhlenmenschen keine Null kannten, definierenwir einen leeren String als die Null. Da es keine negativen Zahlen gibt, definieren wirdie Subtraktion wie folgt: x - y ist gleich dem leeren String, wenn x kleiner als y ist.Außerdem soll eine Cast-Methode für die Wandlung von unär nach int bereitgestelltwerden.Lösung: 2. Aufgabe, Seite 403

3. Aufgabe:Wir haben in diesem Kapitel die Standardklasse list um die Methode push erweitert.In Perl gibt es noch die Methode „splice”, mit der eine Folge von Elementen aus einerListe gelöscht werden kann und durch eine Liste von anderen Elementen ersetzt wird.

splice(offset, length, replacement);

„offset” entspricht dem ersten Index, ab dem die Elemente gelöscht werden sollen,und der Parameter „length” gibt an, wie viele Elemente gelöscht werden sollen. An dieStelle der gelöschten Elemente sollen die Elemente der Liste replacement eingefügtwerden.Erweitern Sie nun die Klasse plist um eine Methode splice.Lösung: 3. Aufgabe, Seite 404

Page 205: 3446435476_Pytho

TEIL IIWeiterführende Themen

Page 206: 3446435476_Pytho
Page 207: 3446435476_Pytho

20 Tests und Fehler

20.1 Einführung

BILD 20.1 Fehler sind menschlich

Es ist kein Zufall, dass wir unseren weiter-führenden Teil mit einem Kapitel über Fehler,Fehlersuche und Fehlervermeidung beginnen.Tests und Fehlersuche gehören zum Alltag ei-nes jeden Programmierers. Die Zeit, die zurFehlersuche und zum Testen eines Programmsverwendet wird, hat üblicherweise einen ho-hen Anteil an der Programmentwicklung, Stu-dien sprechen von bis zu 50 %.1 Dass Fehlermenschlich seien, wurde schon vor mehr als2000 Jahren von Cicero festgestellt. Sein „er-rare humanum est”2 gilt häufig auch als Aus-rede, um unzureichende Arbeitsergebnisse zuentschuldigen. Auch wenn wir Fehler nie voll-ständig vermeiden werden können, sollten wirdanach trachten, die Anzahl der Fehler, die wir machen, und vor allen diejenigen, die imProdukt bleiben könnten, minimal zu halten.

Es gibt unterschiedliche Fehlerarten. Während der Programmentwicklung gibt es immerwieder „kleine Fehler”, häufig sind es kleine Tippfehler. Irgendwo fehlt immer mal ein Dop-pelpunkt, so zum Beispiel hinter einem „else”, oder man hat vielleicht „true” statt True ge-schrieben. All dies sind sogenannte syntaktische Fehler oder Syntaxfehler.3 Dabei handeltes sich um die Verletzung von Schreibweisen von einzelnen Wörtern, also zum Beispiel

1 Boehm und Barry sprechen von 40 - 50 %, die zum Beheben von Design- und Code-Fehlern benötigt wer-den: Boehm, Barry W. 1987. Ïmproving Software Productivity.ÏEEE Computer, September: 43-57.; Jones, Ca-pers, ed. 1986.

2 Der Spruch stammt von Cicero und lautet in voller Länge: „Errare (Errasse) humanum est, sed in errare (er-rore) perseverare diabolicum.” In Deutsch lautet dies: „Irren ist menschlich, aber auf Irrtümern zu beharrenist teuflisch.”

3 Unter der Syntax einer Programmiersprache versteht man die Beschreibung der erlaubten Zeichenkettenfür Programme dieser Sprache. Fast alle Programmiersprachen werden mittels kontextfreier Grammatikensyntaktisch beschrieben. Man spricht auch in natürlichen Sprachen von syntaktischen Fehlern. „I is lear-ning Python” ist beispielsweise ein Syntaxfehler in der englischen Sprache.

Page 208: 3446435476_Pytho

196 20 Tests und Fehler

Schlüsselwörtern4 oder von Strukturregeln. In der Linguistik würde man das als Satzbau-regeln bezeichnen, also zum Beispiel der vergessene Doppelpunkt hinter dem „else” odereckige statt runder Klammern bei einem Funktionsaufruf.

Neben den syntaktischen Fehlern, die sich häufig relativ leicht finden und beheben lassen,gibt es auch die semantischen Fehler.5

Betrachten wir folgenden Programmausschnitt:

x = int(input("x? "))y = int(input("y? "))

if x > 10:if y == x:

print("Fine")else:

print("So what?")

Es gibt zwei if-Anweisungen, aber nur eine else-Anweisungen. Das Codefragment ist syn-taktisch korrekt. Es könnte aber sein, dass der Schreiber des Programmes den Text „Sowhat?” nur ausgeben wollte, wenn x größer als 10 ist und der Wert von y dem von x ent-spricht. Das bedeutet, dass er folgenden Code hätte schreiben müssen:

x = int(input("x? "))y = int(input("y? "))

if x > 10:if y == x:

print("Fine")else:

print("So what?")

Beide Programme bzw. Programmfragmente sind syntaktisch korrekt. Aber einer von bei-den verstößt gegen die intendierte Semantik, dass der Text „So what?” nur ausgegebenwerden sollte, wenn x größer als 10 ist und der Wert von y dem von x entspricht. Der Pro-grammierer hatte wahrscheinlich die Semantik der if-Anweisung korrekt verstanden, abersein Problem nicht richtig umgesetzt. Es ist aber auch denkbar, dass ein Programmiererdie Semantik eines Python-Konstrukts nicht richtig verstanden und deshalb einen Fehlerproduziert hat.

Betrachten wir hierzu das folgende Beispiel:

>>> for i in range(7):... print(i)...012

4 englisch: key words5 Die Bedeutung der einzelnen Sprachkonstrukte einer Sprache wird als die Semantik einer Programmier-

sprache bezeichnet. Normalerweise beißen Männer keine Hunde, sodass man im folgenden Satz von einemSemantikfehler ausgehen kann: „The man bites the dog!”

Page 209: 3446435476_Pytho

20.2 Modultests 197

3456>>>

Aus der Tatsache, dass es keinen Fehler bei der Ausführung gab, ersehen wir, dass dieseAnweisungen syntaktisch korrekt sind. Ob sie jedoch semantisch korrekt sind, können wirnicht entscheiden, da wir ja nicht die Problemstellung kennen. Nehmen wir an, dass derProgrammierer die Zahlen von 1 bis 10 hatte ausgeben wollen. In diesem Fall hat er einensemantischen Fehler begangen, der wohl daraus resultiert, dass er die range-Funktionnicht richtig verstanden hat. Er ging irrtümlich davon aus, dass range(7) die Zahlen von 1bis 7 produziere, d.h. 1, 2, 3, 4, 5, 6 und 7.

Wir können also die Semantikfehler in zwei Gruppen unterteilen:

■ Fehler, die aus dem mangelnden Verständnis eines Sprachkonstrukts herrühren.

■ Fehler, die aus der fehlerhaften Umsetzung des Problems resultieren.

20.2 Modultests

BILD 20.2 Testen und Messen

Für den Begriff Modultest oder Kompo-nententest wird häufig im Deutschen derenglische Begriff „Unittest” („unit test”)verwendet. Modultests verwendet man inder Software-Entwicklung, um Module, al-so funktionale Einzelteile eines Program-mes zu testen, d.h. man prüft, ob sie diegeforderte Funktionalität bringen. Es emp-fiehlt sich, Tests auf Modulebene durch-zuführen, da die dortigen Funktionalitätennoch eine begrenzte bzw. überschaubareKomplexität zeigen und die Schnittstellennoch klar definiert sind. Häufig kann manso ein Modul vollständig auf Korrektheitprüfen. Dies ist auf einem umfassenden Software-Paket in der Regel nicht mehr möglich.Niemand kann beispielsweise ein Betriebssystem vollständig auf Korrektheit prüfen.

20.3 Modultests unter Benutzung von__name__

Jedes Python-Modul hat einen im built-in-Attribut __name__ definierten Namen. Nehmenwir an, wir haben ein Modul mit dem Namen „abc”, gespeichert unter „abc.py”. Wird die-

Page 210: 3446435476_Pytho

198 20 Tests und Fehler

ses Modul mit „import abc” importiert, dann hat das built-in-Attribut __name__ den Wert„abc”. Wird abc als eigenständiges Programm aufgerufen, also mittels

$python3 abc.py,

dann hat diese Variable den Wert ’__main__’.

Anhand des folgenden Moduls (das sowohl eine einzelne Fibonacci-Zahl für eine bestimm-te Generation n als auch die Liste der Fibonacci-Zahlen bis zu einer Generation n be-rechnen kann) wollen wir veranschaulichen, wie man mit dem built-in-Attribut __name__einen einfachen, aber sehr wirkungsvollen Modultest durchführen kann.

Das fibonacci-Modul sieht wie folgt aus und ist unter dem Namen fibonacci.py in unseremBeispielverzeichnis zu finden:

""" Modul mit wichtigen Funktionen zur Fibonacci-Folge """

def fib(n):""" Die Fibonacci-Zahl für die n-te

Generation wird iterativ berechnet """a, b = 0, 1for i in range(n):

a, b = b, a + breturn a

def fiblist(n):""" produziert die Liste der Fibbo-Zahlen

bis zur n-ten Generation """fib = [0,1]for i in range(1,n):

fib += [fib[-1]+fib[-2]]return fib

Natürlich kann man dieses Modul auch „manuell” in der interaktiven Python-Shell testen:

>>> from fibonacci import fib, fiblist>>> fib(0)0>>> fib(1)1>>> fib(10)55>>> fiblist(10)[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]>>> fiblist(-8)[0, 1]>>> fib(-1)0>>> fib(0.5)Traceback (most recent call last):File "<stdin>", line 1, in <module>File "fibonacci.py", line 6, in fibfor i in range(n):

TypeError: 'float' object cannot be interpreted as an integer>>>

Page 211: 3446435476_Pytho

20.3 Modultests unter Benutzung von __name__ 199

Wir sehen, dass die Funktionen auf Integer-Zahlen definiert sind. Für negative ganze Zah-len liefert die Funktion fib nur Nullen zurück, während fiblist nur die Liste [0,1] zurück-liefert. Ruft man die Funktionen mit Float-Zahlen auf, gibt es einen TypeError, weil rangenicht für Float-Werte definiert ist.

Unser Modul könnten wir testen, indem wir testen, ob die Aufrufe für fib() und fiblist() fürbestimmte Werte definierte Ergebniswerte zurückliefern.

Man könnte also unser Modul direkt um eine oder mehrere if-Anweisungen erweitern:

if fib(0) == 0 and fib(10) == 55 and fib(50) == 12586269025:print("Test für fib-Funktion erfolgreich")

else:print("fib-Funktion liefert fehlerhafte Werte")

Ruft man das Programm dann direkt auf, erhält man folgende Ausgabe:

$ python3 fibonacci.pyTest für fib-Funktion erfolgreich

Nun wollen wir bewusst einen Fehler in unsere fib-Funktion einbauen. Dazu ändern wirdie Zeile

a, b = 0, 1

in

a, b = 1, 1

um.6

Ein Aufruf des veränderten Moduls liefert nun eine Fehlermeldung:

$ python3 fibonacci.pyfib-Funktion liefert fehlerhafte Werte

Dieses Vorgehen hat jedoch einen entscheidenden Nachteil. Wenn man das Modul impor-tiert, wird auch das Ergebnis dieses oder ähnlicher Tests angezeigt:

>>> import fibonacciTest für fib-Funktion erfolgreich

Es ist aber störend und auch nicht üblich, wenn Module solche Meldungen beim importausgeben. Module sollen sich „schweigend” laden lassen.

Die Lösung für dieses Problem stellt das eingangs erwähnte built-in-Attribut __name__dar. Wird unser Modul direkt gestartet, also nicht importiert, hat __name__ den Wert„__main__”.

Wir können unser Modul nun so umschreiben, dass die Tests nur gestartet werden, wenndas Modul direkt gestartet wird:

6 Im Prinzip liefert fib zwar noch die Fibonacci-Werte, aber um eins versetzt. Wollen wir den n-ten Wert (fürn größer als 0 berechnen), so müssen wir fib(n-1) aufrufen.

Page 212: 3446435476_Pytho

200 20 Tests und Fehler

""" Modul mit wichtigen Funktionen zur Fibonacci-Folge """

def fib(n):""" Iterative Fibonacci-Funktion """a, b = 0, 1for i in range(n):

a, b = b, a + breturn a

def fiblist(n):""" produziert Liste der Fibbo-Zahlen """fib = [0,1]for i in range(1,n):

fib += [fib[-1]+fib[-2]]return fib

if __name__ == "__main__":if fib(0) == 0 and fib(10) == 55 and fib(50) == 12586269025:

print("Test für fib-Funktion erfolgreich")else:

print("fib-Funktion liefert fehlerhafte Werte")

Nun gibt es keine Ausgaben, wenn das Modul importiert wird, und zwar weder im Fehlerfallnoch im Erfolgsfall.

Diese Methode ist die einfachste und am weitesten verbreitetste Methode für Modultests.

20.4 doctest-ModulDas doctest-Modul stellt eine weitere einfache Methode dar, Modultests durchzuführen.Der eigentliche Test befindet sich bei dieser Methode, wie der Name vermuten lässt, imDocstring.

Vorgehensweise: Man muss das Modul „doctest” importieren. Dann kopiert man einenAuszug aus einer interaktiven Sitzung in den Docstring einer Funktion.

Im Folgenden zeigen wir das Vorgehen an einem simplen Beispiel. Dazu haben wir dasvorige fibonacci-Modul bis auf die Funktion fib abgespeckt:

import doctest

def fib(n):"""Die Fibonacci-Zahl für die n-teGeneration wird iterativ berechnet"""a, b = 0, 1for i in range(n):

a, b = b, a + breturn a

Page 213: 3446435476_Pytho

20.4 doctest-Modul 201

Dieses Modul rufen wir nun in einer interaktiven Python-Shell auf und lassen ein paar Wer-te berechnen:

>>> from fibonacci import fib>>> fib(0)0>>> fib(1)1>>> fib(10)55>>> fib(15)610>>>

Diese Aufrufe mit den Ergebnissen kopieren wir aus der interaktiven Shell in den Docstringunserer Funktion. Damit das Modul doctest aktiv wird, müssen wir die Methode testmod()starten, falls das Modul direkt aufgerufen wird. Dies können wir wie üblich mit einem Testdes Attributs __name__ auf den Wert „__main__” machen. Das vollständige Modul siehtnun wie folgt aus:

import doctest

def fib(n):"""Die Fibonacci-Zahl für die n-teGeneration wird iterativ berechnet

>>> fib(0)0>>> fib(1)1>>> fib(10)55>>> fib(15)610>>>

"""a, b = 0, 1for i in range(n):

a, b = b, a + breturn a

if __name__ == "__main__":doctest.testmod()

Starten wir obiges Modul direkt mit dem Aufruf

$ python3 fibonacci_doctest.py

erhalten wir keine Ausgabe, weil alles okay ist.

Page 214: 3446435476_Pytho

202 20 Tests und Fehler

Deshalb bauen wir wieder einen kleinen Fehler ein. Dazu ändern wir wieder die Zeile

a, b = 0, 1

in

a, b = 1, 1

um.

Nun erhalten wir folgende Ausgabe beim direkten Start des Moduls:

$ python3 fibonacci_doctest.py

**********************************************************************File "fibonacci_doctest.py", line 8, in __main__.fibFailed example:

fib(0)Expected:

0Got:

1

**********************************************************************File "fibonacci_doctest.py", line 12, in __main__.fibFailed example:

fib(10)Expected:

55Got:

89

**********************************************************************File "fibonacci_doctest.py", line 14, in __main__.fibFailed example:

fib(15)Expected:

610Got:

987

**********************************************************************1 items had failures:

3 of 4 in __main__.fib

***Test Failed*** 3 failures.

Es werden alle Aufrufe angezeigt, die ein fehlerhaftes Ergebnis geliefert haben. Wir sehenjeweils den Beispielaufruf hinter der Zeile „Failed example:”. Hinter „Expected:” folgt dererwartete Wert, also der korrekte Wert, und hinter „Got:” folgt der von der Funktion produ-zierte Ausdruck, also der Wert, den doctest beim Aufruf von fib erhalten hat.

20.5 Testgetriebene EntwicklungIm vorigen Kapitel hatten wir bereits eine fertig geschriebene Fibonacci-Funktion. Mankann auch so vorgehen, dass man bereits am Anfang Ergebnisse in den Docstring schreibt

Page 215: 3446435476_Pytho

20.5 Testgetriebene Entwicklung oder „Im Anfang war der Test” 203

und die Funktion dann erst entwickelt. Im Folgenden haben wir den Rückgabewert derFunktion fib fest auf 0 gesetzt:

import doctest

def fib(n):"""Die Fibonacci-Zahl für die n-teGeneration wird iterativ berechnet

>>> fib(0)0>>> fib(1)1>>> fib(10)55>>> fib(15)610>>>

"""

return 0

if __name__ == "__main__":doctest.testmod()

Es versteht sich von selbst, dass ein Test dieses Moduls außer für fib(0) nur Fehler liefert:

$ python3 fibonacci_TDD.py

**********************************************************************File "fibonacci_TDD.py", line 10, in __main__.fibFailed example:

fib(1)Expected:

1Got:

0

**********************************************************************File "fibonacci_TDD.py", line 12, in __main__.fibFailed example:

fib(10)Expected:

55Got:

0

**********************************************************************File "fibonacci_TDD.py", line 14, in __main__.fibFailed example:

fib(15)Expected:

610

Page 216: 3446435476_Pytho

204 20 Tests und Fehler

Got:0

**********************************************************************1 items had failures:

3 of 4 in __main__.fib

***Test Failed*** 3 failures.

Man ändert bzw. schreibt nun den eigentlichen Code der Funktion fib solange, bis die Testsim Doctest „bestanden” werden.

Dieses Vorgehen ist eine Methode der Software-Entwicklung, die man als „TestgetriebeneEntwicklung” oder auch „Testgesteuerte Entwicklung” bezeichnet. Wie immer in der SW-Branche werden auch häufig nur die englischen Begriffe benutzt, d.h. „test first develop-ment” oder noch geläufiger „test-driven development” (TDD).

20.6 unittestFür das Modul „unittest” standen JUnit7 von Erich Gamma und SUnit8 von Kent Beck Pate.

Ein deutlicher Unterschied zum Modul doctest besteht darin, dass die Testfälle bei demModul „unittest” außerhalb des eigentlichen Programmcodes definiert werden, d.h. in ei-ner eigenen Datei. Der Vorteil besteht unter anderem darin, dass die Programmdokumen-tation und die Testbeschreibungen voneinander getrennt sind. Der Preis dafür besteht be-steht jedoch in einem erhöhten Aufwand für die Erstellung der Tests.

Wir wollen nun für unser Modul fibonacci.py einen Test mit unittest erstellen. In einer Da-tei fibonacci_unittest.py9 müssen wir das Modul unittest und das zu testende Modul, alsoin unserem Fall fibonacci, importieren.

Außerdem müssen wir eine Klasse mit beliebigem Namen – wir wählen in unserem Bei-spiel „FibonacciTest” – erstellen, die von unittest.TestCase erbt. In dieser Klasse werden dienötigen Testfälle in Methoden definiert. Der Name dieser Methoden ist beliebig, er mussjedoch mit test beginnen. In unserer Methode „testCalculation” benutzen wir die MethodeassertEqual der Klasse TestCase. assertEqual(first, second, msg = None) prüft, ob der Aus-druck „first” gleich dem Ausdruck „second” ist. Falls die beiden Ausdrücke ungleich sind,wird msg ausgegeben, wenn msg ungleich None ist.

import unittestfrom fibonacci import fib

class FibonacciTest(unittest.TestCase):

def testCalculation(self):self.assertEqual(fib(0), 0)

7 Dabei handelt es sich um ein Framework zum Testen von Java-Programmen, das für automatisierte Unit-Tests von einzelnen Klassen und Methoden besonders geeignet ist

8 SUnit ist ein Smalltalk-Testframework, das von Kent Beck erfunden wurde. SUnit wurde auf Java unter demNamen JUnit portiert.

9 Der Name kann beliebig gewählt sein. Wichtig ist lediglich, dass der Testfall in einer separaten Datei steht.

Page 217: 3446435476_Pytho

20.6 unittest 205

self.assertEqual(fib(1), 1)self.assertEqual(fib(5), 5)self.assertEqual(fib(10), 55)self.assertEqual(fib(20), 6765)

if __name__ == "__main__":unittest.main()

Rufen wir obigen Testfall auf, erhalten wir folgende Ausgabe:

$ python3 fibonacci_unittest.py.----------------------------------------------------------------------Ran 1 test in 0.000s

OK

Bei der normalen Programmentwicklung ist dies das von uns gewünschte Ergebnis. Hiersind wir allerdings interessiert, was im Fehlerfall passiert. Wir produzieren deshalb wiederunseren Fehler. Dazu ändern wir von neuem die Zeile

a, b = 0, 1

in

a, b = 1, 1

um.

Jetzt sieht der Test wie folgt aus:

$ python3 fibonacci_unittest.pyF======================================================================FAIL: testCalculation (__main__.FibonacciTest)----------------------------------------------------------------------Traceback (most recent call last):File "fibonacci_unittest.py", line 7, in testCalculationself.assertEqual(fib(0), 0)

AssertionError: 1 != 0

----------------------------------------------------------------------Ran 1 test in 0.000s

FAILED (failures=1)

Bereits die erste Anweisung in testCalculation hat eine Ausnahme erzeugt. In diesem Fallwurden die weiteren assertEqual-Aufrufe nicht mehr ausgeführt. Wir verändern fib nundahingehend, dass wir nur einen falschen Wert erhalten, wenn n auf 20 gesetzt ist:

def fib(n):""" Iterative Fibonacci-Funktion """a, b = 0, 1for i in range(n):

a, b = b, a + b

Page 218: 3446435476_Pytho

206 20 Tests und Fehler

if n == 20:a = 42

return a

Die Ausgabe eines Testlaufs sieht nun wie folgt aus:

$ python3 fibonacci_unittest.pyblabalF======================================================================FAIL: testCalculation (__main__.FibonacciTest)----------------------------------------------------------------------Traceback (most recent call last):File "fibonacci_unittest.py", line 12, in testCalculationself.assertEqual(fib(20), 6765)

AssertionError: 42 != 6765

----------------------------------------------------------------------Ran 1 test in 0.000s

FAILED (failures=1)

Jetzt wurden aber auch die folgenden Anweisungen durchgeführt, allerdings generiertensie keine Ausnahme, da ihre Ergebnisse ja korrekt sind:

self.assertEqual(fib(0), 0)self.assertEqual(fib(1), 1)self.assertEqual(fib(5), 5)

20.7 Methoden der Klasse TestCaseWir wollen nun näher auf die Klasse TestCase eingehen. Wir stellen dazu einige wichti-ge Methoden dieser Klasse vor. Zunächst stellen wir die beiden Hook-Methoden10 setUp()und tearDown() vor.

Wir erweitern unser voriges Beispiel um eine setUp- und eine tearDown-Methode:

import unittestfrom fibonacci import fib

class FibonacciTest(unittest.TestCase):

def setUp(self):

10 Ein Hook (auch Einschubmethode) bezeichnet eine Schnittstelle, mit der fremder Programmcode in einebestehende Anwendung integriert werden kann. Dadurch kann die Anwendung erweitert oder ihr Ablaufverändert werden. In der objektorientierten Programmierung kann dies durch Vererbung geschehen. EineKlasse stellt leere Methoden – also Methoden, die nichts tun, wenn man sie aufruft – zur Verfügung, diedann in Unterklassen von Anwendern der Klasse nach eigenem Bedarf implementiert werden können.

Page 219: 3446435476_Pytho

20.7 Methoden der Klasse TestCase 207

self.fib_elems = ( (0,0), (1,1), (2,1), (3,2), (4,3), (5,5) )print ("setUp executed!")

def testCalculation(self):for (i,val) in self.fib_elems:

self.assertEqual(fib(i), val)

def tearDown(self):# Objekte können gelöscht oder geändert werden# in diesem Fall macht es jedoch wenig Sinn:self.fib_elems = Noneprint ("tearDown executed!")

if __name__ == "__main__":unittest.main()

Ein Aufruf führt zu folgendem Ergebnis:

$ python3 fibonacci_unittest2.pysetUp executed!tearDown executed!.----------------------------------------------------------------------Ran 1 test in 0.000s

OK

Die meisten der TestCase-Methoden verfügen über einen optionalen Parameter „msg”. Mit„msg” kann man eine zusätzliche Beschreibung für einen Fehler ausgeben.

TABELLE 20.1 Methoden der Klasse TestCase

Methode BedeutungsetUp() Bei der Methode setUp handelt es sich um eine

sogenannte Hook-Methode. Sie wird vor jedem Aufrufder implementierten Testmethoden aufgerufen. Wird inder Methode setUp eine Ausnahme generiert, so wirddiese auch als Error in der Testausgabe ausgegeben.Selbstverständlich wird auch bei einer Ausnahme imsetUp-Code der Test abgebrochen.

tearDown() Die Methode tearDown wird nach dem Aufruf einerTestmethode gestartet. Ebenso wie bei setUp gilt, dassim Code von tearDown generierte Ausnahmen auch inder Testausgabe ausgegeben werden.

assertEqual(self, first, second,msg=None)

Der Test schlägt fehl, wenn die Parameter „first” und„second” nicht gleich sind. Dabei ist Gleichheit im Sinnevon „==” gemeint, also Wertegleichheit und nicht nurreine Objektgleichheit.

(Fortsetzung nächste Seite)

Page 220: 3446435476_Pytho

208 20 Tests und Fehler

TABELLE 20.1 Methoden der Klasse TestCase (Fortsetzung)

Methode BedeutungassertAlmostEqual(self, first,second, places=None, msg=None,delta=None)

Diese Methode schlägt fehl, wenn die Differenz derbeiden Parameter „first” und „second” gleich 0 ist,nachdem man sie vor dem Vergleich auf „places”Nachkommastellen gerundet hatte. Der Default-Wert für„places” ist 7.

assertCountEqual(self, first,second, msg=None)

Die Parameter „first” und „second” müssen hierbeisequentielle Datentypen sein. Es muss folgendes gelten:Alle Elemente müssen genauso oft in „first” wie in„second” vorkommen.Beispiel:[0, 1, 1] und [1, 0, 1] gelten in obigem Sinne als gleich,weil die 0 und die 1 jeweils gleich oft vorkommen.[0, 0, 1] und [0, 1] sind verschieden, weil die 0 in derersten Liste zweimal vorkommt und in der zweiten Listenur einmal.

assertAlmostEqual(self, first,second, places=None, msg=None,delta=None)

Diese Methode schlägt fehl, wenn die Differenz derbeiden Parameter „first” und „second” gleich 0 ist,nachdem man sie vor dem Vergleich auf „places”Nachkommastellen gerundet hatte. Der Default-Wert für„places” ist 7.

assertCountEqual(self, first,second, msg=None)

Die Parameter „first” und „second” müssen hierbeisequentielle Datentypen sein. Es muss folgendes gelten:Alle Elemente müssen genauso oft in „first” wie in„second” vorkommen.Beispiel:[0, 1, 1] und [1, 0, 1] gelten in obigem Sinne als gleich,weil die 0 und die 1 jeweils gleich oft vorkommen.[0, 0, 1] und [0, 1] sind verschieden, weil die 0 in derersten Liste zweimal vorkommt und in der zweiten Listenur einmal.

assertDictEqual(self, d1, d2,msg=None)

Betrachtet die beiden Argumente als Dictionaries undprüft auf Gleichheit.

assertTrue(self, expr, msg=None) Prüft, ob der Ausdruck „expr” True ist.assertGreater(self, a, b,msg=None)

Prüft, ob a > b gilt.

assertGreaterEqual(self, a, b,msg=None)

Prüft, ob a >= b gilt.

assertFalse(self, expr, msg=None) Prüft, ob der Ausdruck „expr” True ist.assertLess(self, a, b, msg=None) Prüft, ob a < b gilt.assertLessEqual(self, a, b,msg=None)

Prüft, ob a <= b gilt.

assertIn(self, member, container,msg=None)

Prüft, ob a in b gilt.

assertIs(self, expr1, expr2,msg=None)

Prüft, ob a is b gilt.

Page 221: 3446435476_Pytho

20.8 Aufgaben 209

TABELLE 20.1 Methoden der Klasse TestCase (Fortsetzung)

Methode BedeutungassertIsInstance(self, obj, cls,msg=None)

Prüft, ob isinstance(obj, cls) gilt.

assertIsNone(self, obj,msg=None)

Prüft, ob obj is None gilt.

assertIsNot(self, expr1, expr2,msg=None)

Prüft, ob a is not b gilt.

assertIsNotNone(self, obj,msg=None)

Prüft, ob obj nicht None ist.

assertItemsEqual(self,expected_seq, actual_seq,msg=None)

Auf zwei sequentiellen Datenobjekten wird eineGleichheit über die Anzahl der Elemente geprüft, d.h. eswird geprüft, ob jedes Element in jeder Liste gleich häufigvorkommt.Beispiel: [0, 1, 1] und [1, 0, 1] gelten als gleich. [0, 0, 1]and [0, 1] gelten nicht als gleich, da die Anzahl derNullen in beiden Listen variiert.

assertListEqual(self, list1, list2,msg=None)

Listen werden auf Gleichheit geprüft.

assertMultiLineEqual(self, first,second, msg=None)

Mehrzeilige Strings werden auf Gleichheit geprüft.

assertNotRegexpMatches(self,text, unexpected_regexp,msg=None)

Schlägt fehl, wenn der Text „text” den regulären Ausdruckunexpected_regexp matched.

assertTupleEqual(self, tuple1,tuple2, msg=None)

Analog zu assertListEqual

20.8 Aufgaben

1. Aufgabe:Betrachten Sie folgendes Beispiel für einen doctest. Erkennen Sie ein Problem?

import doctest

def fib(n):"""Die Fibonacci-Zahl für die n-teGeneration wird iterativ berechnet

>>> fib(0)0>>> fib(1)1>>> fib(10)55

Page 222: 3446435476_Pytho

210 20 Tests und Fehler

>>> fib(40)102334155>>>

"""if n == 0:

return 0elif n == 1:

return 1else:

return fib(n-1) + fib(n-2)

if __name__ == "__main__":doctest.testmod()

Lösung: Lösungen zu Kapitel 20 (Tests und Fehler), Seite 405

Page 223: 3446435476_Pytho

21 System-programmierung

21.1 SystemprogrammierungUnter Systemprogrammierung versteht man die Programmierung von Software-Kompo-nenten, die Teil des Betriebssystems sind oder die eng mit dem Betriebssystem bzw. mitder darunter liegenden Hardware kommunizieren müssen. Systemnahe Software wie bei-spielsweise das sys-Modul in Python fungiert als Abstraktionsschicht zwischen der An-wendung, dem Python-Programm, und dem Betriebssystem. Mit Hilfe dieser Abstrakti-onsschicht lassen sich Applikationen plattformunabhängig implementieren, auch wennsie direkt auf Betriebssystemfunktionalitäten zugreifen.

Python eignet sich besonders gut für die Systemprogrammierung. All das, was im Allge-meinen für Python gilt, zeichnet auch die Vorteile von Python in der systemnahen Pro-grammierung aus:

■ Einfach und übersichtlich

■ Gute Strukturierbarkeit

■ hohe Flexibilität

21.2 Shell

BILD 21.1 Shells und Schalen

Shell ist ein Begriff, der viel benutzt unddoch oft unterschiedlich verstanden undauch missverstanden wird. Wie leicht zuerkennen ist, kommt der Name aus demEnglischen. Shell bedeutet Schale undHülle, so wie zum Beispiel die Muschel-schale. Die Funktion einer Muschelscha-le besteht in erster Linie im Schutz derMuschel. Die Muschelschale trennt undschützt das Innere vom Äußeren. So trenntauch die Shell den Kern des Betriebssys-

Page 224: 3446435476_Pytho

212 21 Systemprogrammierung

tems vom Shell-Benutzer. Die Shell gibt ihm robuste und einfach zu bedienende Funk-tionen an die Hand, damit er nicht die komplizierten und fehleranfälligen Betriebssystem-funktionen benutzen muss.

Generell unterscheidet man bei Betriebssystemen zwei Arten von Shells:

■ die Kommandozeile (CLI) und

■ die grafischen Benutzeroberflächen (GUI)

In den meisten Fällen wird der Begriff jedoch als Synonym für den Kommandozeileninter-preter (CLI) benutzt, so wie zum Beispiel die Bourne-Shell, C-Shell oder Bash unter Unix.

In unserem Fall gibt es zwei Gründe, von Shells zu sprechen:

■ Die Module shutil, os, sys, die wir in diesem Kapitel besprechen, kennen wir aus denShells der Betriebssysteme, also beispielsweise von der Bash-Shell von Unix/Linux. Dortsind

■ Andererseits bilden die genannten Module ihrerseits im oben genannten Sinne Shells.Sie sind gewissermaßen eine Abstraktionsschicht über den Betriebssystemfunktionen,um Python betriebssystemunabhängig zu machen.

21.3 os-ModulDas os-Modul ist das wichtigste Modul zur Interaktion mit dem Betriebssystem und er-möglicht durch abstrakte Methoden ein plattformunabhängiges Programmieren. Gleich-zeitig ist es auch mit Funktionen wie os.system() oder der exec*()-Funktionsfamile mög-lich, betriebssystemabhängige Programmteile einzubauen. Das os-Modul bietet viele un-terschiedliche Methoden wie beispielsweise zum Zugriff auf das Dateisystem.

Mit Hilfe des os-Modules kann man beispielsweise die Zugriffsrechte einer Datei abfra-gen. Allerdings geht dies nicht direkt. Man ruft zuerst die Funktion stat aus dem os-Modulauf. Diese liefert ein 10-Tupel mit verschiedenen Datei-Metadaten zurück. Die erste Kom-ponente liefert die Zugriffsmaske als Integer. Wenn wir diese in eine Oktal-Zahl wandeln,erhalten wir die unter Linux gewohnte Sichtweise, d.h. „644” in der letzten Zeile des fol-genden Beispiels:

>>> import os>>> info = os.stat("abc.py")>>> type(info)<class 'posix.stat_result'>>>> infoposix.stat_result(st_mode=33188, st_ino=63253, st_dev=2055, st_nlink=1,

st_uid=1000, st_gid=1000, st_size=17, st_atime=1361559201, st_mtime=1361455357, st_ctime=1361455357)

>>> info.st_ino63253>>> info.st_ino is info[1]True>>>

Page 225: 3446435476_Pytho

21.3 os-Modul 213

21.3.1 Vorbemerkungen

Warnung:

Wir hatten bereits früher erwähnt, dass es sehr schlechter Stil ist, Module mit Sternchenzu importieren, da gegebenenfalls Variablen und Funktionen überschrieben werden kön-nen. Importiert man os mittel „from os import *”, wird die eingebaute Funktion open()durch os.open() überschrieben, die ein komplett anderes Verhalten hat. So führt dann bei-spielsweise die folgende, ansonsten korrekte Anweisung zum Öffnen einer Datei zu einerFehlermeldung:

>>> from os import *>>> fh = open("abc.py")Traceback (most recent call last):File "<stdin>", line 1, in <module>

TypeError: open() takes at least 2 arguments (1 given)>>>

Für die im Folgenden beschriebenen Funktionen des os-Moduls gilt:

■ Alle Funktionen, die einen Pfad- oder Dateinamen als Argument haben, akzeptieren so-wohl Byte- als auch String-Objekte. Das Ergebnis ist dann vom gleichen Typ, falls wiederein Pfad- oder Dateinamen zurückgeliefert wird.

■ Falls nicht anders vermerkt, sind alle Funktionen, die unter Unix laufen, auch unter demMac OS X lauffähig.

■ Alle Funktionen dieses Moduls erzeugen einen OSError, falls ungültige oder nicht ver-fügbare Datei oder Pfadnamen oder andere Argumente, die nicht vom Betriebssystemakzeptiert werden, übergeben werden.

21.3.2 Umgebungsvariablen

os.environ ist ein Dictionary, das alle Umgebungsvariablen enthält. Wir können wie üblichdarauf zugreifen. Beispielsweise liefert uns os.environ["USER"] den aktuellen Namendes Benutzers:

>>> os.environ["USER"]'bernd'>>>

Man kann dies natürlich auch über die im Folgenden beschriebene Funktion getenv errei-chen:

■ getenv(key, default=None)Liefert den Wert einer Umgebungsvariablen zurück. None wird zurückgeliefert, wenn dieVariable nicht existiert. Mit dem optionalen zweiten Parameter kann man einen alterna-tiven Default-Wert setzen.

>>> os.getenv("USER")'bernd'

Page 226: 3446435476_Pytho

214 21 Systemprogrammierung

■ getenvb( key, default=None)Liefert den Wert einer Umgebungsvariablen zurück. None wird zurückgeliefert, wenn dieVariable nicht existiert. Mit dem optionalen zweiten Parameter kann man einen alterna-tiven Default-Wert setzen.

„key”, „default” und das Ergebnis sind Byte-Strings!

>>> b = bytes("USER", 'utf-8')>>> os.getenvb(b)b'bernd'>>>

■ putenv(key, value)In der Help-Funktion steht hier nur:

putenv(...)putenv(key, value)

Change or add an environment variable.

Man könnte nun erwarten, dass man eine Environment-Variable erzeugen und verän-dern kann und dies dann auch im aktuellen Prozess sichtbar ist. Dies ist aber nicht so,wie wir im Folgenden sehen:

>>> import os>>> os.putenv("ABC","42")>>> os.getenv("ABC")>>> print(os.getenv("ABC"))None>>>

Die obige Umgebungsvariable ist erst bekannt, wenn wir einen spawn durchführen oderein Unterprozess gestartet wird. Wir starten im folgenden Beispiel eine Subshell und las-sen uns dort mittels echo den Wert der Umgebungsvariablen ABC ausgeben. Wir sehen,dass wir jetzt den Wert 42 erhalten:

>>> command = """ echo $ABC """>>> os.popen(command,"w")<os._wrap_close object at 0x8b9e7ac>>>> 42

>>>

Es stellt sich natürlich die Frage, wie können wir eine Umgebungsvariable im laufendenProzess ändern bzw. neu erzeugen. Wir erreichen dies, indem wir direkt das Dictionaryos.environ manipulieren:

>>> os.environ["ABC"] = "42">>> os.getenv("ABC")'42'>>> os.environ["ABC"]'42'>>>

Page 227: 3446435476_Pytho

21.3 os-Modul 215

■ supports_bytes_environ( )Wenn dieses Attribut den Wert True hat, existiert auch ein Dictionary os.environb, wel-ches die Umgebungsvariablen als Bytestrings enthält.

■ unsetenv( var )Eine Umgebungsvariable wird entfernt. Hier gilt analog das zu putenv Gesagte. Die Um-gebungsvariable existiert weiterhin im aktuellen Prozess, wird aber nicht mehr an Unter-prozesse weitergegeben:

>>> import os>>> os.environ["ABC"] = "42">>> os.getenv("ABC")'42'>>> os.unsetenv("ABC")>>> os.getenv("ABC")'42'>>> command = """ echo $ABC """>>> os.popen(command,"w")<os._wrap_close object at 0x8bfd10c>>>>

21.3.3 Dateiverarbeitung auf niedrigerer Ebene

Im Kapitel 10 (Dateien lesen und schreiben) haben wir gesehen, mit welcher Leichtigkeitund Eleganz sich Dateien mit Python lesen und schreiben lassen. Es stellt sich deshalb so-fort die Frage, weslhalb das os-Modul eigene Funktionen und Methoden zur Dateiverarbei-tung bereitstellt. In den meisten Fällen genügen die eingebauten Python-Funktionalitätenvoll und ganz, und der Großteil der Python-Benutzer wird nie die os-Funktionen benöti-gen. Aber die Dateiverarbeitung von os geht mehr ins Detail, d.h. sie gibt dem Benutzermehr Möglichkeiten, direkt auf Byteebene zu arbeiten.

■ writeZunächst wollen wir zeigen, wie man die os.write-Funktion auch im Zusammenspiel miteiner von der eingebauten open-Funktion zum Schreiben geöffneten Datei nutzen kann:

import osfh = open("nur_text.txt","w")fh.write("Ein wenig Text\n")fh.flush()

fd = fh.fileno()

os.write(fd, b"Noch etwas Text")fh.close()

Startet man obiges Programm,1 erhält man eine Datei namens „nur_text.txt”. Den Inhaltkönnen wir uns mittels cat2 oder mit irgendeinem Editor anschauen:

1 Das Programm befindet sich in unserer Beispielsammlung unter dem Name „os_write.py”.2 type unter Windows

Page 228: 3446435476_Pytho

216 21 Systemprogrammierung

bernd@saturn:~/bodenseo/python/tmp$ python3 os_write.pybernd@saturn:~/bodenseo/python/tmp$ cat nur_text.txtEin wenig TextNoch etwas Textbernd@saturn:~/bodenseo/python/tmp$

Mit os.write lassen sich nur Bytestrings schreiben. Lässt man das „b” vor dem Ausgabe-string in der Zeile „os.write(fd, b"Noch etwas Text")” weg, führt dies deshalb zu folgenderFehlermeldung:

bernd@saturn:~/bodenseo/python/tmp$ python3 os_write.pyTraceback (most recent call last):File "os_write.py", line 8, in <module>os.write(fd, "Noch etwas Text\n")

TypeError: 'str' does not support the buffer interfacebernd@saturn:~/bodenseo/python/tmp$

Sehr wichtig ist es auch, die Methode flush auf das Dateihandle anzuwenden, da ansons-ten die Ausgaben von os.write an die falsche Stelle geschrieben werden. Im Folgendensehen wir den Inhalt der Datei „nur_text.txt” nach einem Programmlauf mit auskom-mentierter flush-Anweisung. Die beiden Zeilen der Datei sind vertauscht:

bernd@saturn:~/bodenseo/python/tmp$ cat nur_text.txtNoch etwas TextEin wenig Textbernd@saturn:~/bodenseo/python/tmp$

■ open(file, flags[, mode])Mit dieser Funktion wird eine Datei „file” zur Dateibearbeitung auf niedrigem Level ge-öffnet. Die Art der Bearbeitung ergibt sich sich aus den „flags” und gegebenenfalls demoptionalen „mode”-Parameter.

– O_RDONLYNumerischer Wert: 0, gesetztes Bit: keinesDatei wird nur zum Lesen geöffnet.

– O_WRONLYNumerischer Wert: 1, gesetztes Bit: 0Datei wird nur zum Schreiben geöffnet.

– O_RDWRNumerischer Wert: 2, gesetztes Bit: 1Datei wird zum Lesen und Schreiben geöffnet.

– O_CREATNumerischer Wert: 64, gesetztes Bit: 6Datei wird erzeugt, falls sie noch nicht existiert.

– O_EXCLNumerischer Wert: 128, gesetztes Bit: 7Fehlermeldung, falls ein Erzeugen der Datei nicht erfolgreich war, weil die Datei be-reits existiert.

Page 229: 3446435476_Pytho

21.3 os-Modul 217

– O_NOCTTYNumerischer Wert: 256, gesetztes Bit: 8Falls es sich bei der Datei um ein Terminal-Device handelt, soll es nicht zum Kontroll-terminal für den Prozess werden.

– O_TRUNCNumerischer Wert: 512, gesetztes Bit: 9Falls die Datei existiert, eine reguläre Datei ist und die Datei erfolgreich mittelsO_RDWR oder O_WRONLY geöffnet wurde, wird ihre Länge auf 0 abgeschnitten.Die Dateirechte und die Eigentumsverhältnisse bleiben unverändert. Die Funktionsollte keine Effekte auf FIFO-Spezialdateien und auf Termindevice-Dateien haben.Effekte auf andere Dateitypen ist implementierungsabhängig. Was passiert, wennman O_TRUNC zusammen mit O_RDONLY benutzt, ist undefiniert.

– O_APPENDNumerischer Wert: 1024, gesetztes Bit: 10Mit jedem write wird ans Ende der Datei geschrieben.

– O_NDELAY und O_NONBLOCKNumerischer Wert: 2048, gesetztes Bit: 11Diese Flags beeinflussen aufeinanderfolgende reads und writes. Wenn sowohl O_NDELAYund O_NONBLOCK gesetzt sind, hat O_NONBLOCK den Vorrang.Falls O_NDELAY oder O_NONBLOCK gesetzt ist, wird ein open für read-only ohneVerzögerung ausgeführt. Ein open for write-only führt zu einem Fehler, falls die Dateinoch von keinem anderen Prozess zum Lesen geöffnet ist.Falls O_NDELAY und O_NONBLOCK nicht gesetzt sind gilt: Ein open für ein read-onlywird solange geblockt, bis ein Prozess die Datei zum Schreiben öffnet. Ein open fürein write-only wird solange geblockt, bis ein Prozess die Datei zum Lesen öffnet.

– O_DSYNCNumerischer Wert: 4096, gesetztes Bit: 12

– O_ASYNCNumerischer Wert: 8192, gesetztes Bit: 13

– O_DIRECTNumerischer Wert: 16384, gesetztes Bit: 14Cache-Effekte sollen eliminiert bzw. reduziert werden.

– O_LARGEFILENumerischer Wert: 32768, gesetztes Bit: 15

– O_DIRECTORYNumerischer Wert: 65536, gesetztes Bit: 16

– O_NOFOLLOWNumerischer Wert: 131072, gesetztes Bit: 17Symbolischen Links wird nicht gefolgt.

– O_NOATIMENumerischer Wert: 262144, gesetztes Bit: 18

– O_RSYNCNumerischer Wert: 1052672, gesetztes Bit: 20

– O_SYNCNumerischer Wert: 1052672, gesetztes Bit: 20

Page 230: 3446435476_Pytho

218 21 Systemprogrammierung

Das folgende Skript öffnet eine Datei für den Write-only-Zugriff „os.O_WRONLY”. os.O_CREATstellt sicher, dass die Datei „blabla.txt” beim Öffnen erzeugt wird, falls sie noch nichtexistiert.

import os

fd = os.open( "blabla.txt", os.O_WRONLY|os.O_CREAT )

os.write(fd, b"Etwas Text\n")os.write(fd, b"und noch eine Zeile\n")

os.close(fd)

Falls blabla.txt noch nicht existiert und man kein „os.O_CREAT” angegeben hat, also fd =os.open( "blabla.txt", os.O_WRONLY ), dann erfolgt eine Fehlermeldung, wenn man dasSkript startet, wie man im Folgenden sieht:

bernd@saturn:~/bodenseo/python/tmp$ python3 os_write.pyTraceback (most recent call last):File "os_write.py", line 3, in <module>fd = os.open( "blabla.txt", os.O_WRONLY )

OSError: [Errno 2] No such file or directory: 'blabla.txt'bernd@saturn:~/bodenseo/python/tmp$

■ read(fd,n)Die Funktion read liest höchstens „n” Bytes vom Dateideskriptor „fd”. Sie liefert einenByte-String zurück, der die gelesenen Bytes enthält.

Falls das Dateiende der durch „fd” referenzierten Datei erreicht ist, wird ein leerer Stringzurückgeliefert.

Im Folgenden lesen wir die ersten 80 Bytes des Romans „Ulysses” und lassen uns denzurückgelieferten Typ zusätzlich ausgeben:

import os, sys

fd = os.open("ulysses.txt",os.O_RDWR)

res = os.read(fd,80)print(res)

print("type of returned object: ", type(res))

os.close(fd)

Das Skript liefert folgendes Ergebnis:

bernd@saturn:~/bodenseo/python/beispiele$ python3 os_read.pyb'ULYSSES\r\n\r\nby James Joyce\r\n\r\n\r\n\r\n\r\n-- I --\r\n\r\

nStately, plump Buck Mulligan came 'type of returned object: <class 'bytes'>bernd@saturn:~/bodenseo/python/beispiele$

Page 231: 3446435476_Pytho

21.3 os-Modul 219

■ close(fd)Die mit dem Dateideskriptor „fd” referenzierte Datei wird geschlossen. Die Funktion lie-fert keinen return-Wert zurück.

■ openpty( )Die Funktion openpty() öffnet ein Pseudoterminal-Paar und liefert ein Deskriptorenpaar(master, slave) für pty und tty zurück.

Im folgenden Skript sehen wir die Funktionsweise dieser Funktion:

import os

master_pty,slave_tty = os.openpty()

terminal_name = os.ttyname(slave_tty)

print(master_pty, slave_tty, terminal_name)

Obiges Programm liefert folgende Ausgaben:

bernd@saturn:~/bodenseo/python/tmp$ python3 openpty.py3 4 /dev/pts/3bernd@saturn:~/bodenseo/python/tmp$

■ lseek(fd, pos, how)Die Funktion lseek positioniert die aktuelle Position des Dateideskriptors auf die gege-bene Position „pos” in Abhängigkeit von „how”.

Mittels lseek haben wir eine index-sequentielle Zugriffsmethode.

Parameter:

– fdDer Dateideskriptor, der bearbeitet wird.

– posInteger-Wert, der die Position innerhalb der Datei mit dem Dateideskriptor „fd” an-gibt. Die Position hängt vom Wert von „how” ab. Hat „how” den Wert os.SEEK_SEToder 0, berechnet sich der Wert von pos vom Beginn der Datei. os.SEEK_CUR oder 1,dann berechnet sich „pos” relativ zur aktuellen Position; os.SEEK_END oder 2 bedeu-tet in Relation zum Dateiende.

– howDieser Parameter bestimmt den Referenzpunkt innerhalb der Datei:os.SEEK_SET oder 0 steht für den Anfang der Datei, os.SEEK_CUR oder 1 bezeichnetdie aktuelle Position, und os.SEEK_END oder 2 kennzeichnet das Ende der Datei.

import os, sys

fd = os.open("customer_numbers.txt",os.O_RDWR)

for customer in [2,1,4,0,3]:os.lseek(fd, customer * 7, os.SEEK_SET)res = os.read(fd,6)print("customer number " + str(customer + 1) +": " + str(res))

Page 232: 3446435476_Pytho

220 21 Systemprogrammierung

print("\nEine Iteration über die Kundennummern:")# iteration über die Kundennummern:os.lseek(fd, 0, 0)for customer in range(1,6):

res = os.read(fd,6)os.lseek(fd, 1, os.SEEK_CUR) # skip the newline characterprint("customer number " + str(customer) +": " + str(res))

print("\n... und nun von hinten:")for customer in range(1,6):

os.lseek(fd, -7 * customer, os.SEEK_END)res = os.read(fd,6)print(str(customer) + " from last: " + str(res))

os.close(fd)

In der ersten for-Schleife des obigen Programms können wir sehen, wie wir auf beliebi-ge Kundennummern zugreifen können, ohne sie sequentiell durchlaufen zu müssen. Inder zweiten for-Schleife benutzen wir os.seek mit dem how-Parameter „os.SEEK_CUR”,d.h. nachdem wir eine Kundennummer eingelesen haben, überspringen wir das nächsteByte mit dem os.seek-Kommando. In der letzten for-Schleife durchlaufen wir die Kun-dennummern von hinten. Die Ausgabe des Programms sieht wie folgt aus:

bernd@saturn:~/bodenseo/python/beispiele$ python3 os_seek.pycustomer number 3: b'ZH8765'customer number 2: b'AB9876'customer number 5: b'QW5185'customer number 1: b'JK3454'customer number 4: b'KL9878'

Eine Iteration über die Kundennummern:customer number 1: b'JK3454'customer number 2: b'AB9876'customer number 3: b'ZH8765'customer number 4: b'KL9878'customer number 5: b'QW5185'

... und nun von hinten:1 from last: b'QW5185'2 from last: b'KL9878'3 from last: b'ZH8765'4 from last: b'AB9876'5 from last: b'JK3454'bernd@saturn:~/bodenseo/python/beispiele$

■ dup()Liefert die Kopie eines Datei-Handles3 zurück.

3 engl. file descriptor

Page 233: 3446435476_Pytho

21.3 os-Modul 221

BILD 21.2 Systematik der exec-Familie

21.3.4 Die exec-„Familie”

Die Funktionen execl, execlp, execle, execv, execvp und execvpe des os-Moduls entspre-chen den gleichnamigen UNIX/Linux-Funktionen. Jede dieser Funktionen aus der exec-Familie ersetzt das aktuelle Prozessabbild durch einen neuen Prozess. Das neue Prozessab-bild wird aus einer einer regulären und ausführbaren Datei konstruiert. Man nennt dieseDatei „Prozessabbilddatei”. Die Prozessabbilddatei ist entweder ein ausführbares Objektoder eine Datendatei, die von einem Interpreter ausgeführt werden kann. Führt man einedieser Funktionen aus, gibt es keine Rückkehr (return) nach einem erfolgreichen Aufruf,denn der aufgerufene Prozess überlagert das aktuelle Prozessabbild.

Schaut man sich die exec*-Funktionen in obigem Schaubild an, fällt auf, dass alle Funktio-nen exec als Präfix haben. Dann folgen bis zu drei der folgenden Buchstaben: „e”, „l”, „p”oder „v”

Diese Buchstaben haben folgende Bedeutung:

■ eEin Dictionary mit Umgebungsvariablen wird an den auszuführenden Prozess überge-ben.

■ vKommandozeilenargumente werden als Liste oder Tupel an das Skript übergeben.

■ lKommandozeilenargumente für das aufzurufende Skript werden direkt und einzeln imexec*-Aufruf angegeben.

■ pDie $PATH-Umgebungsvariable wird benutzt, um das Skript, dessen Namen als Parame-ter als String übergeben wurde, zu finden.

Fehler bei einer exec*-Funktion werden als OSError generiert.

Zur Beschreibung der folgenden exec-Befehle benutzen wir ein Bash-Skript zum Testen.Wir nehmen an, dass dieses Skript unter dem Namen „test.sh” im Verzeichnis „/home/-bernd/bin2” steht.

Page 234: 3446435476_Pytho

222 21 Systemprogrammierung

Das Bash-Skript „test.sh”:

#!/bin/bashscript_name=$0arg1=$1current=`pwd`echo $script_name, $arg1echo "XYZ: "$XYZecho "PATH: "$PATHecho "current directory: $current"

Achtung:

test.sh muss ausführbar sein, und bin2 sollte, damit die folgenden Beispiele funktionieren,nicht in $PATH enthalten sein.

■ execvp( scriptname, args )Wie bereits eingangs erklärt, bedeutet das „p” im Namen, dass der Skriptname „scriptna-me” in den Pfaden der Systemvariablen $PATH gesucht wird. Das „v” bedeutet, dass wirKommandozeilenargumente mit dem Parameter „args” übergeben können, und zwar inForm einer Liste oder eines Tupels.

Wir starten unsere interaktive Python-Shell in einem Verzeichnis, was nicht gleich dembin2-Verzeichnis ist. Außerdem ist „/home/bernd/bin2” nicht Teil unserer Umgebungs-variablen „$PATH”:

Dann passiert Folgendes, wenn wir versuchen unser Test-Shellskript mit execvp aufzu-rufen:

>>> import os>>> args = ("test","abc")>>> os.execvp("test.sh", args)Traceback (most recent call last):File "<stdin>", line 1, in <module>File "/usr/lib/python3.2/os.py", line 353, in execvp_execvpe(file, args)

File "/usr/lib/python3.2/os.py", line 398, in _execvperaise last_exc.with_traceback(tb)

File "/usr/lib/python3.2/os.py", line 388, in _execvpeexec_func(fullname, *argrest)

OSError: [Errno 2] No such file or directory>>>

execvp konnte die Skriptdatei „test.sh” nicht finden, da sie sich im Verzeichnis „/home/-bernd/bin2” befindet und dieser Pfad nicht Teil von $PATH ist.

Rufen wir unsere interaktive Python-Shell direkt im Verzeichnis „/home/bernd/bin2”auf, funktioniert alles:

bernd@saturn:~/bin2$ python3Python 3.2.3 (default, Oct 19 2012, 19:53:57)[GCC 4.7.2] on linux2Type "help", "copyright", "credits" or "license" for more information.>>> import os>>> args = ("test", "abc")

Page 235: 3446435476_Pytho

21.3 os-Modul 223

>>> os.execvp("test.sh", args)./test.sh, abcXYZ:PATH: /home/bernd/perl5/bin:/home/bernd/bin:/usr/lib/lightdm/lightdm:/

usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/home/writing/etrusker_ag/bin:/home/bernd/bin:.::bin/bck_scripts/:bin/bck_scripts/

current directory: /home/bernd/bin2bernd@saturn:~/bin2$

Alternativ kann man auch den Pfad „/home/bernd/bin2” in die $PATH-Umgebungsva-riable mit aufnehmen. Dann wird test.sh immer gefunden – egal, wo wir uns aufhalten,also auch zum Beispiel im Home-Verzeichnis:

bernd@saturn:~$ PATH=$PATH:/home/bernd/bin2bernd@saturn:~$ python3Python 3.2.3 (default, Oct 19 2012, 19:53:57)[GCC 4.7.2] on linux2Type "help", "copyright", "credits" or "license" for more information.>>> import os>>> args = ("test", "abc")>>> os.execvp("test.sh", args)/home/bernd/bin2/test.sh, abcXYZ:PATH: /home/bernd/perl5/bin:/home/bernd/bin:/usr/lib/lightdm/lightdm:/

usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/home/writing/etrusker_ag/bin:/home/bernd/bin:.::/home/bernd/bin2

current directory: /home/berndbernd@saturn:~$

■ execvpe(scriptname, args, env )Wählen wir die execvpe-Variante unserer exec-Familie, dann können wir zusätzlich nochneue Umgebungsvariablen beim Aufruf mit übergeben. Dazu schreiben wir die neuenUmgebungsvariablen in ein Dictionary, in unserem Fall „env”. „filename” ist der Namedes auszuführenden Skripts, und „args” eine Liste oder ein Tupel mit den an „filename”zu übergebenden Argumenten.

Das Verzeichnis „/home/bernd/bin2” befindet sich zwar noch in unserer PATH-Umge-bungsvariablen, aber beim Aufruf werden nur die in env definierten Umgebungsvaria-blen an das neue Skript übergeben. Deshalb funktioniert der folgende Aufruf nur, wennwir uns im bin2-Verzeichnis befinden:

>>> import os>>> env = {"ABC":"Von Anfang", "KLM":"bis zum ", "XYZ":"Ende"}>>> args = ("erstes","zweites")>>> os.execvpe("test.sh", args, env)test.sh, zweitesXYZ: EndePATH: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bincurrent directory: /home/bernd/bin2

Page 236: 3446435476_Pytho

224 21 Systemprogrammierung

Wir wollen nun PATH und die anderen alten Umgebungsvariablen auch an das Skriptübergeben. Wir können dies mit der update-Methode erreichen. os.environ enthält al-le definierten Umgebungsvariablen, der aktuellen Shell. Dann können wir unser Skriptwieder von allen Orten aufrufen, sofern noch bin2 im PATH enthalten ist:

>>> import os>>> env = {"ABC":"Von Anfang", "KLM":"bis zum ", "XYZ":"Ende"}>>> env.update(os.environ)>>> args = ("erstes", "zweites")>>> os.execvpe("test.sh", args, env)/home/bernd/bin2/test.sh, zweitesXYZ: EndePATH: /home/bernd/perl5/bin:/home/bernd/bin:/usr/lib/lightdm/lightdm:/

usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/home/writing/etrusker_ag/bin:/home/bernd/bin:.::bin/bck_scripts/:/home/bernd/bin2

current directory: /home/bernd/bodenseo/pythonbernd@saturn:~/bodenseo/python$

■ execv( scriptname, args)Wie „execvp”, aber der Skriptname „scriptname” muss den kompletten Pfad des Skriptsenthalten, wenn das Skript sich nicht im aktuellen Arbeitsverzeichnis befindet, da derName nicht in den Verzeichnissen von der Umgebungsvariablen PATH gesucht wird:

>>> import os>>> args = ("arg1", "arg2")>>> os.execv("test.sh", args)Traceback (most recent call last):File "<stdin>", line 1, in <module>

OSError: [Errno 2] No such file or directory>>> os.execv("/home/bernd/bin2/test.sh", args)/home/bernd/bin2/test.sh, arg2XYZ:PATH: /home/bernd/perl5/bin:/home/bernd/bin:/usr/lib/lightdm/lightdm:/

usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/home/writing/etrusker_ag/bin:/home/bernd/bin:.::bin/bck_scripts/:/home/bernd/bin2

current directory: /home/bernd/bodenseo/pythonbernd@saturn:~/bodenseo/python$

■ execve(scriptname, args, env )Analog zu „execv”, allerdings wird bei dieser Variante noch ein Dictionary mit Umge-bungsvariablen mit übergeben:

>>> import os>>> args = ("arg1", "arg2")>>> env = {"ABC":"Van Anfang", "KLM":"bis zum ", "XYZ":"Ende"}>>> os.execve("/home/bernd/bin2/test.sh", args, env)/home/bernd/bin2/test.sh, arg2XYZ: EndePATH: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bincurrent directory: /home/data/bodenseo/pythonbernd@saturn:~/bodenseo/python$

Page 237: 3446435476_Pytho

21.3 os-Modul 225

■ execlp( scriptname, arg0, arg1, ...)Die Funktion „execlp” unterscheidet sich von „execvpe” in der Art, wie die Argumenteübergeben werden. Bei „execvpe” hatten wir ein Tupel bzw. eine Liste mit den Parame-tern als Strings übergeben. Nun wird jeder Parameter direkt in „execlpe” durch Kommagetrennt als String angegeben, also arg0, arg1, ... arg0 entspricht dabei dem Namen desSkripts, wird aber in der Implementierung nicht genutzt. Der Skriptname wird stattdes-sen an „scriptname” als Name übergeben.

„scriptname” und „env” sind wie bereits in execvpe beschrieben.

Um das Verhalten dieser Funktion besser demonstrieren zu können, benutzen wirein Bash-Skript, das alle übergebenen Argumente ausdruckt. Das Bash-Skript „test_params.sh”:

#!/bin/bashscript_name=$0echo $script_namewhile test $# -gt 0do

echo $1shift

done

Nun rufen wir das Skript „test_params.sh” mit „execlpe” in der interaktiven Python-Shellauf:

>>> import os>>> os.execlp("test_params.sh", "test_params.sh", "a1","a2", "a3", "a4

")/home/bernd/bin2/test_params.sha1a2a3a4bernd@saturn:~/bodenseo/python$

Wir sehen, dass wir beim Aufruf den Namen des Skripts zweimal übergeben haben. arg0sollte also auch den Namen des Skripts enthalten, wird aber von exec ignoriert. Wir kön-nen stattdessen auch irgendetwas eingeben, und es hat keinen Einfluss auf das Skript:

>>> import os>>> os.execlp("test_params.sh", "unsinn.sh", "a1","a2", "a3", "a4")/home/bernd/bin2/test_params.sha1a2a3a4bernd@saturn:~/bodenseo/python$

■ execl( )Analog zu „execlp”. Allerdings müssen wir nun den kompletten Pfad angeben, damit dasSkript gefunden werden kann, denn die Informationen aus der Umgebungsvariablen$PATH werden in dieser exec*-Variante nicht verwendet:

Page 238: 3446435476_Pytho

226 21 Systemprogrammierung

>>> import os>>> os.execl("test_params.sh", "test_params.sh", "arg1", "arg2")Traceback (most recent call last):File "<stdin>", line 1, in <module>File "/usr/lib/python3.2/os.py", line 321, in execlexecv(file, args)

OSError: [Errno 2] No such file or directory>>> os.execl("/home/bernd/bin2/test_params.sh", "test_params.sh", "arg1

", "arg2")/home/bernd/bin2/test_params.sharg1arg2bernd@saturn:~/bodenseo/python$

■ execlp( scriptname, arg0, arg1, ... , env)Wie execlp, nur dass noch ein Dictionary mit Umgebungsvariablen übergeben wird. Sie-he dazu die Beschreibung der Funktion „execvpe”.

■ execle( )Analog zu execl, aber es wird noch zusätzlich ein Dictionary mit Umgebungsvariablenübergeben:

>>> import os>>> env = {"ABC":"Von Anfang", "KLM":"bis zum ", "XYZ":"Ende"}>>> env.update(os.environ)>>> os.execle("/home/bernd/bin2/test.sh", "test.sh", "arg1", "arg2",

env)/home/bernd/bin2/test.sh, arg1XYZ: EndePATH: /home/bernd/perl5/bin:/home/bernd/bin:/usr/lib/lightdm/lightdm:/

usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/home/writing/etrusker_ag/bin:/home/bernd/bin:.::bin/bck_scripts/:/home/bernd/bin2

current directory: /home/bernd/bodenseo/pythonbernd@saturn:~/bodenseo/python$

21.3.5 Weitere Funktionen im Überblick

■ abort( )Der Interpreter wird unmittelbar abgebrochen. Das englische Wort „abort” bedeutet „ab-brechen”, aber im Sinne von „fehlschlagen lassen”, „scheitern” und „vorzeitig beenden”.In der Help-Ausgabe zum os-Modul steht unter abort: „This ’dumps core’ or otherwi-se fails in the hardest way possible on the hosting operating system”. Das bedeutet also,dass der schlimmstmögliche Fehler unter dem jeweiligen Betriebssystem erzeugt wird.

Unter Linux bedeutet das zunächst einmal, dass man nach dem Abbruch die folgendeFehlermeldung erhält:

>>> os.abort()Aborted (core dumped)bernd@saturn:~/bodenseo/python$

Page 239: 3446435476_Pytho

21.3 os-Modul 227

Außerdem klappen zwei Fenster auf, die normalerweise nur bei ernsten Systemfehlernauftauchen:

■ access(path, mode)Die Funktion „os.access” dient zur Überprüfung, welche Rechte das laufende Python-Programm für den Pfad „path” hat. Mit dem optionalen Parameter „mode” kann eineBitmaske übergeben werden, welche die zu überprüfenden Rechte enthält.

Folgende Werte können einzeln oder mit dem bitweisen ODER kombiniert übergebenwerden:

os.F_OK : Prüft, ob der Pfad überhaupt existiert.

os.R_OK : Prüft, ob der Pfad gelesen werden darf.

os.W_OK : Prüft, ob der Pfad geschrieben werden darf.

os.X_OK : Prüft, ob der Pfad ausführbar ist.

os.access liefert True zurück, wenn alle für „mode” übergebenen Werte auf den Pfad zu-treffen. Gilt mindestens ein Zugriffsrecht nicht, wird False zurückgegeben.

■ chdir(path)Das Arbeitsverzeichnis wird auf durch die Stringvariable „path” bezeichnete Verzeichnisgeändert.

>>> os.getcwd()'/home/bernd'>>> os.chdir("/home/bernd/bodenseo/python")>>> os.getcwd()'/home/data/bodenseo/python'>>>

■ chmod( path, mode)Die Zugriffsrechte eines Pfads bzw. einer Datei werden entsprechend der Eingabemaske„mode” umgesetzt. „mode” muss als Oktalzahl eingegeben werden, d.h. eine Zahl mitPräfix 0o:

>>> permissions = oct(os.stat("abc.py")[0])>>> permissions'0o100644'>>> os.chmod("abc.py",0o644)>>> os.chmod("abc.py",0o755)

Page 240: 3446435476_Pytho

228 21 Systemprogrammierung

>>> permissions = oct(os.stat("abc.py")[0])>>> permissions'0o100755'>>>

■ chown(path, uid, gid)Die Funktion chown ändert den Besitzer und die Gruppen-ID entsprechend der Para-meter „uid” und „gid”. Möchte man einen Wert unverändert lassen, setzt man ihn auf-1 beim Aufruf. Das Skript benötigt allerdings Superuser-Rechte, um diese Änderungendurchführen zu können.

Ruft man chown mit ungenügenden Rechten auf, erhält man folgende Fehlermeldung:

>>> os.chown("abc.py",1001,-1)Traceback (most recent call last):File "<stdin>", line 1, in <module>

OSError: [Errno 1] Operation not permitted: 'abc.py'>>>

Im folgenden Beispiel starten wir die interaktive Python-Shell mit den entsprechendenSuperuser-Rechten:

bernd@saturn:~/bodenseo/python$ sudo python3[sudo] password for bernd:Python 3.2.3 (default, Oct 19 2012, 19:53:57)[GCC 4.7.2] on linux2Type "help", "copyright", "credits" or "license" for more information.>>> import os>>> os.chown("abc.py",1001,-1)>>>

■ chroot(path)Auch zum Ausführen dieser Funktion benötigt das Skript Superuser-Rechte. Das root-Verzeichnis des aktuellen Prozesses wird mit diesem Kommando auf den Pfad „path”gesetzt.

Im folgenden Beispiel setzen wir das root-Verzeichnis auf „/tmp”.

>>> os.chroot("/tmp")

■ extsepEin Attribut mit dem Wert des Separators für die Dateikennung4, also „.”

>>> os.extsep'.'>>>

■ fork( )siehe 22 (Forks)

■ forkpty( )siehe 22 (Forks)

4 file extension

Page 241: 3446435476_Pytho

21.3 os-Modul 229

■ get_exec_path(env=None)Die Funktion get__exec_path liefert die Liste der Verzeichnisse zurück, die zur Ausfüh-rung eines Programms durchsucht werden. Diese Verzeichnisse entsprechen den Ver-zeichnissen der Umgebungsvariablen PATH.

Die Umgebungsvariable PATH wird standardmäßig in os.environ gesucht. Wird für denParameter „env” ein Dictionary übergeben, so wird PATH dort gesucht.

>>> os.get_exec_path()['/home/bernd/perl5/bin', '/home/bernd/bin', '/usr/lib/lightdm/lightdm

', '/usr/local/sbin', '/usr/local/bin', '/usr/sbin', '/usr/bin', '/sbin', '/bin', '/usr/games', '/usr/local/games', '/home/writing/etrusker_ag/bin', '/home/bernd/bin', '.']

>>>

■ getcwd()Liefert das aktuelle Arbeitsverzeichnis als Unicode-String zurück.

■ getcwdb( )Liefert das aktuelle Arbeitsverzeichnis als Byte-String zurück.

■ getegid( )Liefert die effektive Gruppen-ID zurück.

■ geteuid( )Liefert die effektive Benutzer-ID5 zurück. Die effektive Benutzer-ID ist im Allgemeinenidentisch mit der „normalen” Benutzer-ID.

>>> os.getuid()1000

■ getgid( )Liefert die Gruppen-ID zurück.

>>> os.getgid()1000>>>

■ getgroups( )Die Funktion getgroups liefert eine Liste der Gruppen des aktuellen Prozesses zurück.

>>> os.getgroups()[4, 24, 27, 30, 46, 107, 124, 1000]>>>

■ setgroups(list)Die Gruppen des aktuellen Prozesses werden auf die Liste „list” gesetzt.

■ getloadavg( )Liefert die Anzahl der Prozesse in der Systemwarteschlange der laufenden Prozesse zu-rück. Die Zahlen entsprechen den Durchschnittswerten für die letzte Minute, die letztenfünf und die letzten 15 Minuten. Falls die Durchschnittsbelastung (load average) nichtermittelt werden konnte, wird ein OSError generiert.

5 engl. user ID

Page 242: 3446435476_Pytho

230 21 Systemprogrammierung

>>> os.getloadavg()(0.07, 0.21, 0.22)>>>

■ getlogin( )Rückgabewert ist der aktuelle Login-Name.

>>> os.getlogin()'bernd'

■ getpgid(pid )Die Funktion getpgid() liefert die Prozess-Gruppen-ID des Prozesses mit der Prozess-ID„pid” zurück. Falls der Wert von „pid” 0 ist, liefert getpgid() die Prozessgruppen-ID desaufrufenden Prozesses zurück.

■ getpgrp( )Liefert die Gruppen-ID des aktuellen Prozesses zurück.

■ getpid( )Liefert die Prozess-ID des laufenden Prozesses zurück.

■ getppid( )Liefert des Prozess-ID des Elternprozesses zurück.

■ getresgid( )Liefert ein Dreiertupel (rgid, egid, sgid) zurück. Dies entspricht den Werten „realeGruppen-ID” (real group ID), „effektive Gruppen-ID” (effective groupID) und „gespei-cherte Gruppen-ID” (saved group ID).

>>> os.getresgid()(1000, 1000, 1000)>>>

■ getresuid( )Liefert ein Dreiertupel (ruid, euid, suid) zurück. Dies entspricht den Werten „realeBenutzer-ID” (real user ID), „effektive Benutzer-ID” (effective user ID) und „gespei-cherte Benutzer-ID” (saved user ID).

>>> os.getresuid()(1000, 1000, 1000)>>>

■ getuid( )Liefert die Benutzer-ID vom aktuellen Prozess zurück.

■ kill(pid, sig)Diese Funktion entspricht dem Linux/UNIX-Kommando „kill”. Der Befehl dient dazu,Signale „sig” zu verschicken. Standardmäßig wird bei dem UNIX-Kommando kill das Si-gnal SIGTERM versendet, welches den entsprechenden Prozess dazu auffordert, sich zubeenden. Im Prinzip ist der Name „kill” irreführend, da das Beenden („Töten”) von Pro-zessen nur ein Anwendungsfall von kill ist. Allgemein dient kill der Interprozesskommu-nikation.

Page 243: 3446435476_Pytho

21.3 os-Modul 231

Im folgenden Beispiel wollen wir einen Prozess mit der pid 7036 beenden:

>>> import os>>> os.kill(7036,9)

■ killpg(pgid, sig)Eine Prozessgruppe wird mit einem Signal beendet.

■ linesep( )

■ link(src, dst )Diese Funktion erzeugt einen Hardlink „dst” auf die Datei „src”.

■ listdir( )

>>> import os>>> os.listdir()['a.txt', 'abc', 'c.txt', 'b.txt']>>> os.listdir("/home/bernd/Documents")['fritz.cfg','unterlagen.pdf', 'test.py', 'output.pdf', 'fritz2.cfg']>>>

■ lstat(path)Wie stat(path), aber symbolischen Links wird nicht gefolgt

■ major(device)Die Funktion major extrahiert die Majornummer eines raw-Devices, die häufig vomst_dev oder st_rdev Feld von stat()-Ergebnis stammt.

Beispiel: siehe minor-Funktion

■ minor(device)Die Funktion minor extrahiert die Minornummer eines raw-Devices, die häufig vomst_dev oder st_rdev Feld von stat()-Ergebnis stammt.

Beispiel:

import os

path = "ulysses.txt"

info = os.lstat(path)

major_no = os.major(info.st_dev)minor_no = os.minor(info.st_dev)

print("Major Device Number :", major_no)print("Minor Device Number :", minor_no)

Das Ergebnis sieht wie folgt aus:

bernd@saturn:~/bodenseo/python/beispiele$ python3 os_major.pyMajor Device Number : 8Minor Device Number : 7bernd@saturn:~/bodenseo/python/beispiele$

Page 244: 3446435476_Pytho

232 21 Systemprogrammierung

■ makedev( )Diese Funktion erzeugt eine Gerätenummer aus Minor- und der Majornummer.

Beispiel:

import os

path = "ulysses.txt"

info = os.lstat(path)

major_no = os.major(info.st_dev)minor_no = os.minor(info.st_dev)

print("Major Device Number :", major_no)print("Minor Device Number :", minor_no)

dev_no = os.makedev(major_no, minor_no)print("Device Number :", dev_no)

Folgende Ausgaben werden von obigem Skript erzeugt:

bernd@saturn:~/bodenseo/python/beispiele$ python3 os_makedev.pyMajor Device Number : 8Minor Device Number : 7Device Number : 2055bernd@saturn:~/bodenseo/python/beispiele$

■ mkdir(path[, mode])Die Funktion mkdir() entspricht der Linux/Unix-Funktion mkdir. Ein Verzeichnis „path”wird erzeugt, aber nur, wenn alle oberen Verzeichnisse bereits existieren. Im folgendenBeispiel führt der Versuch, das Verzeichnis „x2/x22/x33” zu erzeugen, zu einer Fehler-meldung, da bereits das Verzeichnis „x2” nicht existiert.

Der optionale Parameter „mode” entspricht dem Modus, also z.B. „0755”.

Beispiel:

>>> import os>>> os.path.isdir("x1")False>>> os.mkdir("x1", 0755)>>> os.path.isdir("x1")True>>> os.path.isdir("x2")False>>> os.mkdir("x2/x22/x33")Traceback (most recent call last):File "<stdin>", line 1, in <module>

OSError: [Errno 2] No such file or directory: 'x2/x22/x33'>>>

■ makedirs(path[, mode] )Wie der Befehl mkdir, aber makedirs erzeugt auch gegebenenfalls übergeordnete Ver-zeichnisse, wenn sie noch nicht existieren.

Page 245: 3446435476_Pytho

21.3 os-Modul 233

Der optionale Parameter „mode” entspricht dem Modus, also z.B. „0755”.

Beispiel:

>>> import os>>> os.path.isdir("x2")False>>> os.makedirs("x2/x22/x33")>>> os.path.isdir("x2")True>>> os.path.isdir("x2/x22/x33")True>>>

■ mkfifo(path[, mode])Funktion zum Erzeugen einer benannten Pipe mit Namen „path”. Optional kann der Mo-dus mit dem Parameter „mode” gesetzt werden.

■ nice(inc)Die Funktion vermindert die Priorität des Prozesses durch „inc” und liefert den Wert derneuen Priorität zurück.

>>> import os>>> os.nice(2)2>>> os.nice(3)5>>> os.nice(1)6>>> os.nice(5)11>>>

■ popen(command[, mode[, bufsize]] )Die Funktion popen öffnet eine Pipe. os.popen eignet sich in besonderer Weise, Shell-Befehle auszuführen, wie man es von der Kommandozeile des Betriebssystems gewohntist. Ein Kommandostring „command” wird dazu von der Default-Shell des Betriebssys-tems ausgeführt. Der Rückgabewert ist ein Objekt der Klasse „os._wrap_close”. DiesesObjekt dient auch dazu, gegebenenfalls die Ergebnisse des Kommandostrings „com-mand” aufzunehmen. Damit hat man dann die Möglichkeit, im Python-Programm mitdiesen Ergebnissen weiterzuarbeiten. Beim Modus „r”, d.h. lesend, werden die Ergeb-nisse der Ausführung des Kommandostrings „command” im os._wrap_close-Objektgespeichert. Wählt man den Modus „w”, also schreibend, dann werden die Ergebnissenicht an das os._wrap_close-Objekt übergeben, sondern an die Standardausgabe deslaufenden Programms.

Der Parameter „bufsize” bestimmt die Größe des zu verwendenden Puffers. Falls für„bufsize” der Wert 0 gewählt wird, erfolgt keine Pufferung. Ist der Wert auf 1 gesetzt,ist die Pufferung aktiviert, und zwar ohne Limit. Gibt man eine positive Zahl größer 1an, entspricht dies der Puffergröße in Bytes. Ein negativer Wert entspricht dem Default-Verhalten.

Page 246: 3446435476_Pytho

234 21 Systemprogrammierung

In einem etwas umfangreicheren Beispiel wollen wir demonstrieren, wie man popensowohl im „w”-Modus als auch im „r”-Modus betreibt. Zuerst suchen wir mit demShell-Kommando „grep catholic *.txt” aus allen Dateien mit der Extension „.txt” alleZeilen, die das Wort „catholic” enthalten. Da wir popen im Modus „w” starten, wirddie Shell-Ausgabe unmittelbar in die Ausgabe unseres Skripts weitergeleitet. Im zweitenpopen starten wir den gleichen Befehl im „r”-Modus. Jetzt wird das Ergebnis in einem„os._wrap_close”-Objekt zurückgeliefert, das wir in einer Variablen „res” speichern. ImFolgenden separieren wir alle Wörter, die in diesen Zeilen vorkommen, und geben dieMenge dieser Wörter aus, d.h. wir haben mehrfache Vorkommen auf eines reduziert:

import os, re

command = """ grep catholic *.txt """

print("""Kommand "command" wird im Modus "w" gestartet.""")print("""Die Ausgabe geht in die Standardausgabe des laufenden

Programms!""")os.popen(command,"w")

print(""" "command" wird wieder gestartet, aber nun im "r"-Modus:""")res = os.popen(command,"r")

list_of_lines = list(res)words = []for line in list_of_lines:

line = line[12:] # Abschneiden des Präfixes "ulysses.txt:"words = words + re.split(r"\W+", line)

words = set(words)print("""Wörter im Kontext von "catholic":""")print(words)

Starten wir das Programm, erhalten wir folgende Ausgabe:

bernd@saturn:~/bodenseo/python/beispiele$ python3 os_popen.pyKommand "command" wird im Modus "w" gestartet.Die Ausgabe geht in die Standardausgabe des laufenden Programms!"command" wird wieder gestartet, aber nun im "r"-Modus:ulysses.txt:the holy Roman catholic and apostolic church.ulysses.txt:of their brazen bells: _et unam sanctam catholicam et

apostolicamulysses.txt:Gill's, catholic club, the industrious blind. Why? Some

reason. Sun orulysses.txt:who is this used to say he was a queer breedy man great

catholic all theulysses.txt:are liege subjects of the catholic chivalry of Europe that

foundered atulysses.txt:the most Roman of catholics call _dio boia_, hangman god,

is doubtlessulysses.txt:practical catholic: useful at mission time.ulysses.txt:--You as a good catholic, he observed, talking of body and

soul, believe

Page 247: 3446435476_Pytho

21.3 os-Modul 235

ulysses.txt:--That's right, the old tarpaulin corroborated. The Irishcatholic

ulysses.txt:a cocked hat. He infinitely preferred the sacred music ofthe catholic

ulysses.txt:corporation emergency dustbuckets, the Roman catholicchurch,

ulysses.txt:prepuce, the carnal bridal ring of the holy Roman catholicapostolic

ulysses.txt:Roman catholicism at the epoch of and with a view to hismatrimony

Wörter im Kontext von "catholic":{'', 'all', 'mission', 'queer', 'useful', 'Gill', 'industrious', '

corroborated', 'infinitely', 'to', 'bridal', 'hat', 'good', 'emergency', 'preferred', 'sanctam', '_dio', 'view', 'right', 'old','Sun', 'soul', 'observed', 'are', 'carnal', 'et', 'That', 'god', 'He', 'body', 'doubtless', 'apostolic', 'reason', '_et', 'great', 'of', 'matrimony', 'practical', 's', 'or', 'blind', 'dustbuckets', 'chivalry', 'cocked', 'apostolicam', 'church', 'ring', 'Europe', 'liege', 'catholicism', 'tarpaulin', 'their', 'call', 'prepuce', 'music', 'was', 'boia_', 'catholics', 'holy', 'that', 'club', 'believe', 'with', 'he', 'Irish', 'this', 'as', 'breedy', 'catholicam', 'unam', 'and', 'is', 'talking', 'say', 'his', 'at', 'You', 'foundered', 'catholic', 'corporation', 'epoch', 'subjects','sacred', 'Why', 'bells', 'used', 'who', 'brazen', 'most', 'Roman','hangman', 'The', 'man', 'a', 'Some', 'time', 'the'}

bernd@saturn:~/bodenseo/python/beispiele$

■ readlink( )Die Funktion readlink liefert einen String zurück, der den kompletten Pfad enthält, aufden ein symbolischer Link „path” zeigt:

>>> import os>>> os.readlink("proj")'/home/data/projects/'>>>

■ remove(path )Entspricht der Funktion unlink. Eine Datei „path” wird entfernt.

■ removedirs(path)Ein Verzeichnis wird rekursiv entfernt. Handelt es sich um das einzige Unterverzeichnisdes Elternprozesses, so wird auch der Elternprozess entfernt. Dies wird rekursiv solangefortgesetzt, bis ein Pfad nicht mehr das einzige Element eines Elternprozesses ist.

bernd@saturn:~/tmp$ tree xx+-- y| +-- a| |-- b| +-- c+-- z

|-- a+-- b

Page 248: 3446435476_Pytho

236 21 Systemprogrammierung

7 directories, 0 filesbernd@saturn:~/tmp$

Aus dem folgendem Beispiel lernen wir, dass wir keine Verzeichnisse mit removedirs lö-schen können, die nicht leer sind. Bei der Ausführung von „os.removedirs("x/y/c")” istdas Verzeichnis c das einzige Unterverzeichnis und Datei von y. Der Aufruf löscht deshalbauch automatisch das Elternverzeichnis „y”.

>>> import os>>> os.removedirs("x/y")Traceback (most recent call last):File "<stdin>", line 1, in <module>File "/usr/lib/python3.2/os.py", line 179, in removedirsrmdir(name)

OSError: [Errno 39] Directory not empty: 'x/y'>>> os.removedirs("x/y/a")>>> os.removedirs("x/y/b")>>> os.removedirs("x/y/c")>>> os.removedirs("x/y")Traceback (most recent call last):File "<stdin>", line 1, in <module>File "/usr/lib/python3.2/os.py", line 179, in removedirsrmdir(name)

OSError: [Errno 2] No such file or directory: 'x/y'>>>

Nach obigen Anweisungen sieht unser Baum wie folgt aus:

bernd@saturn:~/tmp$ tree xx+-- z

|-- a+-- b

3 directories, 0 filesbernd@saturn:~/tmp$

■ rename(src, dst)Mit rename kann eine Datei oder Verzeichnis von „src” nach „dst” umbenannt werden.Falls der neue Name „dst” bereits existiert, erfolgt eine Fehlermeldung.

■ renames(old, new)

>>> import os>>> tree = os.walk("x")>>> list(tree)[('x', ['y', 'z'], ['b.txt', 'a.txt']), ('x/y', ['a', 'b', 'c'], ['b.

txt']), ('x/y/a', [], []), ('x/y/b', [], []), ('x/y/c', [], []), ('x/z', ['a', 'b'], []), ('x/z/a', [], []), ('x/z/b', [], [])]

>>> os.renames("x", "x_new")>>> tree2 = os.walk("x_new")>>> list(tree2)

Page 249: 3446435476_Pytho

21.3 os-Modul 237

[('x_new', ['y', 'z'], ['b.txt', 'a.txt']), ('x_new/y', ['a', 'b', 'c'], ['b.txt']), ('x_new/y/a', [], []), ('x_new/y/b', [], []), ('x_new/y/c', [], []), ('x_new/z', ['a', 'b'], []), ('x_new

■ rmdir(path)Das Verzeichnis „path” wird entfernt, aber nur, wenn es leer ist. Falls es nicht leer ist,wird die Ausnahme „OSError: [Errno 39] Directory not empty: ’x_new’” ge-neriert.

>>> import os>>> os.rmdir("x_new")Traceback (most recent call last):File "<stdin>", line 1, in <module>

OSError: [Errno 39] Directory not empty: 'x_new'>>>

Will man ein nichtleeres Verzeichnis löschen, empfiehlt es sich die Funktion rmtree desModuls shutil zu benutzen.

>>> import shutil>>> shutil.rmtree("x_new")>>>

■ setegid(gid)Die effektive Gruppen-ID des aktuellen Prozesses wird auf „gid” gesetzt.

■ setgid(gid)Die Gruppen-ID des aktuellen Prozesses wird auf „gid” gesetzt.

■ seteuid(uid)Die effektive User-ID des aktuellen Prozesses wird auf „uid” gesetzt.

■ setuid( )Die User-ID des aktuellen Prozesses wird auf „uid” gesetzt.

■ setregid(rgid, egid)Die effektive und reale Gruppen-ID des Prozesses wird gesetzt.

■ setresgid(rgid, egid, sgid)Die effektive, reale und gespeicherte Gruppen-ID des Prozesses wird gesetzt.

■ setpgid( )Der Systemaufruf setpged() wird ausgeführt.

■ setpgrp( )Der aktuelle Prozess wird zum Gruppenleader.

■ setresuid(ruid, euid, suid)Die effektive, reale und gespeicherte User-ID des Prozesses wird gesetzt.

■ setreuid(ruid, euid)Die effektive und reale User-ID des Prozesses wird gesetzt.

■ setsid( )Der Systemaufruf setsid() wird aufgerufen.

■ stat()Ein stat-Systemaufruf wird durchgeführt.

Page 250: 3446435476_Pytho

238 21 Systemprogrammierung

Die Ergebniswerte sind:

– st_mode – protection bits,

– st_ino – Inode-Nummer

– st_dev – Gerätenummer (device)

– st_nlink – Anzahl der Hardlinks

– st_uid – User-ID des Besitzers

– st_gid – Gruppen-ID des Besitzers

– st_size – Dateigröße

– st_atime – letzte Zugriffszeit auf die Datei

– st_mtime – letzte Modifikationszeit der Datei

– st_ctime – plattformabhängig: Zeit der letzten Änderung der Metadaten unter Unix/-Linux und die Entstehungszeit der Datei unter Windows

>>> import os>>> os.stat("b.txt")posix.stat_result(st_mode=33204, st_ino=16781124, st_dev=2055, st_nlink

=1, st_uid=1000, st_gid=1000, st_size=0, st_atime=1361693338,st_mtime=1361693338, st_ctime=1361693475)

>>>

■ stat_float_times([newval])Diese Funktion dient dazu, das Ausgabeverhalten von os.[lf ]stat zu beeinflussen: Falls„newval” True ist, liefern zukünftige Aufrufe von stat() (bzw. lstat, fstat) float-Werte zu-rück. Wenn „newval” False ist, werden zukünftige Aufrufe Integers zurückliefern.

Ruft man die Funktion ohne Parameter auf, wird die aktuelle Einstellung zurückgeliefert.

■ strerror(code)Zu einer gegebenen Fehlernummer „code” wird der entsprechende Fehlerstring zurück-geliefert. Im folgenden Beispiel lassen wir uns die ersten 10 von 132 Fehlermeldungenanzeigen:

>>> import os>>> for i in range(1,11):... print(i, os.strerror(i))...1 Operation not permitted2 No such file or directory3 No such process4 Interrupted system call5 Input/output error6 No such device or address7 Argument list too long8 Exec format error9 Bad file descriptor10 No child processes>>>

Page 251: 3446435476_Pytho

21.3 os-Modul 239

■ symlink(src, dst)symlink erzeugt einen Link „dst”, der auf „src” zeigt.

Im folgenden Beispiel erzeugen wir zunächst einen symbolischen Link namens„link2abc.py” auf die Datei „abc.py” mit der Funktion symlink:

>>> os.symlink("abc.py", "link2abc.py")>>> os.path.islink("abc.py")False>>> os.path.islink("link2abc.py")True>>>

■ sysconf( )

■ system( command )

Eine Kommandostring „command” wird als in einer Untershell ausgeführt. Diese Funk-tionalität ist veraltet. Man sollte stattdessen das Modul subprocesses benutzen.

■ times( )times liefert ein Tupel mit den verschiedenen Prozesszeiten zurück:

(utime, stime, cutime, cstime, elapsed_time)

>>> os.times()(0.14, 0.04, 0.04, 0.01, 17210084.65)>>>

■ umask( )

■ uname( )Die Funktion uname liefert ein Tupel zurück, welches die folgenden Informationen überdas Betriebssystem enthält:

(sysname, nodename, release, version, machine)

>>> import os>>> os.uname()('Linux', 'saturn', '3.5.0-23-generic', '#35-Ubuntu SMP Thu Jan 24

13:05:29 UTC 2013', 'i686')>>>

■ unlink(path)Eine Datei wird entfernt. Entspricht der Funktion remove(path).

■ urandom(n)Die Funktion urandom liefert n Zufallsbytes zurück, die für kryptografische Anwendun-gen benutzt werden können.

>>> os.urandom(10)b'9\xc3\xe9\x02\xe8\xa2z\xea\xfcQ'>>> os.urandom(10)b'\xdd\x06\x9fK\x17{H{>\xa6'>>> os.urandom(10)b'^)\xdb\x9c\x8b\x99\x82\x96\x89\xc4'>>>

Page 252: 3446435476_Pytho

240 21 Systemprogrammierung

■ utime(path, (atime, mtime) )Die letzte Zugriffszeit (access time) und die Modifikationszeit (modification time) einerDatei werden auf die Werte des Tupels „(atime, mtime)” gesetzt. Werden keine Zeitenangegeben, werden die Zeiten der Datei „path” auf die aktuelle Zeit gesetzt.

■ wait()Wartet darauf, dass ein Kind-Prozess terminiert, und liefert ein Tupel zurück, das die PIDund den exit-Status enthält, also das Tupel (pid, status).

■ wait3( )Wie wait, aber es wird ein 3-Tupel (pid, status, rusage) zurückgeliefert. „rusage” bezeich-net die „resource”-Benutzungsinformation.

■ waitpid(pid, options)Wie wait, aber mit den Parametern „pid” und „status”.

■ walk( )Betrachten wir folgenden Teilbaum eines Dateisystems:

Nun wenden wir walk auf diesen Teilbaum an. Wir starten Python in dem Verzeichnis, indem sich das Unterverzeichnis abc befindet:

>>> import os>>> os.walk("abc")<generator object walk at 0xb714ba7c>>>> for path in os.walk("abc"):... print(path)...('abc', ['a', 'b', 'c'], [])('abc/a', [], ['a1.txt', 'a2.txt'])('abc/b', [], ['b2.txt', 'b3.txt', 'b1.txt'])('abc/c', [], ['c1.txt'])>>>

Die Funktion eignet sich also ideal, um einen Dateibaum rekursiv zu durchwandern.

21.3.6 os.path - Arbeiten mit Pfaden

Man kann das Modul „path” auf zwei Arten importieren: entweder direkt mit „importos.path” oder indirekt über „import os”

Page 253: 3446435476_Pytho

21.3 os-Modul 241

■ abspath(name)Liefert einen Pfad zurück, der sich aus dem aktuellen Arbeitsverzeichnis und dem Wertvon Namen zusammensetzt. Dabei spielt es keine Rolle, ob der daraus resultierende Pfadwirklich existiert. Im folgenden Beispiel befinden wir uns im Homeverzeichnis des Be-nutzers bernd. Dort gibt es ein Unterverzeichnis „python”, aber keines, was „nonsense”heißt:

bernd@saturn:~$ python3Python 3.2.3 (default, Oct 19 2012, 19:53:57)[GCC 4.7.2] on linux2Type "help", "copyright", "credits" or "license" for more information.>>> import os>>> os.path.abspath(".")'/home/bernd'>>> os.path.abspath("python")'/home/bernd/python'>>> os.path.abspath("nonsense")'/home/bernd/nonsense'>>>

■ basename(p)Liefert die letzte Komponente eines Pfadnamens zurück.

Beispiel:

>>> os.path.basename("/home/bernd/nonsense")'nonsense'>>> os.path.basename("/home/bernd/hellO\_world.py")'hellO\_world.py'>>> os.path.basename("/home/bernd/python/")''>>>

■ commonprefix(pf )Liefert den größtmöglichen Pfad zurück, mit dem alle Pfade der Liste „pf” beginnen.

Beispiel:

>>> paths = ["/home/bill/Documents", "/home/bill/bin", "/home/bill/Documents/articles/"]

>>> os.path.commonprefix(paths)'/home/bill/'>>>

■ sepAttribut mit dem Trennsymbol in Pfaden, also in Linux „/” und unter Windows „\\”.

>>> os.sep'/'>>>

■ dirname(p)Liefert den Anfang des Pfades „p” bis zum letzten Schrägstrich „/” zurück:

Page 254: 3446435476_Pytho

242 21 Systemprogrammierung

>>> path = "/home/bill/documents">>> os.path.dirname(path)'/home/bill'>>> path = "/home/bill/documents/">>> os.path.dirname(path)'/home/bill/documents'>>> path = "/home/bill/documents/beispiel.py">>> os.path.dirname(path)'/home/bill/documents'>>>

■ exists(path)Mit dieser Funktion kann man testen, ob ein Pfad existiert, d.h. „True” wird zurückgelie-fert, wenn ein Pfad existiert, ansonsten „False”. „False” wird auch zurückgeliefert, wennes sich um einen „broken link” handelt.

Beispiel:

Bei der Funktion „abspath” hatten wir vermerkt, dass in „/home/bernd/” ein Unterver-zeichnis „python” existiert, aber keines mit Namen „nonsense”. Im Folgenden prüfen wirdies mittels „exists” nach:

bernd@saturn:~$ python3Python 3.2.3 (default, Oct 19 2012, 19:53:57)[GCC 4.7.2] on linux2Type "help", "copyright", "credits" or "license" for more information.>>> import os>>> os.path.exists("/home/bernd/python")True>>> os.path.exists("/home/bernd/nonsense")False>>>

■ expanduser(path)„~” und „~user” werden durch den vollständigen Pfad ersetzt, d.h. „~” wird mit dem Wertder Shell-Umgebungsvariablen $HOME ersetzt, bzw. mit dem Homeverzeichnis des Be-nutzers „user”. Falls $HOME nicht gesetzt ist oder im Fall von „~user” der Benutzer „user”nicht existiert, wird der Wert von „path” nicht verändert.

Im folgenden Beispiel existiert kein Benutzer mit Namen „homer”, aber einer namens„bill”:

>>> os.path.expanduser("~")'/home/bernd'>>> os.path.expanduser("~homer")'~homer'>>> os.path.expanduser("~bill")'/home/bill'>>>

■ expandvars(path)Variablen bzw. Variablen in Pfaden werden durch ihren Wert ersetzt. Falls eine Variablenicht definiert ist, bleibt sie unverändert.

Page 255: 3446435476_Pytho

21.3 os-Modul 243

>>> os.path.expandvars("$SHELL")'/bin/bash'>>> os.path.expandvars("$ABC")'$ABC'>>> os.path.expandvars("/home/$USER/")'/home/bernd/'>>>

■ getatime(filename)Liefert für einen existierenden Datei- oder Verzeichnisnamen die letzte Zugriffszeit zu-rück. Der Name kann sowohl durch einen relativen Pfad als auch durch einen absolutenPfad gegeben sein. Falls ein Name nicht existiert, wird ein OSError produziert mit derMeldung: „ No such file or directory:”.

atime entspricht der Unix- bzw. Linux-atime, also dem letzten lesenden oder schreiben-den Zugriff auf die Datei.

Beispiel:

>>> import os>>> os.path.getatime("/home/bernd/bodenseo/python/buch.tex")1361384210.8701274>>> os.path.getatime("buch.tex")1361384210.8701274>>> os.path.getatime("buecher.tex")Traceback (most recent call last):File "<stdin>", line 1, in <module>File "/usr/lib/python3.2/genericpath.py", line 59, in getatimereturn os.stat(filename).st_atime

OSError: [Errno 2] No such file or directory: 'buecher.tex'

■ getctime(filename)Wie getatime, jedoch wird hier die ChangeTime, also die Zeit der letzten Änderung ander Datei ausgegeben. Änderung bedeutet hier, dass entweder der Inhalt der Datei selbstoder die Metadaten6 der Datei geändert wurden.

■ getmtime(filename)Diese Funktion ist analog zu getctime und getatime. Die mtime ändert sich jedes Mal,wenn in eine Datei geschrieben wird, also auch, wenn sie erstellt wird. Die mtime ändertsich aber im Unterschied zur ctime nicht, wenn sich die Metadaten einer Datei ändern.

Wir schauen uns nun getatime, getmtime und getctime im Zusammenspiel an. Zunächsterzeugen wir eine leere Datei names „abc.py”. Wir erkennen, dass die ctime und die mti-me gleich sind, während atime einen etwas früheren Wert aufweist, die atime wird beimSchreiben nicht mehr verändert:

>>> fh = open("abc.py","w")>>> fh.close()>>> os.path.getatime("abc.py")1361433115.8920438>>> os.path.getmtime("abc.py")

6 Als Metadaten bezeichnet man in diesem Zusammenhang „Zugriffsrechte”, „Besitzer” oder Ähnliches.

Page 256: 3446435476_Pytho

244 21 Systemprogrammierung

1361433693.0429058>>> os.path.getctime("abc.py")1361433693.0429058

Nun ändern wir die Zugriffsrechte der Datei „abc.py”. Wie erwartet ändert sich nun nurdie ctime, da sich die mtime nur ändert, wenn wirklich der Inhalt einer Datei verändertwurde, was ja hier nicht der Fall ist:

>>> os.chmod("abc.py",755)>>> os.path.getatime("abc.py")1361433115.8920438>>> os.path.getmtime("abc.py")1361433693.0429058>>> os.path.getctime("abc.py")1361433817.0595207

Jetzt ändern wir wirklich den Inhalt, indem wir weiteren Text an die Datei anhängen. Wirerkennen, dass nun sowohl getmtime und getctime die gleichen Werte zurückliefern:

>>> fh = open("abc.py","a")>>> fh.write("Just some spam!")15>>> fh.close()>>> os.path.getatime("abc.py")1361433115.8920438>>> os.path.getmtime("abc.py")1361433951.7121885>>> os.path.getctime("abc.py")1361433951.7121885>>>

■ getsize(filename)Liefert die Dateigröße zurück, d.h. die Anzahl der Bytes.

>>> fh = open("abc.py","w")>>> fh.close()>>> os.path.getsize("abc.py")0>>> fh = open("abc.py","a")>>> fh.write("Do you like spam?")17>>> fh.close()>>> os.path.getsize("abc.py")17>>>

■ isabs(s)Die Funktion prüft, ob es sich bei einem Dateinamen um einen absoluten oder einenrelativen Dateinamen handelt. Dabei spielt es keine Rolle, ob ein Pfadname wirklich imDateisystem existiert. Es handelt sich um eine reine syntaktische Prüfung.

>>> os.path.isabs("/monty/python")True

Page 257: 3446435476_Pytho

21.3 os-Modul 245

>>> os.path.isabs("python")False>>> os.path.exists("/monty/python")False>>>

Achtung:

Obwohl es sich bei Pfadnamen wie „~/Documents” oder „~monty” um absolute Pfadna-men handelt, wird in diesem Fällen jedoch der Wert „False” zurückgeliefert:

>>> os.path.isabs("~/python")False>>> os.path.isabs("~monty/python")False>>>

■ isdir(s)Liefert True zurück, wenn der Pfadname „s” ein existierendes Verzeichnis bezeichnet:

>>> os.path.isdir("beispiele")True>>> os.path.isdir("monty")False>>> os.path.isdir("abc.py")False>>> os.path.exists("abc.py")True>>> os.path.exists("/home/bernd/")True>>>

■ isfile(path)Liefert True zurück, wenn der Pfadname „path” eine existierende Datei bezeichnet:

>>> os.path.isfile("abc.py")True>>> os.path.isfile("/home/bernd/bodenseo/python/abc.py")True>>> os.path.isfile("/home/bernd/bodenseo/python/spam.py")False>>>

■ islink(path)Liefert True zurück, wenn es sich bei „path” um einen symbolischen Link handelt, an-sonsten wird False zurückgegeben. Im folgenden Beispiel erzeugen wir zunächst einensymbolischen Link namens „link2abc.py” auf die Datei „abc.py” mit der Funktion sym-link:

>>> os.symlink("abc.py", "link2abc.py")>>> os.path.islink("abc.py")False>>> os.path.islink("link2abc.py")True>>>

Page 258: 3446435476_Pytho

246 21 Systemprogrammierung

■ ismount(path)Testet, ob es sich bei dem Pfad „path” um einen Mount-Punkt handelt:

>>> os.path.ismount("/media/bernd/")False>>> os.path.ismount("/media/bernd/CDD8-B9BF")True>>>

■ join(a, *p)Zwei oder mehr Pfadnamekomponenten werden vereinigt, wobei ein Schrägstrich „/”bei Bedarf eingefügt wird. Falls eine der Komponenten ein absoluter Pfadname ist, wer-den alle vorher stehenden Komponenten weggelassen. Steht als letzte Komponente einleerer String, wird ein Schrägstrich angefügt, falls der Pfad sonst nicht mit einem Schräg-strich geendet hätte.

>>> os.path.join("abc","def","xyz")'abc/def/xyz'>>> os.path.join("/home/monty","def","xyz")'/home/monty/def/xyz'>>> os.path.join("abc","/home/monty","xyz")'/home/monty/xyz'>>> os.path.join("abc","home/monty","xyz")'abc/home/monty/xyz'>>> os.path.join("abc","home/monty","xyz", "")'abc/home/monty/xyz/'>>> os.path.join("abc","home/monty","xyz/")'abc/home/monty/xyz/'>>>

■ lexists(path)Liefert True zurück, wenn der Pfad „path” existiert. Falls path ein symbolischer Link ist,wird auch True zurückgeliefert, wenn es sich um einen „broken link” handelt. Zum Ver-ständnis des folgenden Beispiels muss man wissen, dass es in dem existierenden Ver-zeichnis „/home/bernd/” kein Unterverzeichnis „monty” gibt und dass es sich bei „/ho-me/bernd/bodenseo/python/monty” um einen „broken link” handelt.

>>> os.path.lexists("/home/bernd/monty")True>>> os.path.lexists("/home/bernd/monty")False>>> os.path.lexists("/home/bernd/bodenseo/python/monty")True>>>

■ normcase(s)Normalisiert Groß- und Kleinschreibung eines Pfades. Diese Funktion hat keine Wirkungunter Posix, also nicht unter Linux und Unix. Unter Windows spielt aber Groß- und Klein-schreibung bei Pfad- und Dateinamen keine Rolle, d.h. „ABC.PY”, „abc.py” oder „Abc.py”sind alles gültige Schreibweisen für das gleiche Dateiobjekt. Wendet man normcase un-ter Windows auf diese Namen an, erhält man jeweils den Namen komplett in Kleinschrei-bung.

Page 259: 3446435476_Pytho

21.3 os-Modul 247

Beispiel unter Windows:

>>> os.path.normcase("ABC.PY")'abc.py'>>> os.path.normcase("Abc.py")'abc.py'>>> os.path.normcase("abc.py")'abc.py'>>> os.path.normcase("c:/Users/Bernd/")'c:\\users\\bernd\\

Unter Linux sieht es hingegen wie folgt aus:

>>> os.path.normcase("ABC.PY")'ABC.PY'>>> os.path.normcase("Abc.py")'Abc.py'>>> os.path.normcase("abc.py")'abc.py'>>>

■ normpath(path)Der Pfad „path” wird normalisiert, d.h. mehrfache Schrägstriche und Schrägstriche amEnde eines Pfades werden entfernt.

>>> os.path.normpath("abc.py/")'abc.py'>>> os.path.normpath("/home///bernd//abc.py/")'/home/bernd/abc.py'

■ realpath(filename)Die Funktion „realpath” liefert den kanonischen Pfad, d.h. ein Pfad ohne symbolischeLinks, eines Pfades „filename” zurück. Symbolische Links werden dabei aufgelöst, wiewir im Beispiel sehen können.

In der folgenden interaktiven Python-Shell befinden wir uns im Verzeichnis „/home/bernd/bodenseo/python/beispiele/intranet”. Dort legen wir einen symbolischen Link„intranet” auf „/var/www/intranet” an:

>>> os.symlink("/var/www/intranet", "intranet")>>> os.path.realpath("intranet")'/home/data/intranet'>>> os.path.realpath("/home/bernd/bodenseo/python/beispiele/intranet")'/home/data/intranet'>>>

■ relpath(path, start=None)Es wird ein Pfadname relativ zur aktuellen Position für den Pfad „path” generiert:

>>> os.getcwd()'/home/data/bodenseo/python/beispiele'>>> os.path.relpath("/home/bernd/")'../../../../bernd'>>>

Page 260: 3446435476_Pytho

248 21 Systemprogrammierung

■ samefile(f1, f2)Es wird getestet, ob zwei Pfadnamen die gleiche Datei referenzieren. Es wird nicht ge-prüft, ob zwei Dateien den gleichen Inhalt haben, wie wir im Folgenden auch erkennenkönnen:

>>> os.path.samefile("abc.py", "../python/abc.py")True>>> os.symlink("abc.py", "monty.py")>>> os.path.samefile("abc.py", "monty.py")True>>> import shutil>>> shutil.copyfile("abc.py", "beispiele/abc.py")>>> os.path.samefile("beispiele/abc.py", "monty.py")False>>> os.path.samefile("beispiele/abc.py", "abc.py")False>>>

■ split(p)Ein Pfadname wird in den Dateinamen bzw. das tiefste Verzeichnis und den Basispfad,also alles vor dem letzten Schrägstrich aufgeteilt. Beide Teile können jeweils leer sein.

Beispiel:

>>> os.path.split("/abc/xyz/beispiel.py")('/abc/xyz', 'beispiel.py')>>> os.path.split("/abc/xyz/")('/abc/xyz', '')>>> os.path.split("/abc/xyz")('/abc', 'xyz')>>> os.path.split("xyz.py")('', 'xyz.py')>>>

■ splitdrive(p)Diese Funktion ist im Prinzip auf das Windows-Betriebssystem zugeschnitten. Ein Pfad-name wird in den Laufwerksnamen (drive) und den Rest des Pfades zerlegt. Unter Linuxund Unix ist der erste Wert immer der leere String, weil es keine Laufwerkskomponentegeben kann.

Das folgende Beispiel wurde unter Windows erstellt:

>>> os.path.splitdrive("c:\\windows\users")('c:', '\\windows\\users')>>>

Unter Linux sieht es wie folgt aus, auch wenn der Pfad keinen Sinn ergibt:

>>> os.path.splitdrive("c:/abc/xyz/beispiel.py")('', 'c:/abc/xyz/beispiel.py')

■ splitext(p)Die Dateierweiterung wird von einem Pfad abgesplittet, d.h. alles, was nach dem letztenPunkt folgt:

Page 261: 3446435476_Pytho

21.4 shutil-Modul 249

>>> os.path.splitext("/abc/xyz/beispiel.py")('/abc/xyz/beispiel', '.py')>>> os.path.splitext("/abc/xyz/beispiel")('/abc/xyz/beispiel', '')>>> os.path.splitext("/abc/xyz/beispiel/")('/abc/xyz/beispiel/', '')>>>

21.4 shutil-ModulIm vorigen Abschnitt hatten wir bereits eine Datei unter Benutzung des shutil-Moduleskopiert. Viele Python-Nutzer, die shutil noch nicht kennen, vermuten die Kopierfunktionzunächst im os-Modul.

Dieses Modul stellt eine plattformunabhängige Schnittstelle zur Verfügung, um Dateienund Dateibäume zu kopieren, entfernen oder zu archivieren.

Bei der Beschreibung der folgenden Funktionen benutzen wir häufig die String-Parameter„src” und „dst”. Dabei bezeichnet „src” die Quelldatei7 bzw. das Quellverzeichnis, und „dst”steht für die Zieldatei bzw. Zielverzeichnis.8

■ copyfile(src, dst)Kopiert die Datei „src” nach „dst”. Wenn die Datei unter dst bereits existiert, wird sieüberschrieben.

Dabei muss der Pfad dst schreibbar sein. Ansonsten wird ein IOError geworfen.

■ copy(src, dst)Kopiert die Datei src nach dst unter Beibehaltung der Zugriffsrechte. Existiert „dst” be-reits, wird „dst” überschrieben. Entspricht „dst” einem Ordner, wird innerhalb diesesOrdners eine Datei mit dem Dateinamen von src angelegt oder überschrieben, falls dieseDatei bereits in „dst” existiert.

■ copy2(src, dst)Wie copy, allerdings werden auch die Zugriffsrechte, der Eigentümer und der Zeitstempelmitkopiert. Der Befehl entspricht dem Unix/Linux-Befehl: „cp -p src dst”

■ copyfileobj(fsrc, fdst, length=16384)Der Inhalt des zum Lesen geöffneten Dateiobjekts „fsrc” wird in das zum Schreiben ge-öffnete „fdst”-Objekt kopiert. Mit dem optionalen Parameter kann gesteuert werden, wieviele Bytes jeweils gelesen und anschließend gespeichert werden.

>>> fh = open("abc.txt")>>> fhw = open("spam.txt", "w")>>> shutil.copyfileobj(fh,fhw)>>> fh.close()>>> fhw.close

7 src steht für das englische Wort source, was Quelle in Deutsch bedeutet.8 dst steht dabei als Abkürzung für destination, was in Deutsch Ziel bedeutet.

Page 262: 3446435476_Pytho

250 21 Systemprogrammierung

■ copymode(src, dst)Die Bits für die Zugriffsrechte werden von „src” nach „dst” kopiert.

■ copystat(src, dst)Alle Statusinformationen, d.h.mode bits, atime, mtime, flags, werden von „src” nach„dst” kopiert.

■ copytree(src, dst, symlinks=False, ignore=None, copy_function=<function copy2>,ignore_dangling_symlinks=False)Ein Verzeichnisbaum „src” wird rekusiv in einen Verzeichnisbaum „dst” kopiert.

Wir betrachten das Beispiel, das wir bereits in Beispiel 21.3.5 (Weitere Funktionen imÜberblick) kennengelernt haben:

>>> import shutil>>> shutil.copytree("abc","xyz")

Der Baum „dst” darf noch nicht existieren. Wenn wir obigen Befehl wieder anwenden,erhalten wir deshalb eine Fehlermeldung:

>>> shutil.copytree("abc","xyz")Traceback (most recent call last):File "<stdin>", line 1, in <module>File "/usr/lib/python3.2/shutil.py", line 203, in copytreeos.makedirs(dst)

File "/usr/lib/python3.2/os.py", line 152, in makedirsmkdir(name, mode)

OSError: [Errno 17] File exists: 'xyz'>>>

Interessant ist vor allen Dingen der Fall, wenn es sich bei dem Pfadnamen „dst” um einenPfad handelt, der im Innern des durch „src” bezeichneten Teilbaumes liegt. In anderenWorten „src” ist ein Teilstring von „dst”, wenn man von Pfadnamen in kanonischer Dar-stellung, also ohne Links, ausgeht.

Wir kopieren nun den gesamten Teilbaum „abc” in das Verzeichnis „abc” mit dem neuenNamen „xyz”, also parallel zu den Verzeichnissen „a”, „b” und „c”. Mithilfe der Funktionwalk des Moduls os schauen wir uns die Bäume jeweils an:

>>> import shutil>>> import os>>> os.walk("abc")<generator object walk at 0xb71d6054>>>> list(os.walk("abc"))[('abc', ['a', 'b', 'c'], []), ('abc/a', [], ['a1.txt', 'a2.txt']), ('

abc/b', [], ['b2.txt', 'b3.txt', 'b1.txt']), ('abc/c', [], ['c1.txt'])]

>>> shutil.copytree("abc","abc/xyz")>>> list(os.walk("abc"))[('abc', ['xyz', 'a', 'b', 'c'], []), ('abc/xyz', ['a', 'b', 'c'], []),

('abc/xyz/a', [], ['a1.txt', 'a2.txt']), ('abc/xyz/b', [], ['b2.txt', 'b3.txt', 'b1.txt']), ('abc/xyz/c', [], ['c1.txt']), ('abc/a', [], ['a1.txt', 'a2.txt']), ('abc/b', [], ['b2.txt', 'b3.txt', 'b1.txt']), ('abc/c', [], ['c1.txt'])]

>>>

Page 263: 3446435476_Pytho

21.4 shutil-Modul 251

Falls der optionale Parameter symlinks auf True gesetzt ist, werden symbolische Linksim Quellbaum auch als symbolische Links im Zielbaum realisiert. Ist der Parameter aufFalse gesetzt, was per Default der Fall ist, werden die Inhalte der Dateien, auf die sym-bolische Links zeigen, kopiert. Falls ein symbolischer Link nicht existiert, wird dies alsFehler ausgegeben. Um diese Fehlermeldung zu unterdrücken, setzt man den optiona-len Parameter ignore_dangling_symlinks auf True.

Der optionale „ignore”-Parameter ist ein aufrufbares Objekt wie zum Beispiel eine Funk-tion. Es erhält den Pfad „src” und eine Liste der Namen „names”, die von copytree be-sucht werden, als Parameter, so wie sie von os.listdir() erzeugt wird. Das aufrufbare Ob-jekt liefert dann eine Liste „ignored_names” zurück. Also eine Liste der Namen, die nichtvon copytree kopiert werden sollen.

Man kann auch eine eigene Funktion übergeben, die festlegt, wie die Dateien zu kopie-ren sind. Dazu dient der optionale Parameter copy_function. Als Default wird copy2()benutzt.

■ get_archive_formats()Liefert eine Liste der unterstützten Formate zum Archivieren und Dearchivieren zurück.Jedes Element dieser Ergebnisliste ist ein Zweiertupel mit dem Namen und einer Be-schreibung:

>>> shutil.get_archive_formats()[('bztar', "bzip2'ed tar-file"), ('gztar', "gzip'ed tar-file"), ('tar',

'uncompressed tar file'), ('zip', 'ZIP file')]>>>

■ get_unpack_formats()Liefert eine Liste der Dekomprimierformate zurück. Jedes Element der Ergebnisliste ent-spricht einem Tupel der Form: (Name, Extension, Beschreibung)

>>> shutil.get_unpack_formats()[('bztar', ['.bz2'], "bzip2'ed tar-file"), ('gztar', ['.tar.gz', '.tgz

'], "gzip'ed tar-file"), ('tar', ['.tar'], 'uncompressed tar file'), ('zip', ['.zip'], 'ZIP file')]

>>>

■ ignore_patterns(*patterns)Diese Funktion kann als „ignore”-Parameter bei copytree benutzt werden.

Die „patterns” entsprechen glob-Pattern der Linux-Shells.

■ make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0, dry_run=0, owner=None, group=None, logger=None)Eine Archivdatei wird erzeugt. Dafür stehen die gängigen Archivierungsformate zur Ver-fügung, wie zum Beispiel das zip- oder tar-Format.

Die Funktion liefert den vollständigen Pfad und Namen der erzeugten Archivdatei zu-rück.

„base_name” ist der Name der Archivdatei, die erzeugt werden soll, ohne die üblicheEndung.

Der vollständige Name der Archivdatei ergibt sich mit dem Parameter „format”, welcherder Extension des Archivformats entspricht, also „zip”, „tar”, „bztar” oder „gztar”.

Page 264: 3446435476_Pytho

252 21 Systemprogrammierung

Der vollständige Name der Archivdatei wird also durch die folgende Konkatenation ge-bildet:

base_name + +̈+̈ format

„root_dir” entspricht dem Root-Verzeichnis, von dem relative Pfadnamen in base_dir be-ginnen.

„base_dir” ist das Root-Verzeichnis des zu archivierenden Teilbaums.

Im folgenden Beispiel wird ein Verzeichnisbaum mit dem Namen „./abc” archiviert. Die-ser Baum muss sich im aktuellen Unterverzeichnis befinden, denn „root_dir” ist auf „.”gesetzt:

>>> import shutil>>> shutil.make_archive("my_archive", "bztar", ".", "./abc")'/home/bernd/tmp/my_archive.tar.bz2'>>>

Die erzeugte Archivdatei befindet sich nun im aktuellen Verzeichnis.

Nun rufen wir obigen Befehl in einem Verzeichnis auf, in dem sich kein Unterverzeichnis„./abc” befindet, d.h. in unserem Beispiel das Homeverzeichnis des Benutzers „bernd”.Wir erhalten dann eine Fehlermeldung:

>>> import shutil>>> shutil.make_archive("my_archive", "bztar", ".", "./abc")Traceback (most recent call last):File "<stdin>", line 1, in <module>File "/usr/lib/python3.2/shutil.py", line 585, in make_archivefilename = func(base_name, base_dir, **kwargs)

File "/usr/lib/python3.2/shutil.py", line 426, in _make_tarballtar.add(base_dir, filter=_set_uid_gid)

File "/usr/lib/python3.2/tarfile.py", line 2065, in addtarinfo = self.gettarinfo(name, arcname)

File "/usr/lib/python3.2/tarfile.py", line 1937, in gettarinfostatres = os.lstat(name)

OSError: [Errno 2] No such file or directory: './abc'>>>

Falls wir den Pfad in base_dir nicht relativ, sondern absolut angeben, wird der Teilbaum„abc” unabhängig vom Wert von root_dir gefunden. Die Archivdatei wird jedoch im vonroot_dir definierten Pfad abgelegt:

>>> import shutil>>> shutil.make_archive("my_archive", "bztar", "/home/bernd/", "/home/

bernd/tmp/abc")'/home/bernd/my_archive.tar.bz2'>>>

■ move(src, dst)Dieser Befehl ist ähnlich wie der Unix/Linux-Befehl „mv”. „move” dient dazu, eine Dateioder ein Verzeichnis an einen anderen Speicherort zu verschieben.

Falls der Zielpfad „dst” ein Verzeichnis oder ein symbolischer Link auf ein Verzeichnisist, dann wird das durch „src” bezeichnete Objekt, also beispielsweise eine Datei oder

Page 265: 3446435476_Pytho

21.4 shutil-Modul 253

ein Verzeichnis, in das Verzeichnis „dst” verschoben. Wenn das Verzeichnis „dst” nochnicht existiert, wird es erzeugt.

Wenn das Ziel „dst” bereits existiert, aber kein Verzeichnis, sondern eine Datei ist, wirddiese überschrieben, sofern die entsprechenden Rechte vorhanden sind. Dies aber nur,falls es sich bei „src” auch um eine Datei handelt. Ist „src” ein Verzeichnis, erhält maneine Fehlermeldung.

■ register_archive_format(name, function, extra_args=None, description=”)Ein Archivformat registrieren.

„name” ist der Name des Formats. „function” ist eine Funktion („callable”), die benutztwird, um ein Archiv zu erzeugen. Der optionale Parameter „extra_args” ist ein sequenti-elles Datenobjekt mit (name, value)-Tupels, die der Funktion „function” übergeben wer-den.

„description” ist optional und kann die Beschreibung des Formats enthalten. Außerdemwird „description” von get_archive_formats()-Funktion zurückgeliefert.

■ register_unpack_format(name, extensions, function, extra_args=None, descripti-on=”)Analog zu register_archive_format allerdings für ein Format zum Entpacken.

„name” ist der Name des Formats. „extensions” ist eine Liste von Extensions für diesesFormat.

„function” ist eine Funktion, die benutzt wird, um ein Archiv zu entpacken. Als Parameterwird ein Archiv übergeben. Falls die Funktion mit dem Archiv nicht klarkommt, d.h. esnicht entpacken kann, wird ein ReadError generiert.

Der optionale Parameter „extra_args” ist ein sequentielles Datenobjekt mit (name,value)-Tupels, die der Funktion „function” übergeben werden.

„description” ist optional und kann die Beschreibung des Formats enthalten. Außerdemwird „description” von der get_unpack_formats()-Funktion zurückgeliefert.

■ rmtree(path, ignore_errors=False, onerror=None)Ein Verzeichnisbaum „path” wird rekursiv gelöscht.

Falls „ignore_errors” gesetzt ist, werden Fehler ignoriert.

Falls „onerror” gesetzt ist, d.h. ein Zeiger auf ein aufrufbares Objekt, dann wird diesesObjekt mit den Argumenten (func, path, exc_info) aufgerufen, wobei „func” entweder„os.listdir”, „os.remove”, oder „os.rmdir” ist, „path” der Pfad ist, der den Fehler verursachthat, und exc_info ist ein Tupel, was von sys.exc_info() zurückgeliefert wurde.

Falls sowohl „ignore_errors” auf False gesetzt ist und „onerror” auf None, dann wird eineAusnahme generiert.

■ unpack_archive(filename, extract_dir=None, format=None)Entpackt ein Archiv.

„filename” ist der Name des Archivs.

„extract_dir” ist der Name des Zielverzeichnisses, wohin das Archiv entpackt werdensoll. Falls dieser optionale Parameter nicht angegeben wird, wird das aktuelle Arbeits-verzeichnis als Zielverzeichnis verwendet.

„format” ist das verwendete Archivierungsformat, also eines der Formate „zip”, „tar”,oder „gztar” oder ein anderes registriertes Format (siehe register_archive_format). Falls

Page 266: 3446435476_Pytho

254 21 Systemprogrammierung

kein Format angegeben wird, prüft „unpack_archive”, ob es für die Dateiendung einenregistrierten Entpacker gibt. Falls keiner gefunden wird, wird ein ValueError generiert.

■ unregister_archive_format(name)Ein Format wird deregistriert.

■ unregister_unpack_format(name)Entfernt das pack-Format aus der Registrierung.

Page 267: 3446435476_Pytho

22 Forks

22.1 Fork

BILD 22.1 Zellteilung

Schon lange vor der Biologie hat sich die Informatik mitdem Klonen beschäftigt. Aber man nannte es nicht Klo-nen sondern Forken. fork bedeutet in Englisch Gabel, Ga-belung, Verzweigung und als Verb gabeln, aufspalten undverzweigen. Aber es bedeutet auch eine Aufspaltung oderals Verb aufspalten. Im letzteren Sinn wird es bei Betriebs-systemen verwendet, vor allem bei Unix und Linux. Prozes-se werden aufgespalten – geklont würde man heute ehersagen – und führen dann ein eigenständiges „Leben”.In der Informatik steht der Begriff Fork für die Bezeichnungverschiedener Sachverhalte:

■ Ein vom Betriebssystem bereitgestellter Systemaufruf, der den bestehenden Prozess auf-spaltet – und dabei eine exakte Kopie des Prozesses erzeugt – und dann beide Prozessegewissermaßen parallel laufen lässt.

■ In der Software-Entwicklung bezeichnet ein Fork eine Abspaltung von einem (Haupt-)Projekt.

■ Die Fähigkeit einiger Dateisysteme zur Unterteilung von Dateien

22.2 Fork in PythonFalls Sie Python nur unter Windows benutzen, können Sie den Rest des Kapitels getrostüberspringen, da Forks unter Windows nicht unterstützt werden. Sie laufen aber unter Li-nux und Unix.

Beim Systemaufruf os.fork erzeugt der aktuelle Prozess eine Kopie von sich selbst, welchedann als Kindprozess des erzeugenden Programmes läuft. Der Kindprozess übernimmt dieDaten und den Code vom Elternprozess und erhält vom Betriebssystem eine eigene Pro-zessnummer, eine sogenannte PID (engl. „Process IDentifier”). Der Kindprozess läuft als

Page 268: 3446435476_Pytho

256 22 Forks

eigenständige Instanz des Programms, unabhängig vom Elternprozess. Am Rückgabewertvon fork() erkennt man, in welchem Prozess man sich befindet. 0 kennzeichnet den Kind-prozess. Im Fehlerfall liefert fork() einen Wert kleiner 0 zurück, und kein Kindprozess wirderzeugt.

Um Prozesse forken zu können, müssen wir das Modul os in Python importieren.

Das folgende Beispiel-Skript zeigt einen Eltern-Prozess (Parent), der sich beliebig oft for-ken kann, solange man als Benutzer des Skripts kein „q” bei der Eingabeaufforderung ein-gibt. Sowohl der Kindprozess als auch der Elternprozess machen nach dem fork mit derif-Anweisung weiter. Im Elternprozess hat newpid einen von 0 verschiedenen Wert, sodassdie Ausführung im else-Teil der if-Anweisung fortfährt. Im Kindprozess hat newpid denWert 0, sodass dort die Funktion child() aufgerufen wird. Die exit-Anweisung os.exit(0) inder child-Funktion ist notwendig, da sonst der Kindprozess in den Elternprozess zurück-kehren würde, und zwar zur input-Anweisung.

import os

def child():print('A new child ', os.getpid( ))os._exit(0)

def parent():while True:

newpid = os.fork()if newpid == 0:

child()else:

pids = (os.getpid(), newpid)print("parent: %d, child: %d" % pids)

if input( ) == 'q': break

parent()

Starten wir dieses Programm, erhalten wir folgende Ausgaben:

bernd@saturn:~/bodenseo/python/beispiele$ python3 fork.pyparent: 5023, child: 5024A new child 5024qbernd@saturn:~/bodenseo/python/beispiele$

Das Diagramm 22.2 veranschaulicht nochmals, was genau beim Forking passiert:

Im nächsten Skript erzeugen wir drei Kindprozesse, in denen wir jeweils vier print-Ausgaben im Sekundenabstand absetzen:

import os, time

def counter(count):for i in range(count):

time.sleep(1)print('%s. count of [%s]' % (i, os.getpid()))

Page 269: 3446435476_Pytho

22.2 Fork in Python 257

BILD 22.2 Forking

for i in range(3):pid = os.fork()if pid == 0:

counter(4)os._exit(0)

else:print('Another process spawned: %d' % pid)

print('Exit of parent process')

Die Ausgabe sieht wie folgt aus:

bernd@saturn:~/bodenseo/python/beispiele$ python3 fork_counter.pyAnother process spawned: 6428Another process spawned: 6429Another process spawned: 6430Exit of parent processbernd@saturn:~/bodenseo/python/beispiele$ 0. count of [6428]0. count of [6429]0. count of [6430]1. count of [6428]1. count of [6429]1. count of [6430]2. count of [6429]2. count of [6428]2. count of [6430]

Page 270: 3446435476_Pytho

258 22 Forks

3. count of [6429]3. count of [6428]3. count of [6430]

bernd@saturn:~/bodenseo/python/beispiele$

Page 271: 3446435476_Pytho

23 Das Modul „pickle”

23.1 Daten sichern mit pickle.dump

BILD 23.1 Daten „eingurken”

Beim Programmieren kommt es natürlich immer wiedermal vor, dass man in die Klemme gerät, aber bei Pythonist das wahrscheinlich – hoffen wir – seltener als in ande-ren Sprachen der Fall. In die Klemme geraten heißt im Eng-lischen „to get oneself into a pickle”. Aber „to pickle” be-deutet eigentlich einlegen oder pökeln, z.B. saure Gurken.Aber was hat nun das Pökeln von sauren Gurken mit Py-thon zu tun? Na ja, ganz einfach: Python hat ein Modul, das„pickle” heißt, und mit dem kann man Python-Objekte ge-wissermaßen einlegen, um sie später, also in anderen Sit-zungen oder anderen Programmläufen, wieder zu benut-zen. Aber jetzt wollen wir wieder seriös werden: Mit demModul pickle (dt. einlegen) lassen sich Objekte serialisiertabspeichern, und zwar so, dass sie später wieder deseriali-siert werden können. Dazu dient die Methode dump, die inihrer allgemeinen Syntax wie folgt aussieht:

dump(obj, file, protocol=None, *, fix_imports=True) -> None

dump() schreibt eine „gepickelte” Darstellung des Objekts „obj” in das Dateiobjekt file. Dasoptionale Argument für das Protokollargument „protocol” steuert die Art der Ausgabe:

Protokollversion Beschreibung

0 ist die ursprüngliche Ablageart des Pickle-Moduls, d.h. vor Python3. Dabeihandelt es sich um ein für Menschen lesbares Format. Dieser Modus istabwärts kompatibel mit früheren Python-Versionen.

1 benutzt das alte Binärformat. Dieser Modus ist ebenfalls abwärts kompatibelmit früheren Python-Versionen.

2 wurde mit 2.3. eingeführt. Es stellt ein effizienteres „Pickling” zur Verfügung.3 wurde speziell für Python 3.0 entwickelt. Dateien, die in diesem Modus er-

zeugt 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.xempfohlenen Modus. Er stellt auch den Default-Wert dar.

Page 272: 3446435476_Pytho

260 23 Das Modul „pickle”

Wird das Argument „fix_imports” auf True gesetzt und hat „protocol” einen Wert kleinerals 3, wird pickle versuchen, die neuen Python 3.x-Namen auf die alten Modulnamen zusetzen, sodass der Pickle-Datenstrom mit Python 2.x lesbar ist.

Im folgenden Beispiel schreiben wir eine Liste in eine Pickle-Datei:

>>> import pickle>>> cities = ["Bern", "Basel","St. Gallen", "Zürich"]>>> fh = open("data.pkl","wb")>>> pickle.dump(cities,fh)>>> fh.close()

23.2 pickle.loadObjekte, die mit pickle.dump() in eine Datei geschrieben worden sind, können mit derPickle-Methode pickle.load(file) wieder eingelesen werden. pickle.load erkennt automa-tisch, in welchem Format eine Datei erstellt worden ist. Im Folgenden zeigen wir, wie wirdie eben geschriebene Liste in einer anderen Sitzung wieder einlesen können:

$ python3Python 3.2.3 (default, Oct 19 2012, 19:53:57)[GCC 4.7.2] on linux2Type "help", "copyright", "credits" or "license" for more information.>>> import pickle>>> f = open("data.pkl","rb")>>> staedte = pickle.load(f)>>> print(staedte)['Bern', 'Basel', 'St. Gallen', 'Zürich']

Page 273: 3446435476_Pytho

24 Reguläre Ausdrücke

24.1 Ursprünge und Verbreitung

BILD 24.1 Reguläre Ausdrücke

In diesem Abschnitt präsentieren wir ei-ne detaillierte und anschauliche Einfüh-rung in die regulären Ausdrücke ganz all-gemein, aber vor allen Dingen auch unterPython 3.

Der Begriff „Regulärer Ausdruck” kommtaus der Automatentheorie und der Theo-rie der formalen Sprachen, zwei Gebietender theoretischen Informatik. Als Erfinderder regulären Ausdrücke gilt der amerika-nische Mathematiker Stephen Cole Klee-ne, der in den 1950er Jahren reguläre Mengen einführte. In der theoretischen Informatikdienen reguläre Ausdrücke zur formalen Definition einer Sprachfamilie mit bestimmtenEigenschaften, die sogenannten regulären Sprachen. Zu jedem regulären Ausdruck exis-tiert ein endlicher Automat, der die von dem Ausdruck spezifizierte Sprache akzeptiert.

In Programmiersprachen werden reguläre Ausdrücken meist zur Filterung von Texten oderTextstrings genutzt, d.h. sie erlauben einem zu prüfen, ob ein Text oder ein String zu einemRA „matcht”, d.h. zutrifft, übereinstimmt oder passt.

RA finden auch Verwendung, um Textersetzungen durchzuführen, die recht komplex seinkönnen.

Einen äußerst interessanten Aspekt von regulären Ausdrücken möchten wir nicht uner-wähnt lassen: Die Syntax der regulären Ausdrücke ist in allen Programmiersprachen undSkriptsprachen gleich, also z.B. in Python, Perl, Java, SED oder AWK. Außerdem werden sievon vielen Texteditoren wie zum Beispiel dem vi benutzt.

24.2 StringvergleicheEin einfacher Sonderfall von regulären Ausdrücken stellt die Suche nach einem String ineinem anderen String dar. Dazu kann man statt regulärer Ausdrücke natürlich auch den

Page 274: 3446435476_Pytho

262 24 Reguläre Ausdrücke

„in”-Operator benutzen, den wir im Abschnitt über sequentielle Datentypen bereits ken-nengelernt haben.

>>> s = "Reguläre Ausdrücke einfach erklärt!">>> "einfach" in sTrue>>>

Im obigen Beispiel wurde geprüft, ob das Wort„einfach” im String s vorkommt.

Im Folgenden zeigen wir Schritt für Schritt, wie die Stringvergleiche durchgeführt werden.

In dem String s = "xaababcbcd"

soll geprüft werden, ob der Substring sub = "abc"

vorkommt. Übrigens handelt es sich bei einem Substring bereits um einen regulären Aus-druck, wenn auch einen besonders einfachen.

Zuerst wird geprüft, ob die ersten Positionen übereinstimmen, also s[0] == sub[0]. Dies istin unserem Beispiel nicht erfüllt, was wir durch die Farbe Rot kenntlich machen:

Nun wird geprüft, ob gilt s[1:4] == sub. Dazu wird erst geprüft, ob sub[0] == s[1] erfüllt ist.Dies gilt, was wir mit der Farbe Grün kenntlich machen. Dann wird weiter verglichen, abers[2] ist ungleich sub[1]:

Nun vergleichen wir s[2:5] mit sub. In diesem Fall stimmen sogar die ersten beiden Positio-nen des Substrings überein.

Für den nächsten Schritte benötigen wir wohl keine Erklärung mehr:

Page 275: 3446435476_Pytho

24.3 Überlappungen und Teilstrings 263

Im nächsten Schritt kann eine vollständige Übereinstimmung gefunden werden, denn esgilt s[4:7] == sub

24.3 Überlappungen und TeilstringsMit regulären Ausdrücken können prinzipiell zwei Aufgaben bewerkstelligt werden:

1. Überlappungen: Feststellen, ob solch ein String vollständig durch einen regulären Aus-druck überlappen lässt. Zum Beispiel werden in Kontaktformularen im Internet regulä-re Ausdrücke dazu verwendet, die Gültigkeit von E-Mail-Adressen oder Postleitzahlenzu überprüfen.

2. Teilstringsuche: In einem String wird geprüft, ob es einen Teilstring gibt, der zu einemgegebenen regulären Ausdruck passt.

24.4 Das re-ModulReguläre Ausdrücke gehören nicht zum Grundumfang von Python, d.h. sie sind nicht builtin. Um in Python mit regulären Ausdrücken arbeiten zu können, muss man erst das Modulre importieren. re ist eine Standardbibliothek, die zahlreiche Funktionen und Methodenzum Arbeiten mit regulären Ausdrücken zur Verfügung stellt.

Beginnen wir mit der wohl wichtigsten Funktion: search

Syntax:

re.search(pattern, string, flags=0)

Mit dieser Funktion wird ein String „string” durchsucht, ob der reguläre Ausdruck „pattern”darin vorkommt. Gibt es eine Übereinstimmung, d.h. also einen Teilstring in „string”, aufden der reguläre Ausdruck „pattern” passt, dann gibt die Funktion search ein MatchObjectzurück. Falls „pattern” nicht passt, wird None zurückgegeben.

Ein regulärer Ausdruck entspricht einem String in Python. Es empfiehlt sich aber, am bes-ten immer einen raw-String zu verwenden, da es sonst häufig zu Problemen kommt, wennman einen Rückwärtsschrägstrich (Backslash) verwendet. Das Problem liegt darin, dassPython Rückwärtsschrägstriche bereits auswertet, bevor es einen String an das re-Modulübergibt.

„\b” wird in regulären Ausdrücken dazu verwendet, Wortgrenzen zu bezeichnen. Benutztman „\b” aber in einem String, wird er von Python als Backspace (also Rückwärtsschritt)interpretiert. Man sieht dies am besten im folgenden Beispiel:

Der reguläre Ausdruck „\b(\w+).*\b\1\b” wird Ihnen zum jetzigen Zeitpunkt noch wieHieroglyphen vorkommen, aber er dient dazu, in einem String festzustellen, ob ein Wortmehr als einmal vorkommt.

Page 276: 3446435476_Pytho

264 24 Reguläre Ausdrücke

Benutzt man in search einen normalen String statt eines Raw-Strings, funktioniert es nicht:

>>> import re>>> t = "Manchmal ist ein Wort doch kein Wort.">>> x = re.search("\b(\w+).*\b\1\b", t)>>> print(x)None>>> x = re.search(r"\b(\w+).*\b\1\b", t)>>> print(x)<_sre.SRE_Match object at 0x8e89220>

Das liegt daran, dass Python, bevor es einen String an search übergibt, diesen interpre-tiert, d.h. Escape-Sequenzen werden aufgelöst. „\b” steht zum Beispiel für einen Backspace(Rückwärtsschritt):

>>> s = "abc\bde\bf">>> print(s)abdf

Die Buchstaben „c” und „e” wurden gewissermaßen aus dem String gelöscht. So werdenauch alle Zeichen vor einem „\b” aus einem regulären Ausdruck gelöscht, bevor dieser anre.search übergeben wird. Dies geschieht aber nicht, wenn wir einen raw-String verwen-den.

Ohne Raw-Strings müsste man jeden Backslash mit einem Backslash versehen, um die Se-quenz, also z.B. „\b”, vor einer Interpretation durch den Python-Interpreter zu schützen1:

>>> s = "abc\\bde\\bf">>> print(s)abc\bde\bf

24.5 Matching-ProblemBevor wir mit der Besprechung der Syntax der regulären Ausdrücke beginnen, wollen wirnoch auf ein allgemeines Problem bei regulären Ausdrücken zu sprechen kommen. WennSie wollen, können Sie diesen Abschnitt momentan überspringen, um ihn später durchzu-arbeiten.

Jeder String – also Strings ohne spezielle Zeichen mit Sonderbedeutungen – ist bereits einregulärer Ausdruck. So ist beispielsweise r"cat" ein regulärer Ausdruck. Er gehört zu denwohl am einfachsten zu verstehenden Ausdrücken, denn er enthält keinerlei Metazeichen(Funktionszeichen) mit Sonderbedeutungen. Unser Beispielausdruck r"cat" matcht bei-spielsweise die folgende Zeichenkette: „A cat and a rat can’t be friends.”

Interessanterweise zeigt sich schon in diesem ersten Beispiel ein „beliebter” Fehler. Eigent-lich will man Strings matchen, in denen das Wort cat vorkommt. Dies gelingt auch, aber

1 englisch: escape

Page 277: 3446435476_Pytho

24.5 Matching-Problem 265

BILD 24.2 „under” und„over” matching

man erhält auch beispielsweise „cats”, was möglicherweisenoch erwünscht ist. Schlimmer sind jedoch eine ganze Men-ge zusätzlicher Wörter, in denen die Buchstabenfolge „cat”als Teilstring vorkommt, also auch Wörter wie „education”,„communicate”, „falsification”, „ramifications”, „cattle” undviele andere. Dies ist ein Fall von „over matching”, d.h. wirerhalten positive Ergebnisse, die nicht gewünscht sind.

Wir haben dies im nebenstehenden Diagramm mengenmä-ßig veranschaulicht. Der dunkelgrüne Kreis C entspricht derMenge, die wir gerne erkennen wollen, aber wir erkennen dieMenge O (blauer Kreis). C ist eine Teilmenge von O. In die-sem Diagramm erkennt man auch eine Menge U (hellgrünerKreis), die eine Teilmenge unserer gewünschten Menge C ist. Dies ist ein Fall von „undermatching”. Ein Beispiel dafür erhalten wir, wenn wir versuchen, unseren regulären Aus-druck zu verbessern. Wir könnten auf die Idee kommen, vor und hinter dem Wort cat imregulären Ausdruck ein Leerzeichen einzufügen, also r" cat ". Durch diese Änderungwürden wir nicht mehr auf Wörter wie „education”, „falsification” und „ramification” her-einfallen. Wie sieht es aber mit einem String "The cat, called Oscar, climbed on the roof."aus? Er wird nun als nicht mehr passend eingestuft.

Diese Problematik wird im Folgenden noch klarer. Zunächst wollen wir uns jedoch malanschauen, wie man reguläre Ausdrücke in Python überprüfen kann. Dann sind wir auchin der Lage, die folgenden Beispiele direkt in Python nachzuvollziehen.

>>> import re>>> x = re.search("cat","A cat and a rat can't be friends.")>>> print x<_sre.SRE_Match object at 0x7fd4bf238238>>>> x = re.search("cow","A cat and a rat can't be friends.")>>> print xNone

Im vorigen Beispiel haben wir die Methode search aus dem re-Modul verwendet. Es istwohl die am wichtigsten und am häufigsten benutzte Methode. Mittels search(expr,s) wirdein String s nach dem Vorkommen eines Teilstrings untersucht, der auf den regulären Aus-druck expr passt. Der erste gefundene Teilstring wird zurückgeliefert. Wenn wir das Ergeb-nis ausdrucken, sehen wir, dass im positiven Fall ein sogenanntes Match-Objekt zurückge-geben wird, während im negativen Fall ein „None” zurückgegeben wird. Mit diesem Wissenkann man reguläre Ausdrücke bereits in einem Python-Skript nutzen, ohne Näheres überdie Match-Objekte zu wissen:

>>> if re.search("cat","A cat and a rat can't be friends."):... print "Der Ausdruck hat gepasst"... else:... print "Der Ausdruck hat nicht gepasst"...Der Ausdruck hat gepasst>>> if re.search("cow","A cat and a rat can't be friends."):... print "Der Ausdruck hat gepasst"... else:

Page 278: 3446435476_Pytho

266 24 Reguläre Ausdrücke

... print "Der Ausdruck hat nicht gepasst"

...Der Ausdruck hat nicht gepasst

24.6 Syntax der regulären Ausdrücke24.6.1 Beliebiges Zeichen

Nehmen wir an, dass wir im vorigen Beispiel nicht daran interessiert waren, das Wort catzu finden, sondern dreibuchstabige Wörter, die mit „at” enden: Die regulären Ausdrückebieten einen Metacharacter „.”, der als Platzhalter für ein beliebiges Zeichen steht. Denregulären Ausdruck könnte man so formulieren:

r" .at "

Der reguläre Ausdruck matcht nun durch Leerzeichen isolierte dreibuchstabige Wörter, diemit „at” enden. Dieser Ausdruck passt nun auch auf Wörter wie „rat”, „cat”, „bat”, „eat”,„sat” und andere.

Aber was ist, wenn im Text ein „@at” oder „3at” stünde? Die matchen dann auch, und wirhaben wieder ein over matching. Eine Lösung, um dies zu umgehen, lernen wir im nunfolgenden Unterabschnitt unserer Einführung kennen.

Im Folgenden sehen wir obigen regulären Ausdruck in einer Anwendung:

>>> import re>>> for i in cat_and_more:... if re.search(r".at", i):... print("")... else:... print("no match")...matchedmatchedmatchedmatchedno matchno match>>>

24.7 ZeichenauswahlDurch eckige Klammern „[” und „]” können wir eine Zeichenauswahl definieren. Der Aus-druck in eckigen Klammern steht dann für genau ein Zeichen aus dieser Auswahl. Betrach-ten wir den folgenden regulären Ausdruck:

r"M[ae][iy]er"

Page 279: 3446435476_Pytho

24.8 Endliche Automaten 267

Dieser Ausdruck passt auf vier verschiedene Schreibweisen des häufigen deutschen Fami-liennamens. Dem großen M kann ein kleines „a” oder ein kleines „e” folgen, dann muss ein„i” oder „y” folgen, zum Abschluss dann „er”.

Statt einzelner Buchstaben, wie im vorigen Beispiel die Auswahl zwischen „e” oder „a” (inRE-Notation [ea]), benötigt man sehr häufig die Auswahl zwischen ganzen Zeichenklas-sen, also zum Beispiel eine Ziffer zwischen „0” und „5” oder ein Buchstabe zwischen „a”und „e”. Dafür gibt es bei der Notation für reguläre Ausdrücke ein reserviertes Sonder-zeichen innerhalb der Zeichenauswahl, nämlich den Bindestrich „-”. [a-e] ist eine abge-kürzte Schreibweise für [abcde] oder [0-5] steht für [012345]. Der Vorteil beim Schreibenwird sofort ersichtlich, wenn man die Zeichenauswahl „ein beliebiger Großbuchstabe” no-tieren will. Man kann [ABCDEFGHIJKLMNOPQRSTUVWXYZ] oder [A-Z] schreiben. Wendas noch nicht überzeugt: Wie sieht es mit der Zeichenauswahl „ein beliebiger Klein- oderGroßbuchstabe” aus? [A-Za-z] Die umständliche Alternative überlassen wir der geneigtenLeserin oder dem geneigten Leser :-)

Aber es gibt noch eine Besonderheit mit dem Bindestrich, den wir benutzt hatten, um denAnfang und das Ende einer Zeichenklasse zu markieren. Der Bindestrich hat nur eine Son-derbedeutung, wenn er innerhalb von eckigen Klammern steht, und auch dann nur, wenner nicht unmittelbar nach der öffnenden eckigen Klammer oder vor der schließenden ecki-gen Klammer positioniert ist. So bezeichnet der Ausdruck [-az nur die Auswahl zwischenden drei Zeichen „-”, „a” und „z”, aber keine anderen Zeichen. Das Gleiche gilt für [az-].

Frage:

Welche Zeichenklasse wird durch [-a-z] beschrieben?

Antwort:

Das Zeichen „-”, weil es am Anfang direkt nach der öffnenden Klammer steht, und alleZeichen zwischen „a” bis „z”, also das ganze Alphabet der kleinen Buchstaben und der Bin-destrich.

Das einzige andere Metazeichen innerhalb von eckigen Klammern ist das Caret-Zeichen(auch Textcursor oder Einschaltungszeichen genannt). Wenn es direkt hinter der öffnen-den eckigen Klammer positioniert ist, dann negiert es die Auswahl. [^0-9] bezeichnet dieAuswahl „irgendein Zeichen, aber keine Ziffer”. Die Position des Caret-Zeichen innerhalbder eckigen Klammern ist entscheidend. Wenn es nicht als erstes Zeichen steht, dann hates keine spezielle Bedeutung und bezeichnet nur sich selbst. [^abc] bedeutet alles außer„a”, „b” oder „c”. [a^bc] bedeutet entweder ein „a”, „b”, „c” oder ein „^”.

24.8 Endliche AutomatenDieser Abschnitt ist nicht zum weiteren Verständnis für die praktische Anwendung vonregulären Ausdrücken notwendig. Sie können ihn also getrost überspringen.

Ein endlicher Automat wird manchmal auch als Zustandsmaschine oder Zustandsautomatbezeichnet.2 Bei einem endlichen Automaten handelt es sich um ein Modell bestehend aus

2 englisch: finite state machine (FSM)

Page 280: 3446435476_Pytho

268 24 Reguläre Ausdrücke

Zuständen, Zustandsübergängen und Aktionen. Man bezeichnet ihn als endlich, weil dieZahl der Zustände, die er annehmen kann, endlich ist.

BILD 24.3 Endlicher Automat

Wir wollen hier nur ein Beispiel lie-fern: einen endlichen Automaten zum Ak-zeptieren der Meyer/Meier/Mayer/Maier-Varianten.

Vereinfachung im Diagramm: Eigentlichmüsste es im Startknoten einen Zeiger ge-ben, der wieder auf den Startknoten zu-rückzeigt. Das heißt, man bleibt solangeauf dem Startknoten, wie man Zeichenliest, die von „M” verschieden sind. Von al-len anderen Knoten müsste es auch einen Pfeil zum Startknoten zurück geben, wenn mankein Zeichen liest, was auf den ausgehenden Pfeilen vorhanden ist.

Wie bereits eingangs gesagt, kann man das eben über endliche Automaten Gesagte getrostignorieren, wenn man es nicht versteht. Es ist nicht wesentlich für die Anwendung vonregulären Ausdrücken.

24.9 Vordefinierte ZeichenklassenIm Prinzip kann es sehr mühsam werden, bestimmte Zeichenklassen auf die bisherige Artund Weise zu konstruieren. Ein gutes Beispiel hierfür ist sicherlich die Zeichenklasse, dieeinen gültigen Wortbuchstaben definiert. Dies sind alle Klein- und Großbuchstaben, alleZiffern und der Unterstrich "_". Das entspricht der folgenden Zeichenklasse

r"[a-zA-Z0-9_]"

Deshalb gibt es für häufig vorkommende Zeichenklassen vordefinierte Kürzel:

BILD 24.4 Wortbegrenzer

Die vordefinierten Zeichenklassen \b und\B der vorigen Übersicht werden häu-fig nicht richtig oder gar falsch verstan-den. Während die anderen Klassen einzel-ne Zeichen matchen (so matcht beispiels-weise \w unter anderem „a”, „b”, „m”, „3”usw.), matchen sie aber keine Zeichen. Siematchen leere Strings in Abhängigkeit vonderen Nachbarschaft, d.h. es hängt davon ab, welches Zeichen vor und nach dem leerenString steht. \b passt zum Beispiel, wenn ein leerer String zwischen einem \W und einem\w steht oder umgekehrt, wenn er zwischen \w und \W steht. \B bezeichnet wie üblichdas Komplement, das bedeutet, es werden leere Strings zwischen \W und \W und leereStrings zwischen \w und \w gematcht. Diesen Sachverhalt haben wir im nebenstehendenDiagramm veranschaulicht.

Page 281: 3446435476_Pytho

24.10 Anfang und Ende eines Strings 269

TABELLE 24.1 Vordefinierte Zeichenklassen

Kürzel Bedeutung\d Eine Ziffer, entspricht [0-9].\D das Komplement von \d. Also alle Zeichen außer den Ziffern, entspricht

der Klassennotation [^0-9].\s Ein Whitespace, also Leerzeichen, Tabs, Newlines usw., entspricht der

Klasse [ \t \n \r \f \v ].\S Das Komplement von \s. Also alles außer Whitespace, entspricht

[^ \t \n \r \f \v ].\w Alphanumerisches Zeichen plus Unterstrich, also [a-zA-Z0-9_]. Wenn

die LOCALE gesetzt ist, matcht es auch noch die speziellen Zeichen derLOCALE, also z.B. die Umlaute.

\W Das Komplement von \w.\b Passt auf den leeren String, aber nur, wenn dieser am Anfang oder Ende

eines Strings ist.\B Passt wie \b auf den leeren String, aber nur, wenn dieser nicht am

Anfang oder Ende eines Strings ist.\\ Ein Backslash.

24.10 Anfang und Ende eines StringsWie wir bereits ausgeführt hatten, ist der Ausdruck r"M[ae][iy]er" in der Lage, verschie-dene Schreibweisen des Namen Meyer zu matchen. Dabei spielt es keine Rolle, ob sich derName am Anfang, im Inneren oder am Ende des Strings befindet.

>>> import re>>> line = "He is a German called Mayer.">>> if re.search(r"M[ae][iy]er",line):... print("I found one!")...I found one!>>>

Aber wie sieht es aus, wenn wir nur Vorkommen des Namens direkt am Anfang einesStrings suchen, d.h. dass der String unmittelbar mit dem „M” des Namens beginnt? Dasre-Modul von Python stellt zwei Funktionen zum Matchen von regulären Ausdrücken zurVerfügung. Eine der beiden haben wir bereits kennengelernt, d.h. die Funktion search().Die andere Funktion hat unserer Meinung nach einen irreführenden Namen, denn sieheißt match(). Irreführend deshalb, weil match(re_str, s) nur prüft, ob eine Übereinstim-mung am Anfang des Strings vorliegt. Aber egal wie, match() ist eine Lösung auf unsereFragestellung, wie wir im folgenden Beispiel sehen:

>>> import re>>> s1 = "Mayer is a very common Name">>> s2 = "He is called Meyer but he isn't German.">>> print(re.search(r"M[ae][iy]er", s1))

Page 282: 3446435476_Pytho

270 24 Reguläre Ausdrücke

<_sre.SRE_Match object at 0xb724a0c8>>>> print(re.search(r"M[ae][iy]er", s2))<_sre.SRE_Match object at 0xb724a0c8>>>> print(re.match(r"M[ae][iy]er", s1))<_sre.SRE_Match object at 0xb724a0c8>>>> print(re.match(r"M[ae][iy]er", s2))None>>>

Auf diese Art können wir zwar den Anfang eines Strings matchen, aber diese Methode funk-tioniert nur in Python. Aber die Syntax der regulären Ausdrücke stellt eine andere Möglich-keit zur Verfügung.

Das Zeichen „^” (Textcursor, Einfügezeichen) stellt sicher, dass der nachfolgende reguläreAusdruck nur direkt auf den Anfang des Strings angewendet wird, d.h. der reguläre Aus-druck mit einem führenden „^” muss also auf den Anfang des Strings passen. Außer imMULTILINE-Modus, dann kann der Ausdruck immer auf ein Newline-Zeichen folgen.

>>> import re>>> s1 = "Mayer is a very common Name">>> s2 = "He is called Meyer but he isn't German.">>> print re.search(r"^M[ae][iy]er", s1)<_sre.SRE_Match object at 0x7fc59c5f26b0>>>> print re.search(r"^M[ae][iy]er", s2)None

Aber was passiert, wenn wir die beiden Strings s1 und s2 auf nachfolgende Art zusammen-fügen?

s = s2 + "\n" + s1

Der String beginnt nicht mit einem Maier, egal in welcher Schreibweise.

>>> s = s2 + "\n" + s1>>> print re.search(r"^M[ae][iy]er", s)None>>>

Der Ausdruck konnte nicht matchen. Aber der Name kommt nach einem Newline-Zeichenvor. Deshalb ändert sich das Ergebnis, wenn wir den MULTILINE-Modus zuschalten:

>>> s = s2 + "\n" + s1>>> print(re.search(r"^M[ae][iy]er", s, re.MULTILINE))<_sre.SRE_Match object at 0xb724a100>>>> print(re.search(r"^M[ae][iy]er", s, re.M))<_sre.SRE_Match object at 0xb724a100>>>> print(re.match(r"^M[ae][iy]er", s, re.M))None>>>

Das vorige Beispiel zeigt auch, dass der Multiline-Modus keinen Einfluss auf die match-Methode hat. match() prüft nie etwas anderes als den Anfang des Strings unabhängig da-von, ob man sich im Multiline-Modus befindet oder nicht.

Page 283: 3446435476_Pytho

24.11 Optionale Teile 271

Damit haben wir die Prüfung für den Anfang eines Strings erledigt. Die Prüfung, ob einregulärer Ausdruck auf das Ende eines Strings passt, sieht ähnlich aus. Dazu erhält das„$”-Zeichen eine Sonderbedeutung. Wird ein regulärer Ausdruck von einem „$”-Zeichengefolgt, dann muss der Ausdruck auf das Ende des Strings passen, d.h. es darf kein weite-res Zeichen zwischen dem regulären Ausdruck und dem Newline des Strings stehen. Wirdemonstrieren dies im folgenden Beispiel:

>>> print(re.search(r"Python\.$","I like Python."))<_sre.SRE_Match object at 0xb724a138>>>> print(re.search(r"Python\.$","I like Python and Perl."))None>>> print(re.search(r"Python\.$","I like Python.\nSome prefer Java or

Perl."))None>>> print(re.search(r"Python\.$","I like Python.\nSome prefer Java or

Perl.", re.M))<_sre.SRE_Match object at 0xb724a138>>>>

24.11 Optionale TeileFalls Sie denken, dass wir bereits alle Schreibweisen der Namen Mayer und Co. erfasst hät-ten, dann irren Sie sich. Es gibt noch weitere Varianten in vielen anderen Schreibweisen.So gibt es insbesondere in Bayern die Variante, in der das „e” vor dem „r” „verloren” gegan-gen ist. Dadurch erhalten also noch vier weitere Schreibweisen: ["Mayr", "Meyr", "Meir","Mair"] zusätzlich zu unserer alten Liste ["Mayer", "Meyer", "Meier", "Maier"].

Wenn wir nun versuchen, einen passenden regulären Ausdruck zu konstruieren, fällt unsauf, dass uns noch etwas fehlt: Wie können wir sagen: „e kann, aber muss nicht vor-kommen”? Dafür hat man in der Syntax der regulären Ausdrücke dem Fragezeichen eineSonderbedeutung verpasst. Der Ausdruck „e?” bedeutet gerade, was wir wollen, also „derBuchstabe e kann, aber muss nicht vorkommen”.

Unser finaler Mayer-Erkenner sieht nun wie folgt aus:

r"M[ae][iy]e?r"

Ein Fragezeichen kann auch hinter einer runden Klammer stehen. Dann bedeutet das, dassder komplette Unterausdruck innerhalb der Klammern vorkommen kann, aber nicht vor-kommen muss. Mit dem folgenden Ausdruck können wir Teilstrings mit „Feb 2011” oder„February 2011” erkennen:

r"Feb(ruary)? 2011"

Page 284: 3446435476_Pytho

272 24 Reguläre Ausdrücke

24.12 QuantorenMit dem, was wir bisher an syntaktischen Mitteln kennengelernt haben, lassen sich be-stimmte Eigenschaften nicht in regulären Ausdrücken abbilden. Beispielsweise benötigtman immer wieder Möglichkeiten darzustellen, dass man bestimmte Teilausdrücke wie-derholen will. Eine Form von Wiederholung hatten wir gerade eben kennengelernt, dasFragezeichen. Ein Zeichen oder ein in runden Klammern eingeschlossener Teilausdruckwird entweder einmal oder keinmal „wiederholt”.

Außerdem hatten wir zu Beginn dieser Einführung einen anderen Quantor kennengelernt,ohne dass wir in besonderer Weise auf ihn eingegangen sind. Es handelte sich um denStern-Operator. Folgt ein Stern „*” einem Zeichen oder einem Teilausdruck, dann heißtdies, dass dieses Zeichen oder der Teilausdruck keinmal oder beliebig oft vorkommen oderwiederholt werden darf.

r"[0-9].*"

Der obige Ausdruck passt auf eine beliebige Folge von Ziffern, aber auch auf den leerenString.

r".*"

passt auf eine beliebige Folge von Zeichen und auf den leeren String.

Übung:

Schreiben Sie einen regulären Ausdruck, der Strings matched, die mit einer Folge von Zif-fern – wenigstens einer – beginnen und von einem Leerzeichen gefolgt werden.

Lösung:

r"[0-9][0-9] .*"

So, Sie haben also das Plus-Zeichen verwendet? Das ist super, aber in diesem Fall haben Siewohl gemogelt, indem Sie weitergelesen haben, oder Sie wissen bereits mehr über reguläreAusdrücke als das, was wir bisher in unserem Kurs behandelt haben ,

Also dann, wenn wir bereits beim Plus-Operator sind: Mit dem Plus-Operator kann manauf angenehme Art und Weise die vorige Übung lösen. Im Prinzip funktioniert der Plus-Operator wie der Sternchen-Operator, nur dass der Plus-Operator wenigstens ein Vorkom-men des Zeichens oder Teilausdrucks verlangt.

Lösung unserer Aufgabe mit dem "+Operator:

r"[0-9]+ .*"

Aber auch mit Plus- und Sternchen-Operator fehlt noch etwas Wichtiges: Wir wollen in be-stimmten Situation die exakte Anzahl der Wiederholungen oder eine minimale oder maxi-male Anzahl von Wiederholungen angeben können. Nehmen wir an, dass wir Adresszeilenvon Briefumschlägen in der Schweiz lesen wollen. Also die Zeile, in der die Postleitzahl undder Ortsname steht, d.h. eine vierstellige Postleitzahl, gefolgt von einem Leerzeichen unddem Ortsnamen. + bzw. * sind zu unspezifisch für diesen Fall, und die folgende Lösung istsicherlich zu umständlich:

r"^[0-9][0-9][0-9][0-9] [A-Za-z]+"

Page 285: 3446435476_Pytho

24.12 Quantoren 273

Glücklicherweise bietet die Syntax der regulären Ausdrücke eine optimale Lösung:

r"^[0-9]{4} [A-Za-z]*"

Nun wollen wir unseren regulären Ausdruck noch weiter verbessern. Nehmen wir an, dasses keine Stadt oder keinen Ort in der Schweiz gibt, deren Name aus weniger als drei Buch-staben besteht. Diesen Umstand können wir mit [A-Za-z][3,} beschreiben. Nun wollenwir auch noch Briefe mit erfassen, die nach Deutschland gehen. Postleitzahlen haben be-kanntlich eine Stelle mehr in Deutschland. [0-9]{4,5} bedeutet, dass wir mindestens 4Ziffern, aber höchstens 5 erwarten:

r"^[0-9]{4,5} [A-Z][a-z]{2,}"

Allgemein gilt: {min, max}: mindestens min-Mal und höchsten max-Mal. {, max} ist ei-ne abgekürzte Schreibweise für {0,to}, und {min,} ist eine Abkürzung für „höchstensmin-Mal, aber keine Beschränkung nach oben”.

Ein praktisches Beispiel in Python

Bevor wir fortfahren, möchten wir eine kleine praktische Übung mit regulären Ausdrückenin Python einschieben.

Dazu haben wir ein Telefonbuch3 der Simpsons. Genau, DIE SIMPSONS, die aus der be-rühmten amerikanischen Serie.

Allison Neu 555-8396Bob Newhall 555-4344C. Montgomery Burns 555-0001C. Montgomery Burns 555-0113Canine College 555-7201Canine Therapy Institute 555-2849Cathy Neu 555-2362City of New York Parking Violation Bureau 555-BOOTDr. Julius Hibbert 555-3642Dr. Nick Riviera 555-NICKEarn Cash For Your Teeth 555-6312Family Therapy Center 555-HUGSHomer Jay Simpson (Plow King episode) 555-3223Homer Jay Simpson (work) 555-7334Jack Neu 555-7666Jeb Neu 555-5543Jennifer Neu 555-3652Ken Neu 555-8752Lionel Putz 555-5299

In dieser Liste befinden sich Leute, die mit Nachnamen „Neu” heißen. Die selbst auferlegteAufgabe besteht nun darin, diejenigen Leute zu finden, die den Namen Neu führen undderen Vorname mit einem „J” beginnt. Dazu schreiben wir ein Python-Skript, das dieseZeile einliest und Zeile für Zeile bearbeitet:

import re

3 Das gesamte Telefonbuch finden Sie in unserem Programm- und Beispielverzeichnis.

Page 286: 3446435476_Pytho

274 24 Reguläre Ausdrücke

fh = open("simpsons_phone_book.txt")for line in fh:

if re.search(r"J.*Neu",line):print line.rstrip()

fh.close()

Startet man das Programm, erhält man folgende Ausgabe:

$ python3 phone_numbers.py Jack Neu 555-7666Jeb Neu 555-5543Jennifer Neu 555-3652

24.13 Gruppierungen undRückwärtsreferenzen

Ausdrücke lassen sich, wie bereits erklärt, mit runden Klammern „(” und „)” zusammen-fassen. Die gefundenen Übereinstimmungen der Gruppierungen werden von Python ab-gespeichert. Dadurch wird deren Wiederverwendung im gleichen regulären Ausdruck anspäterer Stelle ermöglicht. Dies bezeichnet man als Rückwärtsreferenzen (engl. back refe-rences). \n (n = 1, 2, 3, ... ) bezeichnet die n-te Gruppierung. Bevor wir jedoch mit Rück-wärtsreferenzen weitermachen, wollen wir noch einen Paragraphen über Match-Objekteeinfügen, die wir im Folgenden benötigen.

24.13.1 Match-Objekte

Bisher waren wir immer nur daran interessiert, ob ein Ausdruck gepasst hatte oder nicht.Wir nutzten die Tatsache, dass Python oder genauer die Methode re.search() ein Match-Objekt zurückliefert, wenn der reguläre Ausdruck gepasst hat, und ansonsten nur ein No-ne. Uns interessierte bisher nicht, was gepasst hatte, also welcher Teilstring. Eine andereInformation wäre, wo der Match im String stattfand, also die Start- und die Endposition.

Ein match-Objekt enthält unter allem die Methoden group(), span(), start() und end(), dieman im folgenden Beispiel im selbsterklärenden Einsatz sieht:

>>> import re>>> mo = re.search("[0-9]+", "Customer number: 232454, Date: February 12,

2013")>>> mo.group()'232454'>>> mo.span()(17, 23)>>> mo.start()17>>> mo.end()23

Page 287: 3446435476_Pytho

24.13 Gruppierungen und Rückwärtsreferenzen 275

>>> mo.span()[0]17>>> mo.span()[1]23>>>

Diese Methoden sind nicht schwierig zu verstehen: span() liefert ein 2er-Tupel zurück, dasden Anfangs- und Endwert des Substrings enthält, auf den der reguläre Ausdruck passt. Fürden Anfangs- und Endwert gibt es noch zwei Funktionen start() und end(), wobei gilt, dassspan()[0] dem Wert von start() und span()[1] dem Wert von end() entspricht.

Wird group() ohne Argumente aufgerufen, liefert es den Substring zurück, der auf den REgepasst hat. Ruft man group mit einem Integer-Argument n auf, liefert es den Substringzurück, auf den die n-te Gruppe gepasst hatte. Man kann group() auch mit mehr als einemInteger-Wert aufrufen, z.B. group(n,m). Dann wird kein String zurückgeliefert, sondern einTupel mit den Werten von group(n) und group(m). Also es gilt (group(n),group(m)) istgleich group(n,m):

>>> import re>>> mo = re.search("([0-9]+).*: (.*)", "Customer number: 232454, Date:

February 12, 2013")>>> mo.group()'232454, Date: February 12, 2013'>>> mo.group(1)'232454'>>> mo.group(2)'February 12, 2013'>>> mo.group(1,2)('232454', 'February 12, 2013')

Ein sehr intuitives Beispiel stellt das Lesen von korrespondierenden schließenden Tags vonXML oder HTML dar. In einer Datei (z.B. „tags.txt”) steht folgender Inhalt:

<composer>Wolfgang Amadeus Mozart</composer><author>Samuel Beckett</author><city>London</city>

Wir möchten diesen Text automatisch in folgendes Format umschreiben:

composer: Wolfgang Amadeus Mozartauthor: Samuel Beckettcity: London

Dies lässt sich mittels Python und regulären Ausdrücken mit folgendem Skript realisieren.Der reguläre Ausdruck funktioniert wie folgt: Er versucht erst das Symbol „<” zu finden.Danach liest er eine Gruppe von Kleinbuchstaben, bis er auf das Größerzeichen „>” stößt.Alles, was zwischen „<” und „>” steht, wird in einer Rückwärtsreferenz (back reference)gespeichert, und zwar unter \1. Zuerst enthält \1 den Wert „composer”: Nachdem der Aus-druck das erste „>” erreicht hat, wird der reguläre Ausdruck so weiter ausgeführt, als hätteer von Anfang an „<composer>(.*)</composer>” gelautet.

Das zugehörige Python-Skript:

Page 288: 3446435476_Pytho

276 24 Reguläre Ausdrücke

import refh = open("tags.txt")for i in fh:

res = re.search(r"<([a-z]+)>(.*)</\1>",i)print(res.group(1) + ": " + res.group(2))

Wenn es mehr als ein Klammerpaar (runde Klammern) innerhalb eines regulären Aus-drucks gibt, dann sind die Rückwärtsreferenzen in der Reihenfolge der Klammern durch-nummeriert: \1, \2, \3, ...

Übung:

Im nächsten Beispiel werden drei Rückwärtsreferenzen benutzt. Gegeben ist eine Telefon-liste der Simpsons. Nicht jeder Eintrag enthält eine Telefonnummer, aber wenn eine Tele-fonnummer existiert, dann steht sie am Anfang des Strings. Dann folgt, getrennt durch einLeerzeichen, der Nachname. Durch ein Komma getrennt folgen dann Nachnamen. Die Lis-te soll in der folgenden Form ausgegeben werden, d.h. zuerst der vollständige Name unddann die Telefonnummer:

$ python3 simpsons_phone_book.pyAllison Neu 555-8396C. Montgomery BurnsLionel Putz 555-5299Homer Jay Simpson 555-7334

Das folgende Python-Programm löst die Aufgabe:

import re

l = ["555-8396 Neu, Allison","Burns, C. Montgomery","555-5299 Putz, Lionel","555-7334 Simpson, Homer Jay"]

for entry in l:res = re.search(r"([0-9-]*)\s*([A-Za-z]+),\s+(.*)", entry)print(res.group(3) + " " + res.group(2) + " " + res.group(1))

24.14 Umfangreiche ÜbungIn dieser Übung geht es darum, die Informationen aus zwei Listen, d.h. Dateien, unter Be-nutzung von regulären Ausdrücken zusammenbringen. Die erste Datei beinhaltet nahezu15.000 Zeilen mit Postleitzahlen mit den zugehörigen Städtenamen sowie weiteren Infor-mationen. Es folgen ein paar willkürlich ausgewählte Zeilen zur Verdeutlichung und zurFindung von regulären Ausdrücken:

90402,"Nürnberg",9564,"Nürnberg, Stadt",9,"Bayern"80331,"München",9184,"München",9,"Bayern"

Page 289: 3446435476_Pytho

24.14 Umfangreiche Übung 277

86150,"Augsburg",9761,"Augsburg, Stadt",9,"Bayern"89073,"Ulm",8421,"Ulm",8,"Baden-Württemberg"70173,"Stuttgart",8111,"Stuttgart",8,"Baden-Württemberg"71032,"Böblingen",8115,"Böblingen",8,"Baden-Württemberg"78462,"Konstanz",8335,"Konstanz",8,"Baden-Württemberg"88662,"Überlingen",8435,"Bodenseekreis",8,"Baden-Württemberg"88045,"Friedrichshafen",8435,"Bodenseekreis",8,"Baden-Württemberg"79098,"Freiburg im Breisgau",8311,"Freiburg im Breisgau",8,"Baden-Wü

rttemberg"76131,"Karlsruhe",8212,"Karlsruhe",8,"Baden-Württemberg"68159,"Mannheim",8222,"Mannheim",8,"Baden-Württemberg"67059,"Ludwigshafen am Rhein",7314,"Ludwigshafen am Rhein, Stadt",7,"

Rheinland-Pfalz"66740,"Saarlouis",10044,"Saarlouis",10,"Saarland"66111,"Saarbrücken",10041,"Stadtverband Saarbrücken",10,"Saarland"78048,"Villingen-Schwenningen",8326,"Schwarzwald-Baar-Kreis",8,"Baden-Wü

rttemberg"78532,"Tuttlingen",8327,"Tuttlingen",8,"Baden-Württemberg"60311,"Frankfurt am Main",6412,"Frankfurt am Main, Stadt",6,"Hessen"50667,"Köln",5315,"Köln, Stadt",5,"Nordrhein-Westfalen"

Die andere Datei enthält eine Liste der 19 größten deutschen Städte. Jede Zeile enthält diePosition der Stadt, den Namen der Stadt, die Einwohnerzahl und das Bundesland. Was je-doch fehlt, ist die zugehörige Postleitzahl:

1. Berlin 3.382.169 Berlin2. Hamburg 1.715.392 Hamburg3. München 1.210.223 Bayern4. Köln 962.884 Nordrhein-Westfalen5. Frankfurt am Main 646.550 Hessen6. Essen 595.243 Nordrhein-Westfalen7. Dortmund 588.994 Nordrhein-Westfalen8. Stuttgart 583.874 Baden-Württemberg9. Düsseldorf 569.364 Nordrhein-Westfalen10. Bremen 539.403 Bremen11. Hannover 515.001 Niedersachsen12. Duisburg 514.915 Nordrhein-Westfalen13. Leipzig 493.208 Sachsen14. Nürnberg 488.400 Bayern15. Dresden 477.807 Sachsen16. Bochum 391.147 Nordrhein-Westfalen17. Wuppertal 366.434 Nordrhein-Westfalen18. Bielefeld 321.758 Nordrhein-Westfalen19. Mannheim 306.729 Baden-Württemberg

Die Aufgabe besteht nun darin, die 19 größten Städte zusammen mit ihren Postleitzahlenauszugeben. Die Dateien befinden sich in unserem Beispielverzeichnis unter den Namenlargest_cities_germany.txt und post_codes_germany.txt.

import reimport codecs

Page 290: 3446435476_Pytho

278 24 Reguläre Ausdrücke

fh_post_codes = codecs.open("post_codes_germany.txt", encoding="iso-8859-1")

PLZ = {}for line in fh_post_codes:

(post_code, city, rest) = line.split(",",2)PLZ[city.strip("\"")] = post_code

fh_largest_cities = codecs.open("largest_cities_germany.txt", encoding="utf-8")

for line in fh_largest_cities:re_obj = re.search(r"^[0-9]{1,2}\.\s+([\wÄÖÜäöüß\s]+\w)\s+[0-9]",line

)city = re_obj.group(1)print(city, PLZ[city])

Die Ausgabe für obiges Programm sieht wie folgt aus:

$ python3 largest_cities_postcode.py14199 Berlin22769 Hamburg81929 München51149 Köln65936 Frankfurt am Main45359 Essen44388 Dortmund70629 Stuttgart40629 Düsseldorf28779 Bremen30669 Hannover47279 Duisburg4357 Leipzig90491 Nürnberg1478 Dresden44894 Bochum42399 Wuppertal33739 Bielefeld68309 Mannheim

24.15 findallPython oder besser das Modul re stellt noch eine weitere großartige Methode zur Verfü-gung, die sich Perl- und Java-Programmierer vergeblich wünschen:

re.findall(pattern, str[, flags])

findall liefert alle Übereinstimmungen des regulären Ausdrucks „pattern” im String „str” alsListe zurück. (Zur Erinnerung: search() und match() liefern nur die erste Übereinstimmung

Page 291: 3446435476_Pytho

24.16 Alternativen 279

zurück.) Der String „str” wird von links nach rechts gescannt, und die Einträge der Listeentsprechen diesem Arbeitsablauf.

>>> import re>>> t="A fat cat doesn't eat oat but a rat eats bats.">>> mo = re.findall("[force]at", t)>>> print(mo)['fat', 'cat', 'eat', 'oat', 'rat', 'eat']>>>

Falls eine oder mehrere Gruppen in dem regulären Ausdruck vorkommen, wird eine Listemit Tupels der einzelnen Gruppenergebnisse zurückgeliefert:

>>> import re>>> items = re.findall("[0-9]+.*: .*", "Customer number: 232454, Date:

January 22, 2013")>>> print(items)['232454, Date: January 22, 2013']>>> items = re.findall("([0-9]+).*: (.*)", "Customer number: 232454, Date

: January 22, 2013")>>> print(items)[('232454', 'January 22, 2013')]>>>

24.16 AlternativenZu Beginn unseres Kapitels hatten wir Zeichenklassen eingeführt. Zeichenklassen bieteneine Auswahl aus einer Menge von Zeichen. Manchmal benötigt man etwas Analoges fürverschiedene Teilausdrücke. Also die Wahl zwischen verschiedenen Ausdrücken. Dazu ver-wendet man das Symbol „|”, da es sich um ein logisches „Oder” handelt. Im folgendenBeispiel prüfen wir, ob in einem String die Städte London, Paris, Zürich oder Strasbourgvorkommen, und zwar nach einem vorausgegangenen Wort „destination”:

>>> import re>>> str = "The destination is London!">>> mo = re.search(r"destination.*(London|Paris|Zurich|Strasbourg)",str)>>> if mo: print mo.group()...destination is London>>>

Wer das letzte Beispiel für zu künstlich und nicht praxisnah genug hält, für den oder diehaben wir hier ein weiteres Beispiel. Nehmen wir an, wir wollen unsere E-Mails filtern. Wirmöchten die gesamte Korrespondenz mit Guido van Rossum, dem Schöpfer und Designervon Python, finden. Der folgende reguläre Ausdruck dürfte dann recht hilfreich sein:

r"(^To:|^From:) (Guido|van Rossum)"

Page 292: 3446435476_Pytho

280 24 Reguläre Ausdrücke

Dieser Ausdruck passt auf alle Zeilen, die entweder mit „To:” oder mit „From:” beginnenund von einem Leerzeichen gefolgt werden, denen dann entweder der Vorname „Guido”oder der Nachname „van Rossum” folgt.

24.17 Kompilierung von regulärenAusdrücken

Falls man denselben regulären Ausdruck mehrmals in seinem Skript verwenden will, istes eine gute Idee, den Ausdruck zu kompilieren. Die allgemeine Syntax der compile()-Funktion:

re.compile(patter[, flags])

compile lieferte ein regex-Objekt zurück, das man später zum Suchen und Ersetzen ver-wenden kann. Das Verhalten des Ausdrucks kann man mit Flag-Werten modifizieren.

Schauen wir uns dies in einem Beispiel an:

>>> import re>>> ce = re.compile(r"01+")>>> ce<_sre.SRE_Pattern object at 0xb72ac440>>>>

Auf dem SRE_Pattern-Objekt ce kann man nun die Methode search anwenden, die nureinen String braucht, auf dem versucht wird, das zuvor kompilierte Pattern zu matchen:

>>> x = ce.search("Eine Binärzahl: 00111")>>> x<_sre.SRE_Match object at 0xb71af0c8>>>> x.span()(17, 21)>>> x.group()'0111'>>>

Das SRE_Pattern-Objekt kennt auch eine Methode findall4, wie wir im folgenden Beispielsehen:

>>> import re>>> ce = re.compile(r"01+")>>> x = ce.search("Eine Binärzahl: 00111 und noch eine 01011")>>> x.findall()Traceback (most recent call last):File "<stdin>", line 1, in <module>

AttributeError: '_sre.SRE_Match' object has no attribute 'findall'

4 ebenso wie match, split sub und andere

Page 293: 3446435476_Pytho

24.17 Kompilierung von regulären Ausdrücken 281

TABELLE 24.2 Flags für compile

Flag Beschreibungre.I, re.IGNORECASE Groß- und Kleinschreibung wird nicht mehr

unterschieden.re.L, re.LOCALE Das Verhalten von bestimmten Zeichenklassen wie z.B.

\w , \W , \b ,\s , \S wird von der eingestelltenSprachumgebung (locale) abhängig gemacht.

re.M, re.MULTILINE ^ und $ passen defaultmäßig nur auf den Anfang und dasEnde eines Strings. Mit diesem Flag passen sie auch imInnern eines Strings vor und nach einem Newline „\n”.

re.S, re.DOTALL „.” passt dann auf alle Zeichen plus dem Newline „\n”.re.U, re.UNICODE \w , \W , \b , \B , \d , \D , \s , \S werden

von Unicode-Einstellungen abhängig.re.X, re.VERBOSE Ermöglicht „wortreiche” (verbose) reguläre Ausdrücke,

d.h. Leerzeichen werden ignoriert. Das bedeutet, dassLeerzeichen, Tabs, Wagenrücklauf „\c” usw. nicht alssolche gematcht werden. Wenn man ein Leerzeichen imVerbose-Modus matchen will, muss man es mittelsBackslash schützen (escape) oder es in eineZeichenklasse packen. # wird auch ignoriert, außer wenndieses Zeichen in einer Zeichenklasse oder hinter einemBackslash steht. Alles hinter einem „#” wird bis zumEnde einer Zeile als Kommentar ignoriert.

>>> x = ce.findall("Eine Binärzahl: 00111 und noch eine 01011")>>> x['0111', '01', '011']>>>

Kompilierte reguläre Ausdrücke sparen in der Regel nicht viel Rechenzeit, weil Python so-wieso automatisch reguläre Ausdrücke kompiliert und zwischenspeichert, auch wenn mansie mit re.search() oder re.match() aufruft. Ein guter Grund dafür, sie zu verwenden, bestehtdarin, die Definition und die Benutzung von regulären Ausdrücken zu trennen.

Page 294: 3446435476_Pytho

282 24 Reguläre Ausdrücke

24.18 Aufspalten eines Strings mit oderohne regulären Ausdruck

24.18.1 split-Methode der String-Klasse

BILD 24.5 Splitting Strings

Es gibt eine String-Methode split(), mit derenHilfe man einen String in Teilstrings aufspaltenkann.

str.split([sep[, maxsplit]])

Wenn das optionale Argument von split fehltoder None ist, werden alle Teilstrings, die ausLeerräumen (Whitespaces) bestehen, als Sepa-ratoren benutzt. Den zweiten Parameter werdenwir später besprechen.Wir demonstrieren dieses Verhalten mit einemZitat von Abraham Lincoln:

>>> law_courses = "Let reverence for the laws be breathed by everyAmerican mother to the lisping babe that prattles on her lap. Let itbe taught in schools, in seminaries, and in colleges. Let it bewritten in primers, spelling books, and in almanacs. Let it bepreached from the pulpit, proclaimed in legislative halls, andenforced in the courts of justice. And, in short, let it become thepolitical religion of the nation."

>>> law_courses.split()['Let', 'reverence', 'for', 'the', 'laws', 'be', 'breathed', 'by', 'every

', 'American', 'mother', 'to', 'the', 'lisping', 'babe', 'that', 'prattles', 'on', 'her', 'lap.', 'Let', 'it', 'be', 'taught', 'in', 'schools,', 'in', 'seminaries,', 'and', 'in', 'colleges.', 'Let', 'it', 'be', 'written', 'in', 'primers,', 'spelling', 'books,', 'and', 'in', 'almanacs.', 'Let', 'it', 'be', 'preached', 'from', 'the', 'pulpit,', 'proclaimed', 'in', 'legislative', 'halls,', 'and', 'enforced', 'in', 'the', 'courts', 'of', 'justice.', 'And,', 'in', 'short,', 'let', 'it', 'become', 'the', 'political', 'religion', 'of','the', 'nation.']

>>>

Wir schauen uns nun einen String an, der von seinem Aufbau von Excel oder von Calc (Ope-nOffice) kommen könnte. In unserem vorigen Beispiel hatten wir gesehen, dass standard-mäßig Leerräume (Whitespaces) als Separatoren genommen werden. Im folgenden klei-nen Beispiel wollen wir das Semikolon als Separator nehmen. Dazu müssen wir lediglich„;” als Argument beim Aufruf übergeben:

>>> line = "James;Miller;teacher;Perl">>> line.split(";")['James', 'Miller', 'teacher', 'Perl']

Page 295: 3446435476_Pytho

24.18 Aufspalten eines Strings mit oder ohne regulären Ausdruck 283

Wie bereits erwähnt hat die Methode split() noch einen weiteren optionalen Parameter:maxsplit.

Falls maxsplit angegeben wird, dann wird der String an maximal „maxsplit” Separatorenzerlegt, d.h. die Ergebnisliste besteht aus höchstens „maxsplit + 1” Elementen. Wir de-monstrieren die Wirkungsweise von maxsplit mit einer Definition des Begriffs Mammonaus dem „Devil’s Dictionary” von Ambrose Bierce:

>>> mammon = "The god of the world's leading religion. The chief templeis in the holy city of New York."

>>> mammon.split(" ",3)['The', 'god', 'of', "the world's leading religion. The chief temple is

in the holy city of New York."]

Wir benutzten im vorigen Beispiel ein Leerzeichen als Separator, was ein Problem darstel-len kann, wenn mehrere Leerzeichen (irgendwelche Leerzeichen, also auch Tabs) hinter-einander stehen. In diesem Fall wird split() nach jedem Leerzeichen den String aufsplitten,und dadurch erhalten wir in unserer Ergebnisliste leere Strings und Strings, die nur ein „\t”enthalten:

>>> mammon = "The god \t of the world's leading religion. The chieftemple is in the holy city of New York."

>>> mammon.split(" ",5)['The', 'god', '', '\t', 'of', "the world's leading religion. The chief

temple is in the holy city of New York."]>>>

Die leeren Strings können wir verhindern, indem wir None als erstes Argument, also fürden Separator, benutzen. Dann arbeitet split() mit der Standardeinstellung, also so, alswenn man keinen Trennstring angegeben hat:

>>> mammon.split(None,5)['The', 'god', 'of', 'the', "world's", 'leading religion. The chief

temple is in the holy city of New York.']

24.18.2 split-Methode des re-Moduls

In den meisten Fällen genügt die split-Methode aus dem str-Modul voll und ganz. Aber wiesieht es beispielsweise aus, wenn man nur die reinen Wörter eines Texts herausfiltern will?Wenn man also alles herausfiltern will, was nicht Buchstaben entspricht. Dann benötigenwir eine Split-Funktion, die die Angabe von regulären Ausdrücken zur Definition von Se-paratoren zulässt. Das Modul re bietet eine solche Methode, die ebenfalls split() heißt. Wirerläutern die Arbeitsweise mit re.split() in einem kurzen Text, dem Beginn der Metamor-phosen von Ovid:

>>> import re>>> metamorphoses = "OF bodies chang'd to various forms, I sing: Ye Gods,

from whom these miracles did spring, Inspire my numbers withcoelestial heat;"

Page 296: 3446435476_Pytho

284 24 Reguläre Ausdrücke

>>> re.split("\W+",metamorphoses)['OF', 'bodies', 'chang', 'd', 'to', 'various', 'forms', 'I', 'sing', 'Ye

', 'Gods', 'from', 'whom', 'these', 'miracles', 'did', 'spring', 'Inspire', 'my', 'numbers', 'with', 'coelestial', 'heat', '']

Seine volle Wirkungskraft entfaltet split im nachfolgenden Beispiel. Als Beispieltext ver-wenden wir eine ironische Definition von Logik, wie sie Ambrose Bierce in seinem berühm-ten „Devil’s Dictionary” vorschlägt:

import re

logik = """Logik: Die Kunst des Denkens und Schlussfolgerns in strenger Übereinstimmung mit den Beschränkungen und Unfähigkeiten desmenschlichen Missverständnisses. Die Grundlage der Logik ist derSyllogismus (logische Schluss), der aus einem Obersatz, einemUntersatz und einer Konklusion (Schlussfolgerung) besteht - somit:

Obersatz: Sechzig Männer können eine Arbeit sechzig mal so schnell machenwie einer.

Untersatz: Ein Mann kann ein Pfostenloch in sechzig Sekunden graben;deswegen:

Konklusion: Sechzig Männer können ein Pfostenloch in einer Sekunde graben."""

definitions = re.split("\w+: ", logik)[1:]

print("Definition von Logik: \n" + definitions[0])print("Beispiel für einen Obersatz: \n" + definitions[1])print("Beispiel für einen Untersatz: \n" + definitions[2])print("Konklusion: \n" + definitions[3])

Wenn man sich den String logik anschaut, sieht man, dass jede Definition bzw. die Beispielefür den Obersatz und den Untersatz sowie die Konklusion durch den jeweiligen Begriff,gefolgt von einem Doppelpunkt, eingeleitet werden. Wir können also als Separator denregulären Ausdruck "\w+:"verwenden.5

Wir haben auf re.split("\w+: ", logik) den Slice-Operator [1:] angewendet, weilder erste Eintrag der Ergebnisliste von re.split("\w+: ", logik) ein leerer String ist,denn vor dem ersten Separator „Logik:” steht kein Zeichen.

Die Ausgabe des obigen Skripts sieht wie folgt aus:

$ python3 split_logics.pyDefinition von Logik:Die Kunst des Denkens und Schlussfolgerns in strenger Übereinstimmung mit

den Beschränkungen und Unfähigkeiten des menschlichen Missverständnisses. Die Grundlage der Logik ist der Syllogismus (logischeSchluss), der aus einem Obersatz, einem Untersatz und einerKonklusion (Schlussfolgerung) besteht - somit:

5 Die Definitionen stammen von Ambrose Bierce, dem Autor von „The Devil’s Dictionary”. Übersetzungstammt von der Webseite „Logik wider alle Vernunft” (http://www.klein-singen.de/logik/) von Bernd Klein.

Page 297: 3446435476_Pytho

24.18 Aufspalten eines Strings mit oder ohne regulären Ausdruck 285

Beispiel für einen Obersatz:Sechzig Männer können eine Arbeit sechzig mal so schnell machen wieeiner.

Beispiel für einen Untersatz:Ein Mann kann ein Pfostenlock in sechzig Sekunden graben; deswegen:

Konklusion:Sechzig Männer können ein Pfostenloch in einer Sekunde graben.

24.18.3 Wörter filtern

Wir wollen nun die Wörter eines Texts herausfiltern, also ohne Satzzeichen und Sonderzei-chen.

Betrachten wir folgenden String aus Shakespeares Hamlet:

>>> import re>>> t = """HORATIO Where, my lord?...... HAMLET In my mind's eye, Horatio....... HORATIO I saw him once; he was a goodly king....... HAMLET He was a man, take him for all in all,... I shall not look upon his like again....... HORATIO My lord, I think I saw him yesternight....... HAMLET Saw? who?...... HORATIO My lord, the king your father....... HAMLET The king my father!"""

Mithilfe von re.split() können wir eine Liste aller Wörter erzeugen:

>>> re.split(r"\W+",t)['HORATIO', 'Where', 'my', 'lord', 'HAMLET', 'In', 'my', 'mind', 's', '

eye', 'Horatio', 'HORATIO', 'I', 'saw', 'him', 'once', 'he', 'was', 'a', 'goodly', 'king', 'HAMLET', 'He', 'was', 'a', 'man', 'take', 'him', 'for', 'all', 'in', 'all', 'I', 'shall', 'not', 'look', 'upon', 'his', 'like', 'again', 'HORATIO', 'My', 'lord', 'I', 'think', 'I', 'saw', 'him', 'yesternight', 'HAMLET', 'Saw', 'who', 'HORATIO', 'My','lord', 'the', 'king', 'your', 'father', 'HAMLET', 'The', 'king', 'my', 'father', '']

Wir können uns auch sehr leicht die Menge der Wörter, also ohne mehrfache Vorkommenerzeugen:

>>> words = { x.lower() for x in re.split(r"\W+",t)}>>> len(words)

Page 298: 3446435476_Pytho

286 24 Reguläre Ausdrücke

36>>> words{'', 'all', 'mind', 'hamlet', 'in', 'saw', 'your', 'again', 'eye', 'for',

'horatio', 'father', 'who', 'take', 'lord', 'he', 'was', 'his', 'shall', 'yesternight', 'upon', 'like', 'goodly', 'not', 'him', 'man','a', 'king', 'look', 'i', 'my', 's', 'the', 'where', 'think', 'once'}

>>>

24.19 Suchen und Ersetzen mit sub

re.sub(regex, replacement, subject)

Jede Übereinstimmung (match) des regulären Ausdrucks „regex” wird durch den String „re-placement” ersetzt.

Beispiel:6

>>> import re>>> str = "yes I said yes I will Yes.">>> res = re.sub("[yY]es","no", str)>>> print resno I said no I will no.

24.20 Aufgaben

1. Aufgabe:Welche der folgenden regulären Ausdrücke (a), . . . (g) passen auf welche Strings von(1), . . . (6):

(1) abc (a) ab+c?(2) ac (b) a?b*c(3) abbb (c) b+c*(4) bbc (d) ^b+c*$(5) aabcd (e) a.+b?c(6) b (f) b{2,}c?

(g) ^a{1,2}b+.?d*

Lösung: Lösungen zu Kapitel 24 (Reguläre Ausdrücke), Seite 405

6 Der im Beispiel verwendete String entspricht den letzten Worten des Romans „Finnegans Wake” von JamesJoyce.

Page 299: 3446435476_Pytho

24.20 Aufgaben 287

2. Aufgabe:Was matcht x(xy)*x?

(1) xx(2) xyxyxyx(3) xxyxy(4) xyxx(5) xxyxyx

Lösung: Lösungen zu Kapitel 24 (Reguläre Ausdrücke), Seite 406

3. Aufgabe:Schreiben Sie einen Ausdruck, der US-Zip-Codes prüft. Ein Zip-Code besteht ent-weder aus 9 Zeichen, z.B. 205000001 oder 5 Ziffern gefolgt von einem Strichpunkt,gefolgt von 4 Ziffern, z.B. 20500-0001 (president), 20500-0002 (first lady). Außerdemist es auch zulässig, dass ein Zip-Code nur aus 5 Ziffern besteht, also z.B. 20500(Washington DC)Lösung: Lösungen zu Kapitel 24 (Reguläre Ausdrücke), Seite 406

4. Aufgabe:Finden Sie heraus, wie viele verschiedene Wörter in dem Roman „Ulysses” von Ja-mes Joyce mit „ship” enden. Sie finden den Roman in unserem Beispielverzeichnisunter dem Namen „ulysses.txt”.Lösung: Lösungen zu Kapitel 24 (Reguläre Ausdrücke), Seite 407

5. Aufgabe:Finden Sie in „Ulysses” alle Wörter, die aus zwei gleichen Teilwörtern bestehen, wiebeispielsweise „dumdum” oder „poohpooh”.Lösung: Lösungen zu Kapitel 24 (Reguläre Ausdrücke), Seite 407

6. Aufgabe:Auch in dieser Aufgabe bleiben wir bei der Weltliteratur. Das längste Wort in „Ulysses”ist übrigens das Wort „Nationalgymnasiummuseumsanatoriumandsuspensoriumsor-dinaryprivatdocent”. Damit hat dieses Wort noch zwei Buchstaben mehr als das Wort„Grundstücksverkehrsgenehmigungszuständigkeitsübertragungsverordnung”.Berechnen Sie die Häufigkeitsverteilungen von Wortlängen in einem Text ohne Be-rücksichtigung von Groß- und Kleinschreibung.

Page 300: 3446435476_Pytho

288 24 Reguläre Ausdrücke

Zur Erläuterung:Besteht der Text nur aus dem String „Ein Wort ist nur ein Wort.”, dann haben wir fol-gende Verteilung:4 Wörter der Länge 32 Wörter der Länge 4Man könnte eine Häufigkeitsverteilung auch auf die verschiedenen Wörter anwenden,dann sieht die Verteilung so aus:1 Wort der Länge 4: „Wort”3 Wörter der Länge 3: „ein”, „nur”, „ist”Lösung: Lösungen zu Kapitel 24 (Reguläre Ausdrücke), Seite 408

Page 301: 3446435476_Pytho

25 lambda, map, filterund reduce

Wenn es nach Guido van Rossum, dem Autor von Python, gegangen wäre, würde diesesKapitel in unserem Buch wahrscheinlich fehlen. Guido van Rossum hatte lambda, redu-ce(), filter() and map() noch nie gemocht und sie bereits 1993 widerwillig in Python auf-genommnen, nachdem er eine Codeerweiterung mit diesen Funktionalitäten von einem,wie er glaubt, Lisp-Hacker erhalten hatte. In Python 3 sollten sie nach seinem Willen ver-schwinden.

Dazu kann man die folgenden Gründe angeben:

■ Es gibt eine gleich mächtige Alternative zu lambda, filter, map und reduce, nämlich dieListen-Abstraktion (englisch: list comprehension).

■ Die Listen-Abstraktion ist klarer und leichter verständlich.

■ Hat man sowohl die Listen-Abstraktion also auch „Lambda und Co.”, dann verletzt diesdas Python-Motto „Es soll (genau) einen offensichtlichen Weg geben, um ein Problem zulösen” („There should be one obvious way to solve a problem”).

25.1 lambda

BILD 25.1 Dateien

Eine anonyme Funktion oder Lambda-Funktion ist eine Funktion,die nicht über einen Namen verfügt. Eine solche Funktion kann des-halb nur über Verweise angesprochen werden.

Der lambda-Operator bietet eine Möglichkeit, anonyme Funktio-nen, also Funktionen ohne Namen, zu schreiben und zu benut-zen. Lambda-Funktionen kommen aus der funktionalen Program-mierung und wurden insbesondere durch die ProgrammierspracheLisp besonders bekannt. Sie können eine beliebige Anzahl von Pa-rametern haben, führen einen Ausdruck aus und liefern den Wertdieses Ausdrucks als Rückgabewert zurück.

Anonyme Funktionen sind insbesondere bei der Anwendung der map-, filter- und reduce-Funktionen besonders vorteilhaft.

Schauen wir uns ein einfaches Beispiel einer lambda-Funktion an.

lambda x: x + 42

Page 302: 3446435476_Pytho

290 25 lambda, map, filter und reduce

Bei dem obigen Beispiel handelt es sich um eine Funktion mit einem Argument „x”, die dieSumme von x und 42 zurückgibt.

Die allgemeine Syntax einer Lambda-Funktion sieht wie folgt aus:

lambda Argumentenliste: Ausdruck

Die Argumentenliste besteht aus einer durch Kommata getrennten Liste von Argumenten,und der nach dem Doppelpunkt stehende Ausdruck ist ein beliebiger Ausdruck, der dieseArgumente benutzt.

Doch kommen wir zurück zu unserem einführenden Beispiel. Wie können wir unsereFunktion

lambda x: x + 42

benutzen?

>>> (lambda x: x + 42)(3)45>>> y = (lambda x: x + 42)(3) - 100>>> print(y)-55>>> for i in range(10):... (lambda x: x + 42)(i)...42434445464748495051

Natürlich kann man mit obigem Code viele beeindrucken, aber man hätte es natürlichauch viel einfacher hinschreiben können:

>>> for i in range(10):... print(42 + i)...42434445464748495051

Page 303: 3446435476_Pytho

25.1 lambda 291

Eine andere Möglichkeit besteht darin, den lambda-Ausdruck einer Variablen zuzuweisen.Mit diesem Namen können wir unsere Funktion jetzt wie eine „gewöhnliche” Funktion fbenutzen:

>>> f42 = lambda x: x + 42>>> f42(4)46

Aber dazu brauchten wir nicht die lambda-Notation. Wir hätten dies auch mit einer nor-malen Funktionsdefinition bewerkstelligen können:

>>> def f42(x):... return x + 42...>>> f42(4)46

Nun kommen wir endlich zu einer sinnvollen Anwendung der lambda-Notation. Wirschreiben eine Funktion mit dem Namen „anwenden”, die eine Funktion als erstes Ar-gument und eine Liste als zweites Argument erwartet. Die Funktion „anwenden” wendetauf jedes Element der übergebenen Liste die als erstes Argument übergebene Funktion an:

>>> def anwenden(f,liste):... ergebnis = []... for element in liste:... ergebnis.append(f(element))... return ergebnis

Wir können nun die Funktion „anwenden” mit unserer f42-Funktion und der Liste der Zah-len von 0 bis 9 aufrufen:

>>> anwenden(f42,range(10))[42, 43, 44, 45, 46, 47, 48, 49, 50, 51]

Wir haben oben den Funktionsnamen als Referenz auf unsere Funktion übergeben. Umdiese Funktion zu benutzen, hatten wir zuerst eine Funktion mit dem – hässlichen – Namenf42 einführen müssen. Die Funktion f42 ist eine „Wegwerffunktion”, die wir nur einmal beidem Funktionsaufruf von „anwenden” benötigen. Sie können sich leicht vorstellen, dasswir gegebenenfalls auch ähnliche Funktionen wie f43, f44 usw. benötigen könnten.

Aus diesem Grund wäre es natürlich bedeutend eleganter, wenn wir diese Funktionen di-rekt an unsere Funktion „anwenden” übergeben könnten, also ohne den Umweg mit derNamensgebung. Dies ist mit der lambda-Notation möglich:

>>> anwenden(lambda x: x + 42,range(10))[42, 43, 44, 45, 46, 47, 48, 49, 50, 51]

Auch solche Anwendungen sind jetzt möglich:

>>> for i in [17, 22,42]:... anwenden(lambda x: x + i, range(10))...

Page 304: 3446435476_Pytho

292 25 lambda, map, filter und reduce

[17, 18, 19, 20, 21, 22, 23, 24, 25, 26][22, 23, 24, 25, 26, 27, 28, 29, 30, 31][42, 43, 44, 45, 46, 47, 48, 49, 50, 51]

In obigem Beispiel haben wir mit Hilfe des Schleifenparameters i drei verschiedene Funk-tionen kreiert.

25.2 mapNachdem wir uns im vorigen Kapitel intensiv mit der Funktion „anwenden” beschäftigthatten, stellt die von Python zur Verfügung gestellte Funktion „map” kein Problem dar. ImPrinzip entspricht „map” unserer Funktion „anwenden”.

map ist eine Funktion mit zwei Argumenten:

r = map(func, seq)

Das erste Argument func ist eine Funktion und das zweite eine Sequenz (z.B. eine Liste oderein Tupel) seq. map wendet die Funktion func auf alle Elemente von seq an und schreibtdie Ergebnisse in ein map object, also ein Iterator. Rufen wir

map(lambda x: x + 42, range(10))

auf, entspricht dies fast unserem Aufruf

anwenden(lambda x: x + 42, range(10))

Damit es ist völlig gleich ist, müssen wir lediglich das map-Objekt noch in eine Liste wan-deln:

>>> list(map(lambda x: x + 42, range(10)))[42, 43, 44, 45, 46, 47, 48, 49, 50, 51]>>> anwenden(lambda x: x + 42, range(10))[42, 43, 44, 45, 46, 47, 48, 49, 50, 51]

In einem weiteren Beispiel wollen wir nun zeigen, welchen großen Vorteil diese Kombina-tion aus lambda und map-Funktion mit sich bringt. Nehmen wir an, dass wir Listen mitTemperaturwerten in Grad Celsius und Grad Fahrenheit haben. Wir möchten diese wech-selseitig in die jeweils andere Temperaturskala wandeln.

Eine Temperatur C in Grad Celsius lässt sich mittels der Formel

9

5∗C +32

in Grad Fahrenheit wandeln, und eine Temperatur F in Grad Fahrenheit lässt sich mittelsder Formel

5

9∗ (F −32)

in Grad Celsius wandeln.

Für das folgende Beispiel „vergessen” wir nun „lambda” und „map” und beschränken unsauf eine „konventionelle” Programmierung:

Page 305: 3446435476_Pytho

25.2 map 293

def fahrenheit(T):return ((9.0 / 5) * T + 32)

def celsius(T):return (5.0 / 9) * ( T - 32 )

temp = (36.5, 37, 37.5,39)

def Fahrenheit2Celsius(F_liste):erg = []for F in F_liste:

erg.append(celsius(F))return erg

def Celsius2Fahrenheit(C_liste):erg = []for C in C_liste:

erg.append(fahrenheit(C))return erg

F_liste = Celsius2Fahrenheit(temp)print(F_liste)

C_liste = Fahrenheit2Celsius(F_liste)print(C_liste)

Unter Benutzung von lambda und map schrumpft unser obiges Codebeispiel in beachtli-cher Weise:

temp = (36.5, 37, 37.5,39)

F_liste = list(map(lambda C: (5.0 / 9) * ( C - 32 ), temp))print(F_liste)

C_liste = map(lambda F: (9.0 / 5) * F + 32, F_liste)print(list(C_liste))

map kann auch gleichzeitig auf mehrere Listen angewendet werden. Dann werden die Ar-gumente entsprechend ihrer Position und der Reihenfolge der Listenargumente entspre-chend mit den Werten aus den Listen versorgt.

>>> a = [1,2,3,4]>>> b = [17,12,11,10]>>> c = [-1,-4,5,9]>>> list(map(lambda x,y:x+y, a,b))[18, 14, 14, 14]>>> list(map(lambda x,y,z:x+y+z, a,b,c))[17, 10, 19, 23]>>> list(map(lambda x,y,z : 2.5*x + 2*y - z, a,b,c))[37.5, 33.0, 24.5, 21.0]>>>

Wir sehen in dem obigen Beispiel, dass der Parameter x seine Werte aus der Liste a, derParameter y seine Werte aus der Liste b und der Parameter z seine Werte aus der Liste cbeziehen.

Page 306: 3446435476_Pytho

294 25 lambda, map, filter und reduce

25.3 Filtern mit „filter”Die Funktion

filter(funktion, liste)

bietet eine elegante Möglichkeit, diejenigen Elemente aus der Liste liste herauszufiltern,für die die Funktion „funktion” True liefert.

Die Funktion

filter(f,iter)

benötigt als erstes Argument eine Funktion f, die Wahrheitswerte liefert. Diese Funktionwird dann auf jedes Argument des Objekts „iter” angewendet. „iter” ist entweder ein se-quentieller Datentyp wie beispielsweise eine Liste oder ein Tupel, oder es ist ein iterierba-res Objekt. Liefert f True für ein x, dann wird x in der Ergebnisliste übernommen, ansonstenwird x nicht übernommen.

>>> fibonacci = [0,1,1,2,3,5,8,13,21,34,55]>>> odd_numbers = list(filter(lambda x: x % 2, fibonacci))>>> print(odd_numbers)[1, 1, 3, 5, 13, 21, 55]>>> even_numbers = list(filter(lambda x: x % 2 == 0, fibonacci))>>> print(even_numbers)[0, 2, 8, 34]>>>>>>>>> # or alternatively:...>>> even_numbers = list(filter(lambda x: x % 2 -1, fibonacci))>>> print(even_numbers)[0, 2, 8, 34]>>>

25.4 reduceEingangs des Kapitels erwähnten wir bereits, dass Guido van Rossum lambda, map, filterund reduce nicht mehr in Python 3 wollte. reduce ist diejenige Funktion, die er am wenigs-ten mag.1 Mit reduce war er erfolgreich. reduce wurde in das Modul „functools” verbanntund gehört damit nicht mehr in den Kern der Sprache.

Die Funktion

reduce(func, seq)

1 „So now reduce(). This is actually the one I’ve always hated most, because, apart from a few examples invol-ving + or *, almost every time I see a reduce() call with a non-trivial function argument, I need to grab penand paper to diagram what’s actually being fed into that function before I understand what the reduce() issupposed to do.”

Page 307: 3446435476_Pytho

25.4 reduce 295

wendet die Funktion func() fortlaufend auf eine Sequenz seq an und liefert einen einzelnenWert zurück. Die Funktion „func” ist eine Funktion, die zwei Argumente erwartet.

Falls seq = [ s1, s2, s3, ... , sn ] ist, funktioniert der Aufruf reduce(func, seq) wie folgt:

■ zuerst wird func auf die beiden ersten Argumente s1 und s2 angewendet. Das Ergebnisersetzt die beiden Elemente s1 und s2.

■ Die Liste sieht damit wie folgt aus: [ func(s1, s2), s3, ... , sn ].

■ Im nächsten Schritt wird func auf func(s1, s2) und s3 angewendet.

■ Die Liste sieht damit wie folgt aus: [ func(func(s1, s2),s3), ... , sn ].

■ Dies wird solange fortgesetzt, bis nur noch ein Element übrig bleibt, d.h. man hat dieListe auf ein Element reduziert.

Für den Fall n = 4 können wir die vorige Erklärung auch wie folgt illustrieren:

Das folgende Beispiel zeigt die Arbeitsweise von reduce() an einem einfachen Beispiel. Ummit reduce zu arbeiten, müssen wir in Python 3 das Modul functools importieren. Das istder wesentliche Unterschied zu früheren Python-Versionen wie zum Beispiel Python 2.7:

>>> import functools>>> functools.reduce(lambda x,y: x+y, [47,11,42,13])113>>>

Im folgenden Beispiel veranschaulichen wir die Arbeitsweise von reduce an einem konkre-ten Beispiel:

>>> reduce(lambda x,y: x+y, [47,11,42,13])113

Im folgenden Diagram sind die Ergebniswerte dargestellt:

Page 308: 3446435476_Pytho

296 25 lambda, map, filter und reduce

25.5 Aufgaben

1. Aufgabe:In einer Buchhandlung findet sich in einem Abrechnungsprogramm in Python eineListe mit Unterlisten mit folgendem Aufbau:

Bestellnummer Buchtitel und Autor Anzahl Einzelpreis

34587 Learning Python, Mark Lutz 4 40.9598762 Programming Python, Mark Lutz 5 56.8077226 Head First Python, Paul Barry 3 32.95

Schreiben Sie ein Programm unter Benutzung von lambda und map, das als Ergebniseine Liste mit Zweier-Tupeln liefert. Jedes Tupel besteht aus der Bestellnummer unddem Produkt aus der Anzahl und dem Einzelpreis. Das Produkt soll jedoch um 10,- €erhöht werden, wenn der Bestellwert unter 100,00 € liegt.Lösung: Lösungen zu Kapitel 25 (lambda, map, filter und reduce), Seite 410

2. Aufgabe:Situation wie in voriger Aufgabe, aber jetzt sehen die Unterlisten wie folgt aus:[Bestellnummer, (Artikel-Nr, Anzahl, Einzelpreis), ... (Artikel-Nr, Anzahl, Einzelpreis) ]Schreiben Sie wieder ein Programm, was eine Liste mit 2-Tupel (Bestellnummer, Ge-samtpreis) liefert.Lösung: Lösungen zu Kapitel 25 (lambda, map, filter und reduce), Seite 410

Page 309: 3446435476_Pytho

26 Listen-Abstraktion/List Comprehension

26.1 Die Alternative zu Lambda und Co.

BILD 26.1Durchfahrt verbotenfür Lambda und Co.

Im vorigen Kapitel über lambda, filter, reduce and map haben wirerfahren, dass Guido van Rossum diese Funktionen nicht mag undsie am liebsten mit der Einführung von Python 3 entfernt hätte.Stattdessen bevorzugt er die Listen-Abstraktion, die meist auch imDeutschen als „List Comprehension” bezeichnet wird. Die Listen-Abstraktion wurde mit der Version 2.0 in Python eingeführt.

Die Listen-Abstraktion, eigentlich auch im Deutschen besser als „ListComprehension” bekannt, ist eine elegante Methode, Mengen inPython zu definieren oder zu erzeugen. Die List Comprehensionkommt der mathematischen Notation von Mengen sehr nahe. In derMathematik definiert man die Quadratzahlen der natürlichen Zahlen beispielsweise als{x2|x ∈N} oder die Menge der komplexen ganzen Zahlen {(x, y)|x ∈Z∧ y ∈Z}.

26.2 Einführung in die Listen-AbstraktionDie Listen-Abstraktion ist eine einfache Methode, um Listen zu erzeugen, und man kannsie als vollständigen Ersatz für den Lambda-Operator sowie die Funktionen map, filter undreduce ansehen. Die Syntax der List Comprehension ist im Allgemeinen leichter verständ-lich.

Allgemein verwendet man sie, um neue Listen zu erzeugen, bei denen jedes Element durchAnwendung verschiedener Operationen aus einem Element einer anderen Liste oder einesIterators erzeugt wird. Ebenso nutzt man es, um Unterlisten zu erzeugen, d.h. es werdennur Elemente aus einer anderen Liste, einem Tupel oder allgemein eines sequentiellen Da-tentyps übernommen, die bestimmte Bedingungen erfüllen.

Schauen wir uns zunächst einmal an, mit welchem Aufwand es verbunden ist, auf tradi-tionellem Wege, also ohne Listen-Abstraktion, solche Aufgaben zu lösen. Im Folgenden er-zeugen wir die Liste der Quadratzahlen für die Zahlen von 1 bis 9:

Page 310: 3446435476_Pytho

298 26 Listen-Abstraktion/List Comprehension

>>> squares = []>>> for i in range(1,10):... squares.append(i**2)...>>> squares[1, 4, 9, 16, 25, 36, 49, 64, 81]

Viel einfacher – das heißt in nur einer Codezeile – lässt sich dies unter Benutzung derListen-Abstraktion bewerkstelligen:

squares = [i**2 for i in range(1,10)]

Nur zur Erinnerung: Unter Benutzung von map und lambda sieht es wie folgt aus:

>>> squares = list(map(lambda x: x**2, range(1,10)))>>> squares[1, 4, 9, 16, 25, 36, 49, 64, 81]

map liefert einen Iterator auf ein list-Objekt zurück, während die Listen-Abstraktion eineListe zurückgibt.

26.3 SyntaxDie Listen-Abstraktion ist von eckigen Klammern umrahmt. Nach der öffnenden Klammersteht ein Ausdruck, der von einem oder mehreren for-Ausdrücken und gegebenenfalls voneiner if-Bedingung gefolgt wird.

Im folgenden Beispiel erzeugen wir alle 2-Tupel mit Zahlen von 1 bis 6, deren Summe gleich7 ist. Dies könnte beispielsweise den Ergebnissen von zwei Würfeln entsprechen:

>>> [ (x,y) for x in range(1,7) for y in range(1,7) if x + y == 7][(1, 6), (2, 5), (3, 4), (4, 3), (5, 2), (6, 1)]

Dies ist äquivalent mit dem folgenden „konventionellen” Code, der keine Listen-Abstraktionnutzt:

>>> t = []>>> for x in range(1,7):... for y in range(1,7):... if x + y == 7:... t.append( (x,y) )...>>> t[(1, 6), (2, 5), (3, 4), (4, 3), (5, 2), (6, 1)]

Page 311: 3446435476_Pytho

26.4 Weitere Beispiele 299

26.4 Weitere BeispieleIm Abschnitt über die map-Funktion hatten wir die Wandlung einer Liste mit Werten inGrad Celsius in Fahrenheit-Werte mit der map-Funktion gelöst. Mit der List Comprehensi-on lässt sich dies sehr einfach lösen:

>>> Celsius = [39.2, 36.5, 37.3, 37.8]>>> Fahrenheit = [ ((float(9)/5)*x + 32) for x in Celsius ]>>> print(Fahrenheit)[102.56, 97.7, 99.14, 100.03999999999999]>>>

Ein pythagoreisches Tripel oder pythagoreisches Zahlentripel besteht aus drei positivenganzen Zahlen mit der Eigenschaft a2 +b2 = c2. Ein solches Tripel wird üblicherweise mit(a, b, c) notiert. Ein Beispiel stellen die Zahlen 3, 4 und 5 dar, also das Tripel (3, 4, 5).

Die pythagoreischen Tripel lassen sich leicht mittels List Comprehension berechnen, wiedas folgende Beispiel zeigt:

>>> [(x,y,z) for x in range(1,30) for y in range(x,30) for z in range(y,30) if x**2 + y**2 == z**2]

[(3, 4, 5), (5, 12, 13), (6, 8, 10), (7, 24, 25), (8, 15, 17), (9, 12,15), (10, 24, 26), (12, 16, 20), (15, 20, 25), (20, 21, 29)]

>>>

Im folgenden Beispiel berechnen wir das Kreuzprodukt aus zwei Mengen:

>>> colours = [ "red", "green", "yellow", "blue" ]>>> things = [ "house", "car", "tree" ]>>> coloured_things = [ (x,y) for x in colours for y in things ]>>> print(coloured_things)[('red', 'house'), ('red', 'car'), ('red', 'tree'), ('green', 'house'),

('green', 'car'), ('green', 'tree'), ('yellow', 'house'), ('yellow','car'), ('yellow', 'tree'), ('blue', 'house'), ('blue', 'car'), ('blue', 'tree')]

>>>

26.5 Die zugrunde liegende IdeeDie Idee, eine Liste quasi algorithmisch zu beschreiben, ist angelehnt an die mathemati-sche Notation von Mengen.

Beispiel:

Die Menge aller ganzen Zahlen, die durch 4 teilbar sind:

{x|x ∈Z ∧x teilbar durch 4}

oder ein anderes Beispiel:

{x|10 <= x <= 100∧x teilbar durch 4}

Page 312: 3446435476_Pytho

300 26 Listen-Abstraktion/List Comprehension

Das letzte Beispiel lässt sich wie folgt in Python schreiben:

>>> [ x for x in range(10,101) if not x % 4][12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80,

84, 88, 92, 96, 100]

Der senkrechte Strich, also „|”, in der mathematischen Notation, wird übrigens im Engli-schen bei der Mengenschreibweise meist als „for” gelesen, also im obigen Beispiel „All xfor which 10 is . . . ”

26.6 Anspruchsvolleres BeispielBerechnung der Primzahlen bis 100 nach dem Sieb des Eratosthenes:

>>> noprimes = [j for i in range(2, 8) for j in range(i*2, 100, i)]>>> primes = [x for x in range(2, 100) if x not in noprimes]>>> print(primes)[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67,

71, 73, 79, 83, 89, 97]>>>

Das vorige Beispiel wollen wir nun in einer etwas allgemeineren Form schreiben, damit esdie Primzahlen für eine beliebige Zahl n berechnet:

>>> from math import sqrt>>> n = 100>>> sqrt_n = int(sqrt(n))>>> no_primes = [j for i in range(2,sqrt_n) for j in range(i*2, n, i)]

Wenn wir uns no_primes ausgeben lassen, erkennen wir, dass es bei der Konstruktion die-ser Liste ein Problem gibt. Diese Liste beinhaltet Doppeleinträge, was die Berechnung derListe der Primzahlen ineffizient werden lässt, da auch alle Doppeleinträge jeweils betrach-tet werden müssen:

>>> no_primes[4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40,

42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74,76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98, 6, 9, 12, 15, 18, 21,24, 27, 30, 33, 36, 39, 42, 45, 48, 51, 54, 57, 60, 63, 66, 69, 72,75, 78, 81, 84, 87, 90, 93, 96, 99, 8, 12, 16, 20, 24, 28, 32, 36,40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80, 84, 88, 92, 96, 10, 15,20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95, 12,18, 24, 30, 36, 42, 48, 54, 60, 66, 72, 78, 84, 90, 96, 14, 21, 28,35, 42, 49, 56, 63, 70, 77, 84, 91, 98, 16, 24, 32, 40, 48, 56, 64,72, 80, 88, 96, 18, 27, 36, 45, 54, 63, 72, 81, 90, 99]

>>>

Am besten wäre es, diese Doppeleinträge bereits gar nicht zu erzeugen.

Page 313: 3446435476_Pytho

26.7 Mengen-Abstraktion 301

26.7 Mengen-AbstraktionDie Mengen-Abstraktion (Set Comprehension) ist analog zur Listen-Abstraktion (List Com-prehension), aber sie liefert – wie der Name andeutet – eine Menge und nicht eine Liste zu-rück. Zur syntaktischen Unterscheidung zur Listen-Abstraktion benutzen wir geschweifteKlammern, also „richtige Mengenklammern”.

Mit der Mengen-Abstraktion sind wir nun in der Lage, das vorige Problem zu lösen, also dieNicht-Primzahl ohne Doppelte zu kreieren:

>>> from math import sqrt>>> n = 100>>> sqrt_n = int(sqrt(n))>>> no_primes = {j for i in range(2,sqrt_n) for j in range(i*2, n, i)}>>> no_primes{4, 6, 8, 9, 10, 12, 14, 15, 16, 18, 20, 21, 22, 24, 25, 26, 27, 28, 30,

32, 33, 34, 35, 36, 38, 39, 40, 42, 44, 45, 46, 48, 49, 50, 51, 52,54, 55, 56, 57, 58, 60, 62, 63, 64, 65, 66, 68, 69, 70, 72, 74, 75,76, 77, 78, 80, 81, 82, 84, 85, 86, 87, 88, 90, 91, 92, 93, 94, 95,96, 98, 99}

>>> primes = {i for i in range(n) if i not in no_primes}>>> print(primes){0, 1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59,

61, 67, 71, 73, 79, 83, 89, 97}>>>

26.8 Rekursive Funktion zur Berechnungder Primzahlen

Eine effizientere Berechnung der Primzahlen bis zu einer natürlichen Zahl n stellen wir inder folgenden rekursiven Implementierung in Python vor. Wir berücksichtigen darin, dassman sich nur die Vielfachen der Primzahlen von 1 bis zur Quadratwurzel von n anschauenmuss:

from math import sqrtdef primes(n):

if n == 0:return []

elif n == 1:return [1]

else:p = primes(int(sqrt(n)))no_p = {j for i in p for j in range(i*2, n, i)}p = {x for x in range(2, n) if x not in no_p}

return p

print(primes(40))

Page 314: 3446435476_Pytho

302 26 Listen-Abstraktion/List Comprehension

26.9 Generator Comprehension/Generatoren-Abstraktion

Die Generatoren-Abstraktion (Generator Comprehension) wurde mit Python 2.6 einge-führt. Die Syntax ist nahezu identisch mit der Listen-Abstraktionen, außer dass sie in rundeKlammern statt in eckige Klammern eingebettet sind. Erzeugt wird ein Iterator statt einerListe. Man hat sie eingeführt, weil häufig keine Liste benötigt wird, sondern nur die einzel-nen Elemente, über die iteriert wird.

Im folgenden Summationsausdruck wird zunächst mittels der Listen-Abstraktion die Listeder Quadratzahlen von 1 bis 10 gebildet. Danach wird die Funktion sum darauf angewen-det, um diese Quadratzahlen zu summieren:

>>> sum([x**2 for x in range(1,11)])385

Für die Funktion sum hätte aber auch ein Generator-Objekt statt einer Liste genügt. Spei-cherplatzschonend kann man obige Summe also auch so berechnen:

>>> sum(x**2 for x in range(1,11))385

Sicherlich haben Sie bemerkt, dass eigentlich im vorigen Ausdruck ein Klammerpaar fehlte.Im Prinzip hätten wir den Ausdruck so schreiben müssen:

>>> sum((x**2 for x in range(1,11)))385

Bei Reduktionsfunktionen, die ein sequentielles Datenobjekt auf einen Wert reduzieren, sowie sum, min und max, brauchen wir also kein separates Klammernpaar. Sonst ist es abernotwendig:

>>> (x**2 for x in range(1,11))<generator object <genexpr> at 0x960e2ac>>>> x**2 for x in range(1,11)File "<stdin>", line 1x**2 for x in range(1,11)

^SyntaxError: invalid syntax

Page 315: 3446435476_Pytho

26.10 Aufgaben 303

26.10 Aufgaben

1. Aufgabe:Mittels einer Listen-Abstraktion soll die Menge aller Paare von natürlichen Zahlen klei-ner als ein gegebenes n gebildet werden unter den Bedingungen, dass die erste derZahlen ohne Rest durch die zweite teilbar ist und die beiden Zahlen nicht identischsind.Lösung: Lösungen zu Kapitel 26 (Listen-Abstraktion/List Comprehension), Seite 411

2. Aufgabe:Wie groß ist die Wahrscheinlichkeit, dass beim Wurf zweier Würfel die Summe derAugenzahlen 7 entspricht?Anmerkung: Wir erwarten natürlich, dass Sie die Aufgabe unter Benutzung von Listen-Abstraktionen lösen.Lösung: Lösungen zu Kapitel 26 (Listen-Abstraktion/List Comprehension), Seite 411

3. Aufgabe:Schreiben Sie eine Funktion, die obige Wahrscheinlichkeit für eine beliebige Summeals Argument berechnet.Lösung: Lösungen zu Kapitel 26 (Listen-Abstraktion/List Comprehension), Seite 412

Page 316: 3446435476_Pytho

27 Generatorenund Iteratoren

27.1 Einführung

BILD 27.1 Generatoren

In der Informatik versteht man unter einem GeneratorProgrammcode (meist eine Routine oder Funktion), derdazu benutzt wird, das Iterationsverhalten einer Schleifezu kontrollieren. Man kann sich ihn wie eine Funktion vor-stellen, die Parameter hat und bei einem Aufruf eine Folgevon Werten zurückliefert, also zum Beispiel ein Array odereine Liste. Allerdings mit dem Unterschied, dass diese Fol-ge von Werten nicht auf einmal zurückgeliefert wird, son-dern einen Wert nach dem anderen. Dies hat zwei Vortei-le: Zum einen spart man Speicherplatz, weil nie eine sol-che Liste mit all ihren Werten erzeugt werden muss, und zum anderen, weil die Schleifeschneller ihre ersten Werte erhält, denn man muss nicht warten, bis die gesamte Folge vonWerten berechnet worden ist. Falls Letzteres überhaupt möglich ist. Generatoren könnenauch potentiell unendliche Folgen von Werten liefern. Ein Generator ist also im Prinzip ei-ne Funktion, die sich wie ein Iterator verhält. Deshalb werden Generatoren, wie wir schoneingangs erwähnt haben, meistens in Schleifen verwendet.

Auch wenn man denken könnte, dass es sich bei Generatoren um neue und moderneSoftware-Technik handelt, wurden sie bereits 1975 in CLU und 1977 in Icon verwendet.

27.2 Iteration in for-SchleifenIteratoren haben wir bereits in for-Schleifen kennengelernt und häufig benutzt, ohne imBesonderen darauf einzugehen. Im folgenden Beispiel durchlaufen wir die Elemente derListe „cities”.

>>> cities = ["Paris","Berlin","London","Vienna"]>>> for city in cities:... print("city: " + city)...city: Paris

Page 317: 3446435476_Pytho

306 27 Generatoren und Iteratoren

city: Berlincity: Londoncity: Vienna>>>

Schauen wir uns einmal an, was Python genau macht, um die for-Schleife auszuführen:Bevor die Schleife gestartet wird, ruft Python die eingebaute1 Funktion iter mit der Lis-te „cities” als Argument auf. Die Funktion iter liefert nun ein Objekt zurück, mit dem esmöglich ist, die Elemente der Liste zu durchlaufen. Der Rückgabewert von iter() ist ein Ele-ment der Klasse „list_iterator”, also ein Iterator. Die eigentliche „Arbeit” erledigt die Me-thode __iter__ der list-Klasse, die von der Funktion iter() aufgerufen wird. Um das weitereVorgehen bei der for-Schleife zu verstehen, nehmen wir an, dass das Resultat von iter()an eine Variable iter_i gebunden wird. Also in unserem Fall iter_i = iter(cities).Nach jedem Schleifendurchlauf wird die next-Funktion mit iter_x als Argument aufgerufenund das Ergebnis der Variablen iter_x zugewiesen. Die Schleife wird solange wiederholt,bis next(iter_x) die Ausnahme StopIteration erzeugt.

Man kann diese Iteration auch „manuell” nachvollziehen:

>>> l = [23,34,56,78]>>> iter_x = iter(l)>>> next(iter_x)23>>> next(iter_x)34>>> next(iter_x)56>>> next(iter_x)78>>> next(iter_x)Traceback (most recent call last):File "<stdin>", line 1, in <module>

StopIteration>>>

Die sequentiellen Basistypen sowie ein Großteil der Klassen der Standardbibliothek vonPython unterstützen Iterationen. Auch der Datentyp Dictionary (dict) unterstützt die Ite-ration. In diesem Fall läuft die Iteration über die Schlüssel des Dictionarys:

>>> capitals = { "France":"Paris", "Netherlands":"Amsterdam", "Germany":"Berlin", "Switzerland":"Bern" }

>>> for country in capitals:... print("The capital city of " + country + " is " + capitals[

country])...The capital city of Switzerland is BernThe capital city of Netherlands is AmsterdamThe capital city of Germany is BerlinThe capital city of France is Paris>>>

1 Häufig wird hierfür der englische Begriff „built-in” benutzt!

Page 318: 3446435476_Pytho

27.3 Generatoren 307

27.3 GeneratorenDoch kommen wir zurück zu unserem ersten Beispiel über Städtenamen. Wir können dar-aus ein erstes Beispiel für einen Generator bilden:

def city_generator():cities = ["Paris","Berlin","London","Vienna"]for city in cities:

yield city

for city in city_generator():print(city)

Trifft der Programmfluss auf die yield-Anweisung, wird der Generator wie bei einer return-Anweisung verlassen, aber Python „merkt” sich, wo der Generator verlassen wird, undgleichzeitig werden auch alle Zustände, also die lokalen Variablen, für einen weiteren Auf-ruf erhalten. Zur Erinnerung: Bei Funktionen werden alle lokalen Variablen beim Verlassengelöscht und bei einem neuen Aufruf wieder neu angelegt.

Ruft man dieses Programm auf, werden die Städtenamen ausgegeben:

$ python3 city_generator.pyParisBerlinLondonVienna

Die for-Schleife endet also, sobald alle Städte im Generator durchlaufen sind. Wir könnenallerdings auch eine Endlosschleife im Generator verwenden.

def city_generator():cities = ["Paris","Berlin","London","Vienna"]while True:

city = cities.pop(0)yield citycities.append(city)

Speichern wir obige Definition in einer Datei „cities_forever.py”, können wir sie interaktivin der Python-Shell testen:

>>> from cities_forever import city_generator>>> x = city_generator()>>> y = city_generator()>>> next(x)'Paris'>>> next(x)'Berlin'>>> next(y)'Paris'>>> next(x)'London'>>> next(x)

Page 319: 3446435476_Pytho

308 27 Generatoren und Iteratoren

'Vienna'>>> next(x)'Paris'>>> next(y)'Berlin'>>>

Wir sehen also, dass wir einen Generator, der eine Endlosschleife beinhaltet, durchaussinnvoll benutzen können. Wir dürfen ihn lediglich nicht in einer Endlosschleife aufru-fen, wie es im folgenden interaktiven Skript geschieht. Wir müssen dann die Schleife mit„Ctrl-C”’ abbrechen:

>>> from cities_forever import city_generator>>> for city in city_generator():... print(city, end=", ")...Paris, Berlin, London, Vienna, Paris, Berlin, London, Vienna, Paris,

Berlin, London, Vienna, Paris, Berlin, London, Vienna, Paris, Berlin,London, Vienna, Paris, Berlin, London, Vienna, Paris, Berlin, London, Vienna, Paris, Berlin, London, Vienna, Paris, Berlin, London,Vienna, Paris, Berlin, London, Vienna, Paris, Berlin, London, Vienna,Paris, Berlin, London, Vienna, Paris, Berlin, London, Vienna, Paris,Berlin, London, Vienna, Paris, Berlin, London, Vienna, Paris, Berlin, London, Vienna, Paris, Berlin, London, Vienna,

Um eine solche Endlosschleife zu vermeiden, könnten wir unsere for-Schleife mit einemZähler und einem break versehen, welchen nach einer bestimmten Anzahl von Iterationenausgeführt wird. Dies könnte beispielsweise so aussehen:

def city_generator():cities = ["Paris","Berlin","London","Vienna"]while True:

city = cities.pop(0)yield citycities.append(city)

if __name__ == "__main__":count = 0for city in city_generator():

if count == 10:print()break

print(city, end=", ")count += 1

Page 320: 3446435476_Pytho

27.4 Beispiele 309

Ein Aufruf liefert dann, wie erwartet, folgende Ausgabe:

$ python3 cities_forever.pyParis, Berlin, London, Vienna, Paris, Berlin, London, Vienna, Paris,

Berlin,$

27.4 Beispiele27.4.1 Permutationen

BILD 27.2 Permutationen von vierFarben auf vier Plätzen

Unter einer Permutation (von lateinisch permu-tare, vertauschen) versteht man in der Kombi-natorik eine Anordnung von Objekten in einerbestimmten Reihenfolge. Wenn Objekte mehr-fach auftreten dürfen, spricht man von einer Per-mutation mit Wiederholung, ansonsten sprichtman von einer Permutation ohne Wiederholung.Im Folgenden wollen wir uns nur mit Permu-tationen ohne Wiederholung beschäftigen undeinen Generator schreiben, der alle Permutatio-nen eines sequentiellen Datenobjekts erzeugt, al-so z.B. von einem String, einer Liste oder einemTupel.

Die Anzahl der Permutationen von n Objekten berechnet sich durch die Fakultät:

n! = n · (n −1) . . .2 ·1

Auch wenn das hier sehr abstrakt klingt, kennt man Permutationen aus dem täglichen Le-ben:

■ Das Mischen der Karten eines Kartenspiels: Der Kartenstapel entspricht einer Permuta-tion aller Karten.

■ Ein Anagramm ist eine Permutation der Buchstaben eines Wortes: Lager, Regal, erlag

def permutations(items):n = len(items)if n==0: yield []else:

for i in range(len(items)):for cc in permutations(items[:i]+items[i+1:]):

yield [items[i]]+cc

print("""permutations for (['r','e','d']""")for p in permutations(['r','e','d']):

Page 321: 3446435476_Pytho

310 27 Generatoren und Iteratoren

print(''.join(p))

print("""permutations of the letters of the string "bin" """)for p in permutations(list("bin")):

print(''.join(p))

Obiges Programm liefert folgende Ausgabe:

$ python3 permutations_generator.pypermutations for (['r','e','d']redrdeerdedrdrederpermutations of the letters of the string "bin"binbniibninbnbinib

27.4.2 Variationen und Kombinationen

Werden bei einer Anordnung nicht alle Objekte für die Anordnung ausgewählt, dannspricht man von einer Variation statt von einer Permutation. Formal heißt dies: Aus nObjekten werden k ohne Zurücklegen und ohne Beachtung der Reihenfolge ausgewählt.

Die Anzahl der Variationen lässt sich wie folgt berechnen:

n!

(n −k)!= n · (n −1) · (n −2)...(n −k +1)

Spielt die Reihenfolge dabei keine Rolle, so bezeichnet man dies als eine Kombination. Beider Lotterie 6 aus 49 handelt es sich beispielsweise um eine Kombination bzw. Selektion.

Die Anzahl berechnet sich wie folgt:(n

k

)=

(n

n −k

)= n!

k ! · (n −k)!= n · (n −1) · (n −2)...(n −k +1)

k !

Wir können die Kombinationen mit folgendem Generator erzeugen:

def selections(items, n):if n==0: yield []else:

for i in range(len(items)):for ss in selections(items, n-1):

if (not items[i] in ss):

Page 322: 3446435476_Pytho

27.4 Beispiele 311

yield [items[i]]+ssprint("6 aus 49")i = 0l = range(1,50)for s in selections(l,6):

print(s)if i > 20:

breakelse:

i = i+1

Obiges Programm liefert folgende Ausgabe:2

$ python selections.py6 aus 49[1, 2, 3, 4, 5, 6][1, 2, 3, 4, 5, 7][1, 2, 3, 4, 5, 8][1, 2, 3, 4, 5, 9][1, 2, 3, 4, 5, 10][1, 2, 3, 4, 5, 11][1, 2, 3, 4, 5, 12][1, 2, 3, 4, 5, 13][1, 2, 3, 4, 5, 14][1, 2, 3, 4, 5, 15][1, 2, 3, 4, 5, 16][1, 2, 3, 4, 5, 17][1, 2, 3, 4, 5, 18][1, 2, 3, 4, 5, 19][1, 2, 3, 4, 5, 20][1, 2, 3, 4, 5, 21][1, 2, 3, 4, 5, 22][1, 2, 3, 4, 5, 23][1, 2, 3, 4, 5, 24][1, 2, 3, 4, 5, 25][1, 2, 3, 4, 5, 26][1, 2, 3, 4, 5, 27]

Falls Sie allerdings nur an einem Lottogewinn interessiert sind, können Sie auch die Funk-tion sample aus dem Modul random benützen.

random.sample(population, k)

population ist eine Sequenz (z.B. Liste) aus der k Elemente ausgewählt und in einer neuenListe zurückgegeben werden. Dabei wird „population” aber nicht verändert.

>>> import random>>> print(random.sample(range(1,50),6))[19, 10, 30, 2, 14, 41]

2 Achtung: Es dauert ziemlich lange, bis die ersten Ergebnisse nach der Ausgabe „6 aus 49” kommen! Es han-delt sich nicht um eine Endlosschleife!

Page 323: 3446435476_Pytho

312 27 Generatoren und Iteratoren

Obige Zahlen können Sie gerne als Tipp für Ihren Lottoschein verwenden. Vergessen Siebitte nicht den Autor dieses Buches, sollten Sie einen Sechser haben ,

27.5 Generatoren zähmenEine deutlich elegantere Methode besteht jedoch darin, einen Generator zu verwenden,der die ersten n Elemente eines anderen Generators ausgibt. Wir schreiben einen solchenGenerator, den wir firstn nennen. Als Parameter erhält er einen Generator g und eine An-zahl n:

def firstn(g, n):for i in range(n):

yield next(g)

def city_generator():cities = ["Paris","Berlin","London","Vienna"]while True:

city = cities.pop(0)yield citycities.append(city)

if __name__ == "__main__":for city in firstn(city_generator(), 10):

print(city, end=", ")

Aber Python wäre nicht Python, wenn es nicht eine noch elegantere Methode gäbe. Manfindet sie in dem Modul itertools. Mit der Methode islice kann man sich die Elemente voneiner Start- bis zu einer Endposition angeben lassen. Im nächsten Beispiel die ersten fünfElemente, die von von city_generator() generiert werden:

>>> import itertools>>> from cities_forever import city_generator>>> itertools.islice(city_generator(),0,5)<itertools.islice object at 0xb713f4b4>>>> cities = itertools.islice(city_generator(),0,5)>>> list(cities)['Paris', 'Berlin', 'London', 'Vienna', 'Paris']

27.6 send-MethodeSeit Python 2.5 ist es mit Generatoren auch möglich, Werte zu empfangen und nicht nurauszugeben. Die yield-Anweisung wurde verändert, sodass man nun Werte an sie „senden”

Page 324: 3446435476_Pytho

27.7 Generator-Ausdrücke 313

kann. Dies geschieht mittels der send-Methode. Im folgenden Beispiel wird ein Wert, wenner an den Generator gesendet wird, in der Variablen i gespeichert:

i = (yield s[count % 6])

Dadurch wird der Wert der Laufvariablen auf einen neuen Wert gesetzt, in unserem Beispielauf 4 gesetzt.

def abc():s = "abcdef"count = 0while True:

i = (yield s[count % 6])if i:

count = ielse:

count += 1

if __name__ == "__main__":x = abc()print(next(x))print(next(x))print(x.send(4))print(next(x))print(next(x))

Wir erhalten folgende Ausgabe:

$ python3 abc_generator_with_send.pyabefa

27.7 Generator-AusdrückeGenerator-Ausdrücke sind Listen-Abstraktionen (List Comprehensions) sehr ähnlich. Statteckigen Klammern werden runde Klammern verwendet. Anders als Listen-Abstraktionenwerden keine vollständigen Listen erzeugt, sondern Generatoren, die ihre Elemente wieGeneratoren eines nach dem anderen zurückliefern.

Den Zusammenhang zwischen Listen-Abstraktion und Generator-Ausdrücken zeigt dasnächste Beispiel:

>>> squares1 = [i**2 for i in range(1,10)]>>> squares1[1, 4, 9, 16, 25, 36, 49, 64, 81]>>> squares2 = (i**2 for i in range(1,10))>>> type(squares2)

Page 325: 3446435476_Pytho

314 27 Generatoren und Iteratoren

<class 'generator'>>>> list(squares2)[1, 4, 9, 16, 25, 36, 49, 64, 81]

Man sieht, dass list(squares2) und squares1 identisch sind.

>>> squares = (i**2 for i in range(1,5))>>> next(squares)1>>> next(squares)4>>> next(squares)9>>> next(squares)16>>> next(squares)Traceback (most recent call last):File "<stdin>", line 1, in <module>

StopIteration

Der obigen Listenausdruck ist identisch mit dem folgenden Generator:

def squares():for i in range(1,5):

yield i ** 2

27.8 Aufgaben

1. Aufgabe:Schreiben Sie einen Generator, der die Fibonacci-Zahlen generiert.Zur Erinnerung:Die n-te Fibonacci-Zahl errechnet sichF(n) = F(n-1) + F(n-2),mit F(0)=0 und F(1)=1

2. Aufgabe:Schreiben Sie einen Generator „round_robin” mit einem Generator g als Argu-ment, der zunächst die Elemente von g auch generiert. Falls g endlich ist, beginnt„round_robin” wieder von vorne mit g, nachdem das letzte Element von g produziertworden ist.Beispiel: Falls g die Elemente 1, 2 und 3 generiert, dann generiert round_robin dieElemente 1,2,3,1,2,3,1,2,3 . . .

Page 326: 3446435476_Pytho

27.8 Aufgaben 315

3. Aufgabe:Schreiben Sie einen Generator „pendulum” mit einem Generator g als Argument, derals Erstes die Elemente von g generiert. Falls g endlich ist, gibt „pendulum” die Ele-mente von g in umgekehrter Reihenfolge aus, nachdem das letzte Element von g pro-duziert worden ist. Wird dann wieder das erste Element erreicht, beginnt „pendulum”wieder von vorne.Beispiel: Falls g die Elemente 1, 2 und 3 generiert, dann generiert „pendulum” dieFolge 1,2,3,3,2,1,1,2,3,3,2,1 . . .

4. Aufgabe:Schreiben Sie einen Generator „pair_sum” mit einem Generator g als Argument.Nehmen wir an, dass g die Werte g1, g2, . . . gn generiert. „pair_sum” generiert danndie Werte g1 + g2, g2 + g3, . . . gn−1 + gn , gn + g1, g1 + g2, . . .

Page 327: 3446435476_Pytho

28 Memoisation

28.1 Bedeutung und Herkunft desBegriffes

BILD 28.1 Gehirn

Der Ausdruck „Memoisation”1 wurde im Jah-re 1968 von Donald Michie geprägt. DerAusdruck basiert auf dem lateinischen Wort„memorandum”, was „das zu Erinnernde”bedeutet. Memoisation ist eine Technik, diein der Programmierung benutzt wird, umden Programmablauf zu beschleunigen. Dieswird erreicht, indem Berechnungsergebnissegespeichert werden, wie zum Beispiel die Er-gebnisse von Funktionsaufrufen. Wird eineFunktion mit den gleichen Parametern auf-gerufen wie bei einem vorigen Aufruf, wirdauf die gespeicherten Ergebnisse zugegrif-fen, statt die Werte neu zu berechnen. In vielen Fällen wird ein einfaches Array für die Er-gebnisspeicherung benutzt, aber es sind auch kompliziertere Strukturen möglich. So kön-nen beispielsweise assoziative Arrays verwendet werden, die in Perl Hashes und in PythonDictionaries genannt werden.

Die Memoisation kann explizit vom Programmierer programmiert werden, aber einige Pro-grammiersprachen wie auch Python stellen Mechanismen zur Verfügung, welche die Im-plementierung der Memoisation erleichtern.

1 Wir haben den englischen Fachbegriff eingedeutscht, da es keinen deutschen Begriff gibt. Im Englischenkommt der Begriff in den Schreibweisen Memoisation und Memoization vor.

Page 328: 3446435476_Pytho

318 28 Memoisation

28.2 Memoisation mitDekorateur-Funktionen

In unserem Kapitel über rekursive Funktionen hatten wir eine iterative und eine rekursiveFunktionsversion zur Berechnung der Fibonacci-Zahlen programmiert. Wir hatten gezeigt,dass die direkte Umsetzung der mathematischen Definition der Fibonacci-Zahlen ein ex-ponentielles Laufzeitverhalten aufzeigt, also eine Funktion wie die folgende:

def fib(n):if n == 0:

return 0elif n == 1:

return 1else:

return fib(n-1) + fib(n-2)

In unserem Kapitel über Rekursion hatten wir auch einen Weg dargestellt, wie man dasLaufzeitverhalten der rekursiven Version verbessern kann. Wir hatten ein Dictionary dazuverwendet, um bereits durch die Funktion berechnete Werte für spätere Aufrufe zu spei-chern. Dadurch wird eine Neuberechnung verhindert. Dabei handelte es sich bereits, ohnedass wir es so genannt hatten, um ein Beispiel, wie man explizit die Technik der Memoisa-tion nutzen kann. Die Methode hatte jedoch einen entscheidenden Nachteil: Die Klarheitund die Schönheit der ursprünglichen rekursiven Methode ging dabei verloren.

Das „Problem” besteht darin, dass wir den Code der rekursiven fib-Funktion ändern bzw.erweitern mussten. Im folgenden Code präsentieren wir eine Lösung, die ohne Änderun-gen der fib-Funktion auskommt. Dadurch wird ihre Klarheit, Schönheit und Lesbarkeitnicht tangiert. Zu diesem Zweck definieren wir eine Funktion, die wir memoize nennen.memoize() benötigt zwei Argumente. Auch die Funktion memoize benutzt ein Dictiona-ry, um die Funktionswerte zu speichern. Obwohl sowohl die Variable „memo” (unser Dic-tionary zum Speichern der Werte) als auch die Funktion „f” lokal innerhalb der Funkti-on memoize sind, werden ihre Werte durch die helper-Funktion bewahrt. Im Englischenbenutzt man statt „bewahrt” das Wort „captured”, was im Deutschen „eingefangen” oder„gefangen genommen” bedeutet. Diese Technik, d.h. das Bewahren von „lokalen” Wertenoder Funktionen über ihren lokalen Geltungsbereich hinaus, wird auch als Closure oderFunktionsabschluss bezeichnet. memoize() liefert als Funktionswert eine Referenz auf diehelper-Funktion zurück. Ein Aufruf der Funktion memoize(fib) liefert eine Funktion zu-rück, die im Ein- und Ausgabeverhalten identisch mit fib() ist, d.h. sie tut, was fib() tut.Zusätzlich speichert sie jedoch die Ergebnisse im memo-Dictionary.

def memoize(f):memo = {}def helper(x):

if x not in memo:memo[x] = f(x)

return memo[x]return helper

Page 329: 3446435476_Pytho

28.3 Memoize in einer Class 319

def fib(n):if n == 0:

return 0elif n == 1:

return 1else:

return fib(n-1) + fib(n-2)

fib = memoize(fib)

print(fib(40))

Als nächsten Punkt kommen wir zu einem Konzept, was als „Dekorateur” (englisch decora-tor: Raumgestalter, Dekorateur) bezeichnet wird. Schauen wir uns die folgende Codezeilean:

fib = memoize(fib)

In diesem Fall spricht man davon, dass die fib-Funktion durch die memoize-Funktion de-koriert wird.

28.3 Memoize in einer ClassWir können das Zwischenspeichern der Ergebnisse auch in einer Klasse statt einer Funkti-on einkapseln. Wir demonstrieren dies im folgenden kleinen Beispiel:

class Memoize:def __init__(self, fn):

self.fn = fnself.memo = {}

def __call__(self, *args):if args not in self.memo:

self.memo[args] = self.fn(*args)return self.memo[args]

Da wir ein Dictionary verwenden, können wir keine veränderlichen2 Argumente verwen-den. Anders ausgedrückt: Die Argumente dürfen nur unveränderlich 3, also z.B. Tupel, sein.

28.4 Dekorateure in PythonEin Dekorateur in Python ist ein aufrufbares Python-Objekt, das benutzt wird, um eineFunktion, eine Methode oder eine Klassendefinition zu modifizieren. Das ursprüngliche

2 englisch: mutable3 englisch: immutable

Page 330: 3446435476_Pytho

320 28 Memoisation

(als das zu modifizierende) Objekt wird dem Dekorateur als Argument zugeführt. DerDekorateur liefert ein modifiziertes Objekt zurück, also beispielsweise eine modifizierteFunktion, Dieses modifizierte Objekt wird an den Namen gebunden, der modifiziert wird.Python-Dekorateure haben eine ähnliche Syntax wie in Java.

Beispiel: Dekorateur für die Memoisation

Wir hatten bereits einen Dekorateur benutzt, ohne es so zu nennen. Bei der memoize-Funktion, im Beispiel am Anfang dieses Kapitels unseres Tutorials, handelt es sich umeinen Dekorateur.

Allerdings hatten wir in diesem Beispiel nicht die spezielle Syntax von Dekorateuren be-nutzt, also das „@”-Zeichen.

Statt der Anweisung

fib = memoize(fib)

können wir vereinfachend folgende Anweisung benutzen:

@memoize

Allerdings muss diese Zeile unmittelbar vor der dekorierten Funktion stehen. Nun könnenwir unsere fib-Funktion dekorieren. Mit Dekorateur sieht unsere Fibonacci-Funktion wiefolgt aus:

def memoize(f):memo = {}def helper(x):

if x not in memo:memo[x] = f(x)

return memo[x]return helper

@memoizedef fib(n):

if n == 0:return 0

elif n == 1:return 1

else:return fib(n-1) + fib(n-2)

#fib = memoize(fib)

print(fib(40))

Page 331: 3446435476_Pytho

28.5 Überprüfung von Argumenten durch Dekorateure 321

28.5 Überprüfung von Argumentendurch Dekorateure

Im Abschnitt über rekursive Funktionen hatten wir die Fakultätsfunktion eingeführt. Dabeiwollten wir die Funktion so einfach wie möglich halten. Hätten wir die Argumente der Fa-kultätsfunktion auf Plausibilität geprüft, dann hätten wir die zugrunde liegende Idee ver-schleiert, und der Kern des Algorithmus wäre nicht mehr so erkennbar gewesen. So darfdie Funktion keinesfalls mit negativen Werten oder Fließkommazahlen, also float-Werten,aufgerufen werden. In beiden Fällen kommt es zu einer endlosen Rekursion, die allerdingsglücklicherweise durch den endlichen Rekursionsstack von Python mit einem RuntimeEr-ror abgebrochen wird:

RuntimeError: maximum recursion depth exceeded in comparison

Das folgende Programm benutzt eine Dekorateur-Funktion, um sicherzustellen, dass essich bei dem verwendeten Argument um eine positive ganze Zahl handelt:

def argument_test_natural_number(f):def helper(x):

if type(x) == int and x > 0:return f(x)

else:raise Exception("Argument is not an integer")

return helper

@argument_test_natural_numberdef faculty(n):

if n == 1:return 1

else:return n * faculty(n-1)

for i in range(1,10):print(i, faculty(i))

print(faculty(-1))

Page 332: 3446435476_Pytho

29 NumPy

29.1 Übersicht

BILD 29.1 Visualisierte Matrix

NumPy und SciPy sind OpenSource-Erweiterungs-module für Python, die schnelle vorkompilierte Funk-tionen für mathematische und numerische Routinenbereitstellen. NumPy („Numeric Python”) dient zurBearbeitung von großen Arrays und Matrizen mit nu-merischen Daten.

SciPy („Scientific Python”) erweitert die Funktionali-tät von NumPy mit nützlichen Funktionen wie Mi-nimierung, Regression, Fourier-Transformation undvielen anderen.

Sowohl NumPy als auch SciPy sind nicht standardmäßig installiert. Bei der Installation soll-te man jedoch beachten, dass man NumPy vor SciPy installieren muss.

NumPy und SciPy stellen eine kostenlose und freie Alternative zu MATLAB dar. Auch wennMATLAB über eine große Anzahl von zusätzlichen Tools verfügt, besteht der große Vorteilvon NumPy und SciPy darin, dass sie auf Python aufbauen und Python eine modernereund allgemeinere Programmiersprache als MATLAB ist.

Anmerkung zum Bild oben rechts: Das Diagramm in dem Bild entspricht der grafischenVeranschaulichung einer Matrix mit 14 Zeilen und 20 Spalten. Es handelt sich dabei um einsogenanntes „Hinton-Diagramm”. Die Größe eines Quadrats entspricht der Größe einesWertes in der Matrix. Die Farbe bestimmt, ob ein Wert positiv oder negativ ist. Also zumBeispiel entspricht die Farbe Rot den negativen Werten und die Farbe Grün in unseremBeispiel den positiven Werten.

NumPy basiert auf dem Python-Modul Numarray, welches seinerseits eine komplette Neu-überarbeitung eines noch älteren Moduls Numeric ist.

NumPy bietet einen großen Zeitvorteil gegenüber Standard-Python.

Wir wollen dies im Folgenden demonstrieren. Zuvor gehen wir noch kurz auf die Funktionarange von numpy ein. Wie range erzeugt arange gleichmäßig verteilte Werte. Diese Wertewerden in einem speziellen NumPy-Datentyp ndarray abgespeichert:

Page 333: 3446435476_Pytho

324 29 NumPy

>>> import numpy as np>>> x = np.arange(1,10)>>> print(x)[1 2 3 4 5 6 7 8 9]>>> type(x)<class 'numpy.ndarray'>>>> # mit Schrittweite:...>>> x = np.arange(1,10,3)>>> print(x)[1 4 7]>>>

Nun wollen wir, wie eingangs gesagt, den Zeitvorteil demonstrieren, den NumPy bietet.Wir berechnen dazu die komponentenweise Addition von zwei Vektoren. Im folgendenProgramm befinden sich zwei Funktionen. In der Funktion trad_version stellen wir dieVektoren als Listen dar, die wir mit der range-Funktion erzeugen. In der Funktion num-py_version benutzen wir das NumPy-Modul. Als Vektoren benutzen wir eindimensionaleArrays der numpy-Klasse ndarray-Array. Diese erzeugen wir mit der Funktion arange.

import timeimport numpy as np

def trad_version():t1 = time.time()X = range(10000000)Y = range(10000000)Z = []for i in range(len(X)):

Z.append(X[i] + Y[i])return time.time() - t1

def numpy_version():t1 = time.time()X = np.arange(10000000)Y = np.arange(10000000)Z = X + Yreturn time.time() - t1

t_trad = trad_version()t_numpy = numpy_version()print("Benötigte Zeit für traditionelle Lösung:")print(t_trad)

print("Benötigte Zeit für Lösung mit NumPy:")print(t_numpy)

print("Lösung mit NumPy " + str(t_trad * 100 / t_numpy) + " % schneller")

Ruft man dieses Programm auf, erhält man folgendes beeindruckende Ergebnis:

Page 334: 3446435476_Pytho

29.2 Arrays in NumPy 325

$ python3 numpy_comparison.pyBenötigte Zeit für traditionelle Lösung:6.556201934814453Benötigte Zeit für Lösung mit NumPy:0.11806702613830566Lösung mit NumPy 5552.949158840005 % schneller

29.2 Arrays in NumPyArrays sind der zentrale Bestandteil von NumPy. Der wesentliche Unterschied zu Listen inPython besteht darin, dass die Elemente eines Arrays alle vom gleichen Typ sein müssen,normalerweise float oder int. Arrays sind bedeutend effizienter und schneller als Listen inPython. Prinzipiell können Arrays wie Listen angesehen werden, allerdings mit den folgen-den Unterschieden:

■ Alle Elemente haben den gleichen Typ, d.h. Integer, Float (Real) oder Komplexe Zahlen.

■ Die Anzahl der Elemente muss a priori bekannt sein, d.h. wenn das Array erzeugt wird.Ein Array kann später nicht mehr verändert werden.

Selbstverständlich sind Arrays in NumPy nicht auf eine Dimension beschränkt. Sie könnenbeliebig dimensional sein.

Erzeugung eines Arrays aus einer Liste:

>>> import numpy as np>>> x = np.array([42,47,11], int)>>> xarray([42, 47, 11])>>>

Die Methode array wandelt eine Sequenz von Sequenzen – im Beispiel Tupel – in ein zwei-dimensionales Array:

>>> x = np.array( ((11,12,13), (21,22,23), (31,32,33)) )>>> print(x)[[11 12 13][21 22 23][31 32 33]]>>>

Entsprechend wandelt sie Folgen von Folgen von Folgen in ein 3-dimensionales Array:

>>> x = np.array( ( ((111,112), (121,122) ), ((211,212),(221,222)) ) )>>> print(x)[[[111 112][121 122]]

[[211 212]

Page 335: 3446435476_Pytho

326 29 NumPy

[221 222]]]>>> print(x[1][0][1])212>>> print(x[1][1][1])222>>>

BILD 29.2 Achsen

Das Attribut ndim gibt Auskunft über die Anzahl der Dimensio-nen eines Arrays. Die Methode shape() liefert ein Tupel mit denArray-Dimensionen zurück:

>>> x= np.array( ((7,3,0), (7,11,9), (15,3,7),(12,4,8)))>>> x.ndim2>>> x.shape(4, 3)>>> xarray([[ 7, 3, 0],

[ 7, 11, 9],[15, 3, 7],[12, 4, 8]])

>>>

BILD 29.3 Achsen

Ein Besonderheit von NumPy besteht darin, dass mandie Verteilung der Elemente auf die Dimensionen auchim Nachhinein wieder verändern kann. Dazu steht dershape-Parameter zur Verfügung. Wie die Achsen eines n-dimensionalen Vektorraums auf den shape-Parameter ver-teilt sind, zeigen die beiden Abbildungen.

Die Achsen (axis) eines Arrays beschreiben die Reihenfolgeder Indizierungen (axis=0 steht für den ersten Index, axis=1für den zweiten und so weiter.

Die Ausprägung („shape”) eines Arrays bezeichnet ein Tu-pel mit der Anzahl der Elemente pro Achse (Dimension).Im Fall der obersten Abbildung gilt shape = (6,3), d.h. wirhaben sechs Zeilen und drei Spalten.

Die Bearbeitung von Arrays erfolgt ähnlich wie bei Listen:

>>> x = np.array([42,47,11])>>> x[:2]array([42, 47])>>> x = np.array([42,47,11])>>> x[0]42>>> x[:2]array([42, 47])>>> x[1] = 49>>> xarray([42, 49, 11])>>>

Page 336: 3446435476_Pytho

29.3 Arrays flach machen 327

Im Folgenden zeigen wir ein Beispiel eines zweidimensionalen Arrays:

>>> x = np.array([[0.314, 0.5,17],[1.4142,0.67, 7]], float)>>> x[0]array([ 0.314, 0.5 , 17. ])>>> x[1]array([ 1.4142, 0.67 , 7. ])>>> x[0,2]17.0>>>

Auch bei 2-dimensionalen Arrays kann man Slices einsetzen:

>>> x[:,0]array([ 0.314 , 1.4142])>>> x[:,1]array([ 0.5 , 0.67])>>> x[:,2]array([ 17., 7.])>>>

Wir wir bereits erfahren haben, besteht ein NumPy-Array im Gegensatz zu normalen Listenin Python nur aus Daten des gleichen numerischen Typs, also Integer oder Float. Mit demAttribut dtype kann man den Typ der Werte eines Arrays ermitteln.

>>> x.dtypedtype('float64')

float64 ist ein numerischer Typ von NumPy, der die Zahlen in doppelter Präzision abspei-chert, ähnlich zu dem Typ float in Python.

29.3 Arrays flach machenEs gibt zwei Methoden, um ein mehrdimensionales Array flach zu machen1:

■ flatten()

■ ravel()2

Die Arbeitsweise können wir im folgenden Beispiel an einem dreidimensionalen Array be-obachten:

import numpy as np

x = np.array([[[ 0, 1], [ 2, 3], [ 4, 5], [ 6, 7]],

1 englisch: flatten2 Dies Wahl des Begriffes ist „verwirrend” (ravelling): Das Wort „ravel” bedeutet im Englischen „verwirren,

verwickeln, verheddern”, was die Methode ravel aber nicht tut. Sie tut das Gegenteil, was aber Englisch demVerb „ravel out” entsprechen würde.

Page 337: 3446435476_Pytho

328 29 NumPy

[[ 8, 9], [10, 11], [12, 13], [14, 15]],[[16, 17], [18, 19], [20, 21], [22, 23]]])

print("Aufruf von x.flatten():")print(x.flatten())

print("Aufruf von x.flatten():")print(x.ravel())

print("x:")print(x)

Sowohl flatten als auch ravel liefern ein Objekt <class ’numpy.ndarray’> zurück, ver-ändern aber nicht ihr Objekt. flatten und ravel liefern die gleichen Ergebnisse zurück, aberes gibt Unterschiede in der Implementierung. flatten erzeugt immer eine komplette Kopieder Daten, während im Falle von ravel opportunistisch nur so viel kopiert wird, wie not-wendig ist, d.h. es wird versucht, wenn möglich eine View zurückzuliefern.3

Wir sehen bei der Ausgabe des Skripts, dass das ursprüngliche Array x unverändert geblie-ben ist.

$ python3 numpy_arrays.pyAufruf von x.flatten():[ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23]Aufruf von x.flatten():[ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23]x:[[[ 0 1][ 2 3][ 4 5][ 6 7]]

[[ 8 9][10 11][12 13][14 15]]

[[16 17][18 19][20 21][22 23]]]

29.4 Arrays umdimensionierenArrays lassen sich in NumPy beliebig umdimensionieren. Dazu wird die Methode resha-pe(t) zur Verfügung gestellt. Das Argument von reshape ist ein Tupel, dessen Dimension

3 Verwirrend? ravel hat also seinen Namen zu Recht erhalten – siehe vorige Fußnote.

Page 338: 3446435476_Pytho

29.4 Arrays umdimensionieren 329

die Dimension des veränderten Arrays darstellt, während die Werte dieses Tupels die Ver-teilung der Elemente über die Dimensionen widerspiegelt. Die Methode reshape() liefertein Array mit geänderten Dimensionen zurück, ohne dass die Daten des als Parameterübergebenen Arrays verändert werden.

Beispiel:

>>> import numpy as np>>> x = np.array(range(24))>>> y = x.reshape((3,4,2))>>> yarray([[[ 0, 1],

[ 2, 3],[ 4, 5],[ 6, 7]],

[[ 8, 9],[10, 11],[12, 13],[14, 15]],

[[16, 17],[18, 19],[20, 21],[22, 23]]])

>>> xarray([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,

16,17, 18, 19, 20, 21, 22, 23])

>>>

Im folgenden Beispiel zeigen wir, wie wir mit Slices geschickt den Rand eines Arrays weg-schneiden können. Es werden also die erste Zeile, die letzte Zeile, die erste Spalte und dieletzte Spalte entfernt:

>>> x = np.array(range(100))>>> y = x.reshape(10,10)>>> yarray([[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9],

[10, 11, 12, 13, 14, 15, 16, 17, 18, 19],[20, 21, 22, 23, 24, 25, 26, 27, 28, 29],[30, 31, 32, 33, 34, 35, 36, 37, 38, 39],[40, 41, 42, 43, 44, 45, 46, 47, 48, 49],[50, 51, 52, 53, 54, 55, 56, 57, 58, 59],[60, 61, 62, 63, 64, 65, 66, 67, 68, 69],[70, 71, 72, 73, 74, 75, 76, 77, 78, 79],[80, 81, 82, 83, 84, 85, 86, 87, 88, 89],[90, 91, 92, 93, 94, 95, 96, 97, 98, 99]])

>>> y[1:-1,1:-1]array([[11, 12, 13, 14, 15, 16, 17, 18],

[21, 22, 23, 24, 25, 26, 27, 28],[31, 32, 33, 34, 35, 36, 37, 38],[41, 42, 43, 44, 45, 46, 47, 48],

Page 339: 3446435476_Pytho

330 29 NumPy

[51, 52, 53, 54, 55, 56, 57, 58],[61, 62, 63, 64, 65, 66, 67, 68],[71, 72, 73, 74, 75, 76, 77, 78],[81, 82, 83, 84, 85, 86, 87, 88]])

>>>

29.5 Arrays konkatenierenIm folgenden Beispiel konkatenieren4 wir drei eindimensionale Arrays. Die Elemente deszweiten Arrays werden an das erste Array angehängt. Anschließend werden die Elementedes dritten Arrays daran angehängt:

>>> import numpy as np>>> x = np.array([11,22])>>> y = np.array([18,7,6])>>> z = np.array([1,3,5])>>> np.concatenate((x,y,z))array([11, 22, 18, 7, 6, 1, 3, 5])>>>

Wenn wir mehrdimensionale Arrays konkatenieren, wird default-mäßig nach der erstenAchse (axis = 0) verknüpft. Man kann allerdings auch den optionalen Parameter axis explizitsetzen:

>>> x = np.array(range(24))>>> x = x.reshape((3,4,2))>>> y = np.array(range(100,124))>>> y = y.reshape((3,4,2))>>> z = np.concatenate((x,y))>>> zarray([[[ 0, 1],

[ 2, 3],[ 4, 5],[ 6, 7]],

[[ 8, 9],[ 10, 11],[ 12, 13],[ 14, 15]],

[[ 16, 17],[ 18, 19],[ 20, 21],[ 22, 23]],

4 Konkatenieren stammt vom lateinischen catena, was Kette bedeutet. Unter Konkatenieren versteht manalso „verketten” oder „verknüpfen”.

Page 340: 3446435476_Pytho

29.5 Arrays konkatenieren 331

[[100, 101],[102, 103],[104, 105],[106, 107]],

[[108, 109],[110, 111],[112, 113],[114, 115]],

[[116, 117],[118, 119],[120, 121],[122, 123]]])

>>>>>> z = np.concatenate((x,y),axis = 1)>>> zarray([[[ 0, 1],

[ 2, 3],[ 4, 5],[ 6, 7],[100, 101],[102, 103],[104, 105],[106, 107]],

[[ 8, 9],[ 10, 11],[ 12, 13],[ 14, 15],[108, 109],[110, 111],[112, 113],[114, 115]],

[[ 16, 17],[ 18, 19],[ 20, 21],[ 22, 23],[116, 117],[118, 119],[120, 121],[122, 123]]])

>>>>>> z = np.concatenate((x,y),axis = 2)>>> zarray([[[ 0, 1, 100, 101],

[ 2, 3, 102, 103],[ 4, 5, 104, 105],[ 6, 7, 106, 107]],

[[ 8, 9, 108, 109],

Page 341: 3446435476_Pytho

332 29 NumPy

[ 10, 11, 110, 111],[ 12, 13, 112, 113],[ 14, 15, 114, 115]],

[[ 16, 17, 116, 117],[ 18, 19, 118, 119],[ 20, 21, 120, 121],[ 22, 23, 122, 123]]])

>>>

29.6 Array, neue Dimension hinzufügenNeue Dimensionen können einem Array mittels Slicing und np.newaxis hinzugefügt wer-den. Wir demonstrieren diese Technik im folgenden Beispiel:

>>> x = np.array([2,5,18,14,4])>>> xarray([ 2, 5, 18, 14, 4])>>> y = x[:, np.newaxis]>>> yarray([[ 2],

[ 5],[18],[14],[ 4]])

>>>

29.7 Array mit Nullen und Einseninitialisieren

Es gibt zwei Möglichkeiten, Arrays mit Nullen oder Einsen zu initialisieren. Die Methodeones(t) benötigt ein Tupel t mit der „shape” des zu schaffenden Arrays und füllt das Arrayentsprechend mit Einsen. Bei Default wird das Array mit Einsen vom Typ float gefüllt. Wennman Einsen vom Typ Integer braucht, kann man den optionalen Parameter dtype auf intsetzen:

>>> np.ones((2,3))array([[ 1., 1., 1.],

[ 1., 1., 1.]])>>> np.ones((3,4),dtype=int)array([[1, 1, 1, 1],

[1, 1, 1, 1],[1, 1, 1, 1]])

>>>

Page 342: 3446435476_Pytho

29.8 Matrizenarithmetik 333

Was wir über die Methode ones() gesagt haben, gilt analog für die Methode zeros(), wie wirim folgenden Beispiel sehen:

>>> np.zeros((2,4))array([[ 0., 0., 0., 0.],

[ 0., 0., 0., 0.]])>>>

Es gibt noch eine weitere interessante Möglichkeit, eine Matrix mit Einsen oder Nullen zufüllen, wenn das Array die „shape” eines anderen existierenden Arrays „a” haben soll. Fürdiesen Zweck stellt NumPy die Methoden ones_like(a) und zeros_like(a) zur Verfügung.

>>> x = np.array([2,5,18,14,4])>>> np.ones_like(x)array([1, 1, 1, 1, 1], dtype=int32)>>> np.zeros_like(x)array([0, 0, 0, 0, 0])>>>

29.8 MatrizenarithmetikBisher haben wir gesehen, wie man Arrays erzeugen und ändern kann. In diesem Abschnittwollen wir zeigen, wie wir in Python mittels NumPy ohne Aufwand und effizient Matrizena-rithmetik betreiben können, also

■ Matrizenaddition

■ Matrizensubtraktion

■ Matrizenmultiplikation

■ Skalarprodukt

■ Kreuzprodukt

■ und weitere arithmetische Funktionen auf Matrizen

Die arithmetischen Standardoperationen +, -, *, /, ** und % werden elementweise ange-wendet, d.h. die Arrays müssen die gleichen Größen haben, damit man diese Operatorenanwenden kann.

>>> import numpy as np>>> x = np.array([1,5,2])>>> y = np.array([7,4,1])>>> x + yarray([8, 9, 3], dtype=int32)>>> x * yarray([ 7, 20, 2], dtype=int32)>>> x - yarray([-6, 1, 1], dtype=int32)>>> x / yarray([ 0.14285714, 1.25 , 2. ])

Page 343: 3446435476_Pytho

334 29 NumPy

>>> x % yarray([1, 1, 0], dtype=int32)>>>

29.9 Vektoraddition undVektorsubtraktion

BILD 29.4 Achsen

Vielen dürfte die Vektoraddition aus demPhysikunterricht der Schule bekannt sein.Man benutzt die Vektoraddition, um dieGesamtkraft zu berechnen, wenn verschie-dene Einzelkräfte mit unterschiedlichenAusrichtungen auf einen als punktförmigangenommenen Körper einwirken. Wennman wissen will, welche Kraft insgesamtauf den Körper ausgeübt wird, muss maneine sogenannte Vektoraddition durchführen. Grafisch wird eine Vektoraddition realisiert,indem man durch Parallelverschiebung an die Spitze des ersten Vektors, also die Stelle, ander sich der Pfeil befindet, den Anfang des zweiten Vektors ansetzt.

Rechnerisch kann man mit der Vektoraddition die Gesamtverschiebung der Vektoren er-mitteln, indem man die jeweiligen Komponenten miteinander addiert.

>>> import numpy as np>>> x = np.array([3,2])>>> y = np.array([5,1])>>> z = x + y>>> zarray([8, 3], dtype=int32)>>>

BILD 29.5 Achsen

Einen Vektor y von einem Vektor x zu sub-trahieren, ist das Gleiche, als wenn mandas Negative von y zu dem Vektor x ad-diert. Es gilt also: x - y = x + (-y). Geo-metrisch kann man die Subtraktion einesVektors wie folgt durchführen: Um y vonx zu subtrahieren, platzieren wir die End-punkte von x und y auf den gleichen Punkt.Dann zeichnen wir einen Pfeil von der Spitze von y zu der Spitze von x. Dieser „Pfeil” ist einRepräsentant des Vektors x - y, siehe Bild auf der rechten Seite.

Mathematisch gesehen wird die Vektorsubtraktion durch eine komponentenweise Sub-traktion der einzelnen Komponenten durchgeführt.

>>> x = np.array([3,2])>>> y = np.array([5,1])

Page 344: 3446435476_Pytho

29.9 Vektoraddition und Vektorsubtraktion 335

>>> z = x - y>>> zarray([-2, 1], dtype=int32)>>>

Das Skalarprodukt wird häufig auch als inneres Produkt oder Punktprodukt bezeichnet.Mathematisch stellt das Skalarprodukt eine algebraische Operation dar, die zwei Koordi-nationvektoren gleicher Größe als Argument nimmt und eine einfache Zahl zurückliefert.Das Ergebnis wird berechnet, indem die Komponenten mit gleichem Index multipliziertund die so erhaltenen Produkte anschließend addiert werden. Der Name Punktprodukt(oder englisch „dot product”) stammt übrigens davon, dass der „.” häufig als Operatorzei-chen für diese Operation genutzt wird. Der Name Skalarprodukt stellt mehr den Aspekt inden Vordergrund, dass das Ergebnis der Operation ein Skalar ist.

Definition des Skalarprodukts:

a⃗ · b⃗ =| a⃗ | · | a⃗ | cos∠(a⃗, b⃗)

Aus der Definition des Skalarprodukts können wir ersehen, dass es benutzt werden kann,um den Winkel zwischen zwei Vektoren zu ermitteln:

Berechnung des Skalarprodukts:

a⃗ · b⃗ = a1 ·b1 +a2 ·b2 +a3 ·b3

Es ist leicht einzusehen, dass man den Betrag, also die Länge eines Vektors, auch über dasSkalarprodukt definieren bzw. berechnen kann:

| a⃗ |=p

a⃗ · a⃗ =pa1 ·a1 +a2 ·a2 +a3 ·a3 =

√a2

1 +a22 +a2

3

Wir wollen nun mit konkreten Vektoren mit dem Skalarprodukt in NumPy „herumspielen”:

Wir benutzen zwei Beispielvektoren x⃗ und y⃗ :

x⃗ = 2

7−3

und y⃗ =−2

34

Der Absolutbetrag von x⃗ berechnet sich wie folgt:

| x⃗ |=p

a⃗ · a⃗ =

√√√√√√ 2

7−3

·−2

34

Dies entspricht in NumPy-Notation:

numpy.sqrt(np.dot(x,x))

Der Winkel zwischen x⃗ und y⃗ berechnet sich als:

cos∠(⃗x, y⃗) = x⃗ · y⃗

| x⃗ | · | y⃗ |

Page 345: 3446435476_Pytho

336 29 NumPy

Im unten stehenden Skript wird dies wie folgt berechnet:

cos_xy = np.dot(x,y) / ( x_abs * y_abs)

In der folgenden interaktiven Python-Shell wenden wir die obigen Überlegungen an:

>>> import numpy as np>>> x = np.array([2,7,-3])>>> x_abs = np.sqrt(np.dot(x,x))>>> x_abs7.8740078740118111>>> y = np.array([-2,3,4])>>> y_abs = np.sqrt(np.dot(y,y))>>> y_abs5.3851648071345037>>> cos_xy = np.dot(x,y) / ( x_abs * y_abs)>>> cos_xy0.11791665765914455>>> angle_xy = np.arccos(cos_xy)>>> angle_xy # Winkel in Bodenmaß1.4526046862352173>>> angle_xy * 360 / 2 / np.pi # Winkel in Gradmaß83.228117822203146>>>

29.10 Matrix-KlasseDie Matrix-Klasse ist eine Unterklasse der NumPy-Arrays (ndarray). Ein Matrix-Objekt erbtalle Attribute und Methoden von ndarry. Ein Unterschied besteht darin, dass die NumPy-Matrizen streng 2-dimensional sind, während NumPy-Arrays von beliebiger Dimensionsein können, also n-dimensional.

Der größte Vorteil von Matrizen liegt darin, dass sie eine komfortable Notation für verschie-dene Matrizenoperationen wie z.B. die Matrix-Multiplikation zur Verfügung stellen. WennX und Y zwei Matrizen sind, dann definiert X * Y die Matrix-Multiplikation. Wenn allerdingsX und Y zwei ndarrays sind, dann definiert X * Y eine komponentenweise Multiplikation.

>>> x = np.array( ((2,3), (3, 5)) )>>> y = np.array( ((1,2), (5, -1)) )>>> x * yarray([[ 2, 6],

[15, -5]])>>> x = np.matrix( ((2,3), (3, 5)) )>>> y = np.matrix( ((1,2), (5, -1)) )>>> x * ymatrix([[17, 1],

[28, 1]])

Page 346: 3446435476_Pytho

29.10 Matrix-Klasse 337

29.10.1 Matrix-Produkt

Das Produkt einer (l x m)-Matrix A = (ai j )i=1...l , j=1..m und einer (m x n)-Matrix B =(bi j )i=1...m, j=1..n ist eine Matrix C = (ci j )i=1...l , j=1..n , die wie folgt berechnet wird:

ci j =m∑

k=1ai k ·bk j

Die folgende Grafik verdeutlicht dieses Verfahren.

Es gibt mehrere Möglichkeiten, eine Matrizenmultiplikation durchzuführen:

■ Wir definieren zwei Matrizen x und y als zweidimensionale Arrays und bilden das Kreuz-produkt, wodurch eine Matrizenmultiplikation durchführt wird:

>>> import numpy as np>>> x = np.array( ((2,3), (3, 5)) )>>> y = np.array( ((1,2), (5, -1)) )>>> np.dot(x,y)array([[17, 1],

[28, 1]])

■ Wie eben, aber statt des Kreuzprodukts casten wir x und y in Matrix-Objekte:

>>> import numpy as np>>> x = np.array( ((2,3), (3, 5)) )>>> y = np.array( ((1,2), (5, -1)) )>>> np.mat(x) * np.mat(y)matrix([[17, 1],

[28, 1]])>>> x = np.mat("2 3; 3 5")>>> y = np.mat("1 2; 5 -1")>>> np.mat(x) * np.mat(y)matrix([[17, 1],

[28, 1]])>>>

Page 347: 3446435476_Pytho

338 29 NumPy

■ Wir definieren x und y sofort als Matrix-Objekte.

>>> import numpy as np>>> x = np.mat("2 3; 3 5")>>> y = np.mat("1 2; 5 -1")>>> np.mat(x) * np.mat(y)matrix([[17, 1],

[28, 1]])>>>

mat ist übrigens eine Abkürzung für matrix. Wir hätten also auch ebensogut Folgendesschreiben können:

>>> import numpy as np>>> x = np.matrix("2 3; 3 5")>>> y = np.matrix("1 2; 5 -1")>>> np.matrix(x) * np.matrix(y)matrix([[17, 1],

[28, 1]])>>>

29.10.2 Eine einfache praktische Anwendung

BILD 29.6 Pralinen

In dem folgenden praktischen Beispielkommen wir auf die Schokoladenseite desLebens zu sprechen. Nehmen wir an, wirhaben vier Personen, die wir Lukas, Mia,Leon und Hannah taufen. Jeder von ihnenhat Pralinen der Marken A, B und C ge-kauft. Lukas kaufte 100 g der Marke A, 175g der Marke B und 210 von C. Mia wähl-te 90 g von A, 160 g von B und 150 g vonC. Leon kaufte 200 g von A, 50 von B und100 g von C. Hannah mag allem Anscheinnach die Sorte B nicht, weil sie keine Prali-nen dieser Sorte gekauft hat. Dafür scheintsie ein echter Fan der Sorte C zu sein, weilsie davon gleich 310 g gekauft hat. Außer-dem kaufte sie noch 120 g der Marke A.

Nun wollen wir natürlich wissen, wie viel die einzelnen Sorten kosten:

■ A kostet 2.98 pro 100 g,

■ B kostet 3.90 und

■ C nur 1.99 Euro.

Wenn wir nun berechnen wollen, wie viel jeder von ihnen zu zahlen hat, können wir Num-Py und die Matrizenmultiplikation nutzen:

Page 348: 3446435476_Pytho

29.11 Inverse Matrix 339

>>> PersAnz = np.array([[100,175,210],[90,160,150],[200,50,100],[120,0,310]])

>>> Preis_per_100_g = np.array([2.98,3.90,1.99])>>> Preis_in_Cent = np.dot(PersAnz,Preis_per_100_g)>>> Preis_in_ Euro = Preis_in_Cent / np.array([100,100,100,100])>>> Preis_in_Euroarray([ 13.984, 11.907, 9.9 , 9.745])>>>

Das bedeutet, dass Lukas 13.98 Euro, Mia 11.97 Euro, Leon 9.90 Euro und Hannah 9.75 Eurozu zahlen haben.

29.11 Inverse MatrixMit der Funktion inv (numpy.linalg.inv) kann man eine Matrix invertieren.

Hat man die inverse Matrix A_inv zu einer quadratischen Matrix A berechnet, dann gilt,dass die Matrizenmultiplikation von A und A_inv die Einheitsmatrix ergibt, also:

np.dot(A, A_inv) = np.dot(A_inv, A) = E

Hierbei ist E die Einheitsmatrix, also die Matrix, die auf der Diagonalen nur Einsen hat undansonsten Nullen. Die Einheitsmatrix lässt sich übrigens mit der Funktion eye von numpygenerieren:

>>> E = np.eye(3)>>> Earray([[ 1., 0., 0.],

[ 0., 1., 0.],[ 0., 0., 1.]])

>>>

Im folgenden Beispiel entspricht E der Einheitsmatrix, wenn man von numerischen Unge-nauigkeiten absieht:

>>> A = np.array([[3, 5, 1], [1,-1,2], [2,0,-2]])>>> A_inv = np.linalg.inv(A)>>> E = np.dot(A,A_inv)>>> Earray([[ 1.00000000e+00, -5.55111512e-17, 2.77555756e-17],

[ -5.55111512e-17, 1.00000000e+00, 1.11022302e-16],[ -5.55111512e-17, 0.00000000e+00, 1.00000000e+00]])

>>>

Page 349: 3446435476_Pytho

340 29 NumPy

29.12 Kreuzprodukt / Vektorprodukt

BILD 29.7 Kreuzprodukt

Nun müssen wir wieder den Konsum der leckeren Prali-nen einstellen und uns einem kalorienärmeren, mathe-matischeren Thema zuwenden, dem Kreuzprodukt oderVektorprodukt.

Das Kreuz- oder Vektorprodukt ist eine binäre Operationim dreidimensionalen Raum. Das Kreuzprodukt zweierVektoren a⃗ und b⃗ wird mit a⃗ × b⃗ bezeichnet.

Das Ergebnis ist ein Vektor, der senkrecht zu den Vek-toren a⃗ und b⃗ steht, die multipliziert werden. Senkrechtim Sinne eines Rechtssystems, d.h. die beiden Vektorena⃗ und b⃗ sowie der Vektor a⃗ × b⃗ bilden ein Rechtssystem.Sie verhalten sich wie Daumen, Zeigefinger und Mittel-finger der rechten Hand. Dies wird manchmal auch alsDrei-Finger-Regel bezeichnet.

Das Kreuzprodukt ist definiert als:

a⃗ × b⃗ = (| a⃗ | · | b⃗ | ·sin∠(a⃗, b⃗)) · n⃗

wobei der Vektor n⃗ derjenige zu a⃗ und b⃗ senkrechte Einheitsvektor ist, der diese zu einemRechtssystem ergänzt. n⃗ steht senkrecht auf der von a⃗ und b⃗ aufgespannten Ebene. Es gibtzwei Vektoren, die diese Eigenschaft erfüllen. Der richtige ist der, der mit der Drei- Finger-Regel ermittelt werden kann.

Falls einer der Vektoren, die multipliziert werden, Null ist oder wenn die beiden Vektorenparallel sind, dann ist ihr Kreuzprodukt gleich Null. Die Länge des Ergebnisvektors ent-spricht der Fläche des von den beiden multiplizierten Vektoren aufgespannten Parallelo-gramms. Falls die beiden Vektoren senkrecht zueinander stehen, erhalten wir ein Rechteck.

>>> import numpy as np>>> x = np.array([0,0,1])>>> y = np.array([0,1,0])>>> np.cross(x,y)array([-1, 0, 0])>>> np.cross(y,x)array([1, 0, 0])>>>

29.13 Lineare GleichungssystemeNumPy besitzt viele weitere Funktionen und Methoden zur Berechnungen von weiterenAnwendungen aus der Linearen Algebra. Dazu bietet sich vor allen Dingen das Paket linalgan. linalg bietet beispielsweise die Funktion solve zum Lösen von linearen Gleichungssys-temen.

Page 350: 3446435476_Pytho

29.13 Lineare Gleichungssysteme 341

Betrachten wir das folgende Gleichungssystem:

4x1 −2x2 +7x3 =−7

−x1 +5x2 +3x3 +2x4 = 12

−x1 +2x2 +5x3 −2x4 =−8

x1 +2x2 +3x3 +4x4 = 14

Ein solches Gleichungssystem kann man unter Benutzung der Matrix-Vektor-Multiplikationwie folgt schreiben:

A · x⃗ = b⃗

wobei A eine Matrix ist, die sogenannte Koeffizientenmatrix. Hier eine 4 x 4 Koeffizienten-matrix, wie es unserem Beispiel entspricht:

a11 a12 a13 a14a21 a22 a23 a24a31 a32 a33 a34a41 a42 a43 a44

Mit den Werten aus unserem Beispielgleichungssystem sieht A dann wie folgt aus:

4 −2 7 0−1 5 3 2−1 2 5 −21 2 3 4

Der Vektor b⃗ entspricht den Werten auf den rechten Seiten der Gleichungssysteme:

b⃗ =

−712−814

Damit sieht unser Gleichungssystem wie folgt aus:

4 −2 7 0−1 5 3 2−1 2 5 −21 2 3 4

·

x1

x2

x3

x4

=

−712−814

Nun kommen wir endlich zur Lösung dieses Gleichungssystems in Python:

>>> import numpy as np, numpy.linalg>>> A = np.array([[4,-2,7,0],[-1,5,3,2],[-1,2,5,-2],[1,2,3,4]])>>> b = np.array([-7, 12, -8, 14])>>> x = np.linalg.solve(A,b)>>> xarray([ 1., 2., -1., 3.])>>>

Page 351: 3446435476_Pytho

342 29 NumPy

29.14 PolynomeNumPy eignet sich auch hervorragend zum Umgang mit Polynomen und stellt hierfür auchein Paket polynomial zur Verfügung.

Zur Darstellung von Polynomen

f (x) =0∑

i=nai · xi = an · xn +an−1 · xn−1 + . . . a2 · x2 +a1 · x +a0

dient die Klasse poly1d.5.

Beispiel:

Wir wollen das folgende Beispielpolynom in Python implementieren:

f (x) = 3 · x2 −2 · x +5

Wir sehen in der folgenden interaktiven Python-Shell, dass poly1d ein ndarray mit den Ko-effizienten der Funktion erwartet. Dabei müssen die Koeffizienten in absteigender Reihen-folge angegeben werden, d.h. die Koeffizienten von den höchsten Exponenten zuerst.

>>> import numpy as np>>> f = np.poly1d([3,-2,5])>>> f(0)5>>> f(4)45

Polynome kann man bekanntlich auch in Linearfaktoren über die Nullstellen zerlegen.Dann kann man eine Funktion auch in Produktschreibweise schreiben.

Seien 3, 2 und -1 die Nullstellen von f. Dann kann man f schreiben als

f (x) = (x −3)(x −2)(x +1)

In NumPy definiert man f ebenfalls mit poly1d, aber man muss den zweiten Parameter aufTrue setzen. Dann erwartet poly1d keine Koeffizienten, sondern ein ndarry mit Nullstellen.

>>> import numpy as np>>> f = np.poly1d([3,2,-1],True)>>> fpoly1d([ 1, -4, 1, 6])>>> f(3)0>>> f(2)0>>> f(-1)0>>>

5 1d steht für 1-dimensional.

Page 352: 3446435476_Pytho

29.15 Aufgaben 343

Von einem Polynom kann man sich auch die Nullstellen mit der Funktion roots6 bestim-men lassen:

Beispiel:

Wir berechnen die Nullstellen für das Polynom:

f (x) = x3 −x2 −10 · x −8

Die Berechnung in NumPy sieht wie folgt aus:

>>> import numpy as np>>> f = np.poly1d([ 1, -1, -10, -8])>>> np.roots(f)array([ 4., -2., -1.])>>>

Mit numpy lassen sich auch die Ableitung und unbestimmte Integrale von Polynomen be-stimmen. Dazu gibt es die Methoden deriv()7 und integ():

>>> import numpy as np>>> f = np.poly1d([3,-2,-1])>>> f.deriv()poly1d([ 6, -2], dtype=int32)>>> f.integ()poly1d([ 1., -1., -1., 0.])>>>

29.15 Aufgaben

1. Aufgabe:Prüfen Sie, ob die folgenden Vektoren orthogonal (senkrecht) zueinander sind.

1. x⃗ = 2

7−3

und y⃗ =−2

11

2. x⃗ = 2

3−1

und y⃗ =−5

42

Lösung: Lösungen zu Kapitel 29 (NumPy), Seite 415

6 „roots” bezeichnet im Englischen auch die Nullstellen eines Polynoms7 Englisch: derivation

Page 353: 3446435476_Pytho

344 29 NumPy

2. Aufgabe:Bestimmen Sie den Winkel zwischen den beiden Vektoren:

x⃗ = 5

9−5

und y⃗ =−3

06

Lösung: Lösungen zu Kapitel 29 (NumPy), Seite 415

3. Aufgabe:Lösen Sie das folgende Gleichungssystem:

−2x1 −2x2 +2x3 +x4 =−5

−3x1 +4x2 +−2x4 =−1

−x1 +2x2 +3x3 +2x4 = 0

3x1 +2x2 +3x3 +2x4 = 4

Lösung: Lösungen zu Kapitel 29 (NumPy), Seite 416

4. Aufgabe:Berechnen Sie die Nullstellen für das folgende Polynom:

f (x) = 4 · x3 +12 · x2 −7 · x −5

Lösung: Lösungen zu Kapitel 29 (NumPy), Seite 416

5. Aufgabe:Bestimmen Sie für das folgende Polynom das unbestimmte Integral und die Ableitung:

f (x) = 2 · x3 −3 · x2 −2 · x −1

Lösung: Lösungen zu Kapitel 29 (NumPy), Seite 416

Page 354: 3446435476_Pytho

TEIL IIIUmfassende Beispiele

Page 355: 3446435476_Pytho
Page 356: 3446435476_Pytho

30 Bruchklasse

30.1 Brüche à la 1001 Nacht

BILD 30.1 Walter Heubach: LagerndeKarawane mit Dromedaren

Bevor wir mit der Arbeit an unserem Mo-dul und mit den Brüchen beginnen, möch-ten wir Sie noch in die Welt Aladins undSinbads entführen. An einem Zwischen-stopp an einer Oase finden wir drei Män-ner mit einer Herde Kamele. Sie jammernund wehklagen. Der Älteste der drei mitlangem Bart und Turban wendet sich anSie und sagt:

— „Wir sind drei Brüder und haben diese35 Kamele als unser Erbe erhalten. Nachdem Wunsch meines Vaters soll die Hälfteder Herde mir als dem ältesten Sohn gehö-ren.”

— „Und ein Drittel soll mir gehören!”, sagt der mittlere der drei Brüder.

— „Ich als jüngster soll mich mit einem Neuntel zufriedengeben!”, sagt der letzte der Drei-en.

— „Die Hälfte von 35 ist 17 und ein halb, und wir wollen kein Kamel zerteilen!”, fährt derÄlteste fort.

— „Die Schwierigkeiten, ein Drittel und ein Neuntel der Herde zu bestimmen, sind auchnicht kleiner!”, jammern die beiden anderen unisono.

Plötzlich haben Sie einen genialen Einfall und schlagen den ratlosen Brüdern Folgendesvor:

Sie geben Ihr einziges Kamel, das Sie noch weiter durch die Wüste tragen soll, der Herdehinzu und sagen zum ältesten Bruder:

— „Vorher hättest du 17 und ein halbes Kamel erhalten sollen, doch nun sind es 18!”

Zum mittleren der Brüder sagen Sie:

— „Vorher hättest du 11 und zwei Drittel Kamele erhalten sollen, nun werden es 12 sein.Du hast also auch keinen Grund zum Klagen.”

Page 357: 3446435476_Pytho

348 30 Bruchklasse

Und zum Jüngsten sagen Sie:

— „Du wirst nun 4 Kamele statt 3 und noch einen Teil von einem weiteren erhalten undhast auch keinen Grund zu Klage!”

Die Brüder ziehen nun voller Freude mit ihren 34 Kamelen von dannen, und Sie haben nunzwei Kamele statt einem!

30.2 Zurück in die Gegenwart

BILD 30.2 Brüche als Kuchen-diagramm

Ja, damals zu Sinbads Zeiten war Bruchrechnen nocheine hohe und wenig erforschte Kunst, und mit sol-chen Geschichten konnte man die Leute leicht ver-zaubern. Sicherlich haben Sie das Problem gleich er-kannt.

1

2+ 1

3+ 1

9=?

Wir wollen nun eine Python-Klasse für das Rech-nen mit Brüchen entwickeln, wobei wir uns aber aufdie vier Grundrechenarten „Addition”, „Subtraktion”,„Multiplikation” und „Division” beschränken wollen.Zunächst müssen wir uns aber die Frage stellen, wie wir einen Bruch in Python darstellenwollen. Ein Bruch besteht ja bekanntlich aus einem Zähler, einem Bruchstrich und einemNenner. Mathematisch relevant sind natürlich nur Zähler und Nenner, während der Bruch-strich bloß ein Repräsentationsdetail ist.

Zur Repräsentierung eines Bruchs in unserer Bruchklasse genügt es also, wenn wir ein At-tribut für den Nenner und eines für den Zähler definieren. Im folgenden Beispiel finden Siedie __init__-Methode, und wir erzeugen Instanzen für die Brüche 1

2 und 25 :

class Bruch(object):

def __init__(self,z,n):self.zaehler = zself.nenner = n

if __name__ == "__main__":x = Bruch(1,3)y = Bruch(2,5)

Wie sieht es aus, wenn wir einen dieser Brüche versuchen auszudrucken? Wenn wir obigesProgramm unter brueche.py gespeichert haben, können wir es direkt auf der interaktivenPython-Shell testen:

$ python3Python 3.2.3 (default, Oct 19 2012, 19:53:57)

Page 358: 3446435476_Pytho

30.2 Zurück in die Gegenwart 349

[GCC 4.7.2] on linux2Type "help", "copyright", "credits" or "license" for more information.>>> from brueche import Bruch>>> x = Bruch(2,3)>>> print(x)<brueche.Bruch object at 0xb71994ec>

Positiv ist, dass wir einen Bruch ausgeben können, aber leider erhalten wir kein Ergebnis,dass wir uns wünschen würden. Für die Ausgabe der Variable x würden wir eher „2/3” er-warten. Dies lässt sich ziemlich einfach realisieren. Dazu erweitern wir unsere Klasse umeine Methode __str__.

def __str__(self):return str(self.zaehler)+'/'+str(self.nenner)

Die Methode stellt eine Wandlung des Klassenobjekts, in unserem Fall also ein Bruch, indie String-Repräsentation des Objekts dar. Wird print mit einem Klassenobjekt aufgerufen,wendet Python automatisch die __str__-Methode auf das Objekt an und druckt den Ergeb-nisstring aus:

$ python3Python 3.2.3 (default, Oct 19 2012, 19:53:57)[GCC 4.7.2] on linux2Type "help", "copyright", "credits" or "license" for more information.>>> from brueche import Bruch>>> x = Bruch(2,3)>>> print(x)2/3

Zur Bruchrechnung gehört das Kürzen. Unter dem Kürzen eines Bruchs versteht man, dassman Zähler und Nenner des Bruchs durch die gleiche Zahl dividiert. Kürzen dient der Ver-einfachung von Brüchen.

Der Wert des Bruchs bleibt beim Kürzen gleich. Man erhält lediglich eine neue Darstellungderselben Bruchzahl. So sind zum Beispiel die folgenden Brüche alle gleich:

1

3,

2

6,

3

9,

und so weiter.

Die Zahl, durch die man kürzt, wird als Kürzungszahl bezeichnet. Ein Bruch ist in vollstän-dig gekürzter Form, wenn Zähler und Nenner teilerfremd sind. Also in obiger Folge derBruch 1

3 . Um einen Bruch in eine vollständig gekürzte Form zu wandeln, muss man dengrößten gemeinsamen Teiler, abgekürzt ggT, ermitteln.

Der Euklidische Algorithmus ist ein effizientes Verfahren, um den größten gemeinsamenTeiler zweier Zahlen zu berechnen. Bei diesem Algorithmus wird in aufeinanderfolgendenSchritten jeweils eine Division mit Rest durchgeführt, wobei der Rest im nächsten Schrittzum neuen Divisor wird und der vorige Divisor zum Dividenden. Der Divisor, bei dem sichRest 0 ergibt, ist der größte gemeinsame Teiler der Ausgangszahlen.

Page 359: 3446435476_Pytho

350 30 Bruchklasse

Beispiel:

>>> 561 % 391170>>> 391 % 17051>>> 170 % 5117>>> 51 % 170

Also ist 17 der größte gemeinsame Teiler der Zahlen 561 und 391. Obige Berechnung hätteman interessanterweise auch mit 391%561 starten können. Im nächsten Schritt hat mandann 561%391 und die weitere Rechnung läuft wie oben weiter.

Im Folgenden führen wir die gleichen Berechnungen mit Variablen aus:

>>> a = 170>>> b = 391>>> a, b = b, a % b>>> print(a,b)391 170>>> a, b = b, a % b>>> print(a,b)170 51>>> a, b = b, a % b>>> print(a,b)51 17>>> a, b = b, a % b>>> print(a,b)17 0

Wir können unser Programm nun um eine Klassenmethode ggT, die obiges Verfahren an-wendet, und eine Methode kuerze erweitern. Außerdem rufen wir kuerze bei der Initialisie-rung eines Bruchs auf, damit ein Bruch immer in vollständig gekürzter Form gespeichertwird. Unsere Klasse sieht nun wie folgt aus:

class Bruch(object):

def __init__(self,z,n):self.zaehler = zself.nenner = nself.kuerze()

def __str__(self):return str(self.zaehler)+'/'+str(self.nenner)

def ggT(cls,a,b):while b != 0:

a,b = b,a%breturn a

def kuerze(self):g = self.ggT(self.zaehler, self.nenner)

Page 360: 3446435476_Pytho

30.3 Rechenregeln 351

self.zaehler = int(self.zaehler/g)self.nenner = int(self.nenner/g)

if __name__ == "__main__":x = Bruch(2,6)y = Bruch(391,561)print(x,y)

Startet man obiges Programm, erhält man folgende Ausgabe:

$ python3 brueche.py1/3 23/33

Man sieht, dass die Brüche vollständig gekürzt wurden.

Bis jetzt fehlen aber noch die wichtigsten Methode in unserer Bruchklasse BIS HIER. Wirkönnen Brüche definieren und ausgeben, aber wir können keine Operationen auf den Brü-chen ausführen.

30.3 RechenregelnWie auch beim Rechnen mit natürlichen Zahlen1 oder ganzen Zahlen2 werden beim Rech-nen in den vier Grundrechenarten jeweils zwei Brüche miteinander verknüpft, und manerhält als Ergebnis einen neuen Bruch. Wir haben bereits im vorigen Abschnitt gezeigt,dass sich der Wert eines Bruchs nicht ändert, wenn man Zähler und Nenner durch einengemeinsamen Teiler dividiert. Man spricht dann von Kürzen. Ebenso verändert sich einBruch nicht, wenn man Zähler und Nenner mit der gleichen ganzen Zahl multipliziert. Indiesem Fall spricht man von Erweitern.

30.3.1 Multiplikation von Brüchen

Als Erstes zeigen wir, wie man Brüche multipliziert, da dies am einfachsten ist:

Brüche werden multipliziert, indem man ihre Zähler und Nenner miteinander multipli-ziert. Das Produkt der Zähler wird zum Zähler des Ergebnisses, das Produkt der Nennerwird zum Nenner des Ergebnisses:

Beispiel:

2

3· 3

5= 2 ·3

3 ·5= 6

15

Wie wir sehen, kann das Ergebnis der obigen Multiplikation noch durch den größten ge-meinsamen Teiler von 6 und 15, also 3, gekürzt werden:

6

15= 2

5

1 N= 1,2,3, ...2 Z=−3,−2.−1,01,2,3, ...

Page 361: 3446435476_Pytho

352 30 Bruchklasse

Nun haben wissen wir genug, um unsere Bruchklasse um eine Multiplikation zu erweitern.Dazu müssen wir die Methode __mul__ bereitstellen3:

def __mul__(self,other):p = Bruch(self.zaehler * other.zaehler,

self.nenner * other.nenner)p.kuerze()return p

In der Methode __mul__ erzeugen wir einen neuen Bruch P. Dieser wird mit der Initia-lisierung sofort auf das Ergebnis der Multiplikation gesetzt. Da der Bruch noch in nichtvollständig gekürzter Form vorliegen kann, rufen wir nach der Initialisierung noch die Me-thode kuerze mit „p.kuerze()” auf.

Wir können nun zwei Brüche multiplizieren, wie wir im folgenden Beispiel sehen:

$ python3Python 3.2.3 (default, Oct 19 2012, 19:53:57)[GCC 4.7.2] on linux2Type "help", "copyright", "credits" or "license" for more information.>>> from brueche import Bruch>>> x = Bruch(2,3)>>> y = Bruch(4,15)>>> print(x * y)8/45>>> z = x * y>>> print(z)8/45

Intern wird ein Ausdruck „x * y” in den Aufruf „x.__mul__(y) gewandelt, d.h. damit ent-spricht x dem „self” und y dem „other”.

30.3.2 Division von Brüchen

Die Division von Brüchen ist ähnlich einfach wie die Multiplikation. Durch einen Bruchwird dividiert, indem man mit seinem Kehrwert multipliziert.

Beispiel:

2

3:

5

3= 2

3· 3

5= 2 ·3

3 ·5= 6

15

Unsere Methode für die Division zweier Brüche lässt sich nahezu analog zur Multiplikationrealisieren. Der Name für die Division in Python 3 lautet __truediv__4:

def __truediv__(self,other):p = Bruch(self.zaehler * other.nenner,

self.nenner * other.zaehler)p.kuerze()return p

3 Siehe Kapitel Operator-Überladung, Seite 1874 Vor Python 3 hieß diese Methode __div__.

Page 362: 3446435476_Pytho

30.3 Rechenregeln 353

Im Folgenden zeigen wir, wie man Brüche in unserer Bruchklasse dividieren kann:

$ python3Python 3.2.3 (default, Oct 19 2012, 19:53:57)[GCC 4.7.2] on linux2Type "help", "copyright", "credits" or "license" for more information.>>> from brueche import Bruch>>> x = Bruch(2,3)>>> y = Bruch(15,4)>>> print(x/y)8/45

30.3.3 Addition von Brüchen

Im Vergleich zur Multiplikation und Division von Brüchen gestaltet sich die Addition eben-so wie die Subtraktion etwas schwieriger. Bevor man zwei Brüche addieren kann, müs-sen sie zuerst gleichnamig gemacht werden, d.h. sie müssen so erweitert werden, dassanschließend die beiden Nenner gleich sind. Sind die Nenner gleich, können die Zählereinfach addiert werden.

Beispiel:

2

3+ 3

4= 2

3· 4

4+ 3

4· 3

3= 2 ·4+3 ·3

3 ·4= 17

12

Nun können wir die Python-Methode für die Addition (__add__) angeben:

def __add__(self,other):s = fraction(self.zaehler*other.nenner+other.zaehler * self.

nenner,self.nenner*other.nenner)

s.reduce()return s

30.3.4 Subtraktion von Brüchen

Die Subtraktion läuft analog zur Addition, d.h. erst müssen die Brüche gleichnamig ge-macht werden und dann kann man die Zähler subtrahieren.

Beispiel:

3

4− 2

3= 3

4· 3

3+ 2

3· 4

4= 3 ·3−2 ·4

3 ·4= 1

12

Die Python-Methode für die Subtraktion (__sub__) kann fast gleich wie die Additionsme-thode geschrieben werden:

def __sub__(self,other):s = Bruch(self.zaehler*other.nenner+other.zaehler * self.nenner,

Page 363: 3446435476_Pytho

354 30 Bruchklasse

self.nenner*other.nenner)s.kuerze()return s

Im Folgenden wollen wir noch die Methoden für die Vergleichsoperatoren von Brüchenangeben, also für „==”, „!=”, „>”, „>=”, „<”, „<=”:

def __eq__(self, other):return self.zaehler * other.nenner == other.zaehler * self.nenner

def __ne__(self, other):return not self.__eq__(other)

def __gt__(self, other):return self.zaehler * other.nenner > other.zaehler * self.nenner

def __ge__(self, other):return self.zaehler * other.nenner >= other.zaehler * self.nenner

def __lt__(self, other):return self.zaehler * other.nenner < other.zaehler * self.nenner

def __le__(self, other):return self.zaehler * other.nenner <= other.zaehler * self.nenner

In Python 2.x genügte es übrigens, eine Methode __cmd__ zu definieren anstelle der obigensechs Methoden. Aber diese Methode ist mit Python 3 entfallen.

30.3.5 Die Bruchklasse im Überblick

Unsere Bruchklasse ist nun fertig, und im Folgenden sehen wir die gesamte Klasse im Über-blick.

class Bruch(object):

def __init__(self,z,n):self.zaehler = zself.nenner = nself.kuerze()

def __str__(self):return str(self.zaehler)+'/'+str(self.nenner)

def ggT(cls,a,b):while b != 0:

a,b = b,a%breturn a

def kuerze(self):g = self.ggT(self.zaehler, self.nenner)self.zaehler = int(self.zaehler/g)self.nenner = int(self.nenner/g)

def __mul__(self,other):p = Bruch(self.zaehler * other.zaehler,

self.nenner * other.nenner)

Page 364: 3446435476_Pytho

30.3 Rechenregeln 355

p.kuerze()return p

def __truediv__(self,other):p = Bruch(self.zaehler * other.nenner,

self.nenner * other.zaehler)p.kuerze()return p

def __add__(self,other):s = Bruch(self.zaehler*other.nenner+other.zaehler * self.nenner,

self.nenner*other.nenner)s.kuerze()return s

def __sub__(self,other):s = Bruch(self.zaehler*other.nenner+other.zaehler * self.nenner,

self.nenner*other.nenner)s.kuerze()return s

def __eq__(self, other):return self.zaehler * other.nenner == other.zaehler * self.nenner

def __ne__(self, other):return not self.__eq__(other)

def __gt__(self, other):return self.zaehler * other.nenner > other.zaehler * self.nenner

def __ge__(self, other):return self.zaehler * other.nenner >= other.zaehler * self.nenner

def __lt__(self, other):return self.zaehler * other.nenner < other.zaehler * self.nenner

def __le__(self, other):return self.zaehler * other.nenner <= other.zaehler * self.nenner

if __name__ == "__main__":x = Bruch(2,6)y = Bruch(3,14)print(x * y)print(x / y)print(x + y)print(x - y)

if x < y:print("x < y")

else:print("x >= y")

print(x)

Page 365: 3446435476_Pytho

31 Mastermind

31.1 Die Ursprünge des Spiels

BILD 31.1 Mastermind

In diesem Kapitel wollen wir einige der Funktio-nen und Programme, die wir im Laufe des Buchesgeschrieben haben, in einer größeren Anwendungzusammenfließen lassen. Wir wollen eine Versiondes Spiels Mastermind schreiben, die in der Lageist, einen Code zu knacken. Mastermind ist einesder erfolgreichsten Spiele der 1970er-Jahre. Bis zumJahr 2000 wurden weltweit ca. 55 Millionen Exem-plare dieses Spiels in über 80 Ländern verkauft.1

Das Spiel wurde im Jahre 1970 von dem israelischenSpieledesigner Mordechai Meirovitz erfunden. Ge-nauer gesagt hat er „nur” die Umsetzung eines al-ten Spiels mit dem Namen „Cows and Bulls” in ei-ne Spielbrettversion erfunden. Dieses „Cows andBulls”2 kann man bis ins 19. Jahrhundert zurückver-folgen. Es wird von zwei Spielern gespielt. Die Spie-lidee von „Mastermind” und „Cows and Bulls” istim Wesentlichen identisch. Während Mastermindfarbige Stifte benutzt, um die Codierung vorzuneh-men, basiert „Cows and Bulls” auf der Benutzungvon Ziffern. Die Beurteilung eines Versuchs wird inMastermind mit schwarzen und weißen Stiften be-urteilt.

1 Toby Nelson: „A Brief History of the MasterMindTM Board Game”, http://www.tnelson.demon.co.uk/mastermind/history.html2 Das Spiel ist auch unter den Namen „Pigs and Bulls” und „Bulls and Cleots” bekannt.

Page 366: 3446435476_Pytho

358 31 Mastermind

31.2 Die Spielregeln

31.2.1 „Bulls and Cows”

Um „Bulls and Cows” spielen zu können, muss man nichts kaufen. Man benötigt ledig-lich einen Stift und Papier. Das Spiel wird normalerweise zu zweit gespielt. Jeder Spielerschreibt eine vierstellige3 Zahl auf ein Blatt Papier. Die Ziffern dieser vierstelligen Zahlmüssen verschieden sein.4 Die Aufgabe des Spiels besteht nun darin, den Geheimcode desMitspielers herauszufinden. Gewonnen hat derjenige, dem es als Erstem gelingt, den Co-de des anderen zu erraten. Um den Code des Mitspielers herauszufinden, darf man einevierstellige Zahl raten. Der Gegner muss diesen Versuch beurteilen. Man gibt an, wie viele„Cows” und wie viele „Bulls” in dem Versuch stecken. Gilt es zum Beispiel die Ziffernfolge„4 3 2 5” des Mitspielers zu erraten und besteht der eigene Versuch aus „4 3 1 2”, so beur-teilt der Mitspieler dies mit zwei Bulls und einer Cow. Die zwei Bullen („Bulls”) bedeuten,dass zwei Ziffern korrekt an der richtigen Stelle geraten worden sind, d.h. die 4 und die 3.Die eine Kuh („Cow”) bedeutet, dass eine Ziffer zwar richtig geraten worden ist, aber an derfalschen Stelle steht. In unserem Fall die Ziffer 2.

Der erste Spieler, der den gesamten Code des Gegners richtig geraten hat, ist der Sieger desSpiels.

Dieses Spiel wurde sehr häufig implementiert. Das erste bekannte Programm nannte sich„moo” und war in PL/I geschrieben.

31.2.2 Mastermind

BILD 31.2 Mastermind als Spielbrett

Die Spielregeln von Mastermind sind analog zu„Bulls and Cows”, aber es werden Farben statt Zif-fern benutzt. Zur Beurteilen gibt es statt „Bulls”schwarze Stecker und statt „Cows” weiße Stecker.

Zum Spielen benutzt man ein käuflich erhältlichesSpielbrett. Statt Zahlen werden hier farbige Steckerverwendet. Ansonsten sind die Spielregeln nahezuanalog.

3 Es können natürlich auch mehr oder weniger Stellen vereinbart werden.4 Allerdings gibt es auch Versionen des Spiels, in der das mehrfache Vorkommen von Ziffern in einer Zahl

erlaubt ist.

Page 367: 3446435476_Pytho

31.3 Kombinatorikmodul 359

31.3 KombinatorikmodulWir wollen nun das Mastermind-Spiel in Python umsetzen, d.h. implementieren. ImLaufe unseres Buchs haben wir bereits alle zur Implementierung unseres Mastermind-Algorithmus notwendigen kombinatorischen Funktionen entwickelt. Wir speichern denfolgenden Programmcode unter combinatorics.py:

import random

def fac(n):if n == 0:

return 1else:

return (fac(n-1) * n)

def permutations(items):n = len(items)if n==0: yield []else:

for i in range(len(items)):for cc in permutations(items[:i]+items[i+1:]):

yield [items[i]]+cc

def k_permutations(items, n):if n==0: yield []else:

for i in range(len(items)):for ss in k_permutations(items, n-1):

if (not items[i] in ss):yield [items[i]]+ss

def random_permutation(list):length = len(list);max = fac(length);index = random.randrange(0, max)i = 0for p in permutations(list):

if i == index:return p

i += 1

def all_colours(colours, positions):colours = random_permutation(colours)for s in k_permutations(colours, positions):

yield(s)

Der Generator all_colours() gehört streng genommen nicht in das obige Modul, da es sichschon um einen für unser Problem maßgeschneiderten Iterator handelt. Er generiert einenIterator für alle k-Permutationen eines sequentiellen Datentyps „colours”.

$ python3Python 3.2.3 (default, Oct 19 2012, 19:53:57)

Page 368: 3446435476_Pytho

360 31 Mastermind

[GCC 4.7.2] on linux2Type "help", "copyright", "credits" or "license" for more information.>>> from combinatorics import all_colours>>> all_colours(["a","b","c","d"],3)<generator object all_colours at 0xb7176a54>>>> list(all_colours(["a","b","c","d"],3))[['d', 'a', 'b'], ['d', 'a', 'c'], ['d', 'b', 'a'], ['d', 'b', 'c'], ['d

', 'c', 'a'], ['d', 'c', 'b'], ['a', 'd', 'b'], ['a', 'd', 'c'], ['a', 'b', 'd'], ['a', 'b', 'c'], ['a', 'c', 'd'], ['a', 'c', 'b'], ['b', 'd', 'a'], ['b', 'd', 'c'], ['b', 'a', 'd'], ['b', 'a', 'c'], ['b', 'c', 'd'], ['b', 'c', 'a'], ['c', 'd', 'a'], ['c', 'd', 'b'], ['c', 'a', 'd'], ['c', 'a', 'b'], ['c', 'b', 'd'], ['c', 'b', 'a']]

>>> list(all_colours("abcd",3))[['c', 'd', 'b'], ['c', 'd', 'a'], ['c', 'b', 'd'], ['c', 'b', 'a'], ['c

', 'a', 'd'], ['c', 'a', 'b'], ['d', 'c', 'b'], ['d', 'c', 'a'], ['d', 'b', 'c'], ['d', 'b', 'a'], ['d', 'a', 'c'], ['d', 'a', 'b'], ['b', 'c', 'd'], ['b', 'c', 'a'], ['b', 'd', 'c'], ['b', 'd', 'a'], ['b', 'a', 'c'], ['b', 'a', 'd'], ['a', 'c', 'd'], ['a', 'c', 'b'], ['a', 'd', 'c'], ['a', 'd', 'b'], ['a', 'b', 'c'], ['a', 'b', 'd']]

Der Unterschied zu unserem k_permutations-Generator besteht darin, dass wir immer miteiner Zufallspermutation starten:

>>> list(all_colours("abc",2))[['a', 'c'], ['a', 'b'], ['c', 'a'], ['c', 'b'], ['b', 'a'], ['b', 'c']]>>> list(all_colours("abc",2))[['a', 'b'], ['a', 'c'], ['b', 'a'], ['b', 'c'], ['c', 'a'], ['c', 'b']]>>> list(all_colours("abc",2))[['c', 'b'], ['c', 'a'], ['b', 'c'], ['b', 'a'], ['a', 'c'], ['a', 'b']]>>> list(all_colours("abc",2))[['c', 'a'], ['c', 'b'], ['a', 'c'], ['a', 'b'], ['b', 'c'], ['b', 'a']]

31.4 Mastermind in PythonNun können wir mit der Implementierung unseres eigentlichen Mastermind-Programmsbeginnen. Unser Programm soll den n-stelligen Farbcode herausfinden, den ein menschli-cher Spieler sich aus einer Menge Farben, also zum Beispiel

colours = ["red","green","blue","yellow","orange","pink"]

ausdenkt.

Die Anzahl der Stellen legen wir in number_of_positions fest. Also zum Beispiel den Wert 4:

number_of_positions = 4

Wir erzeugen eine leere Liste, die später die Versuche und deren Bewertungen enthaltenwird:

guesses = []

Page 369: 3446435476_Pytho

31.4 Mastermind in Python 361

Diese Liste besteht aus 2-Tupeln, z.B.

[(['blue', 'orange', 'red', 'yellow'], (1, 3)), (['blue', 'red', 'yellow', 'orange'], (1, 3))]

Das erste Element eines solchen 2-Tupels entspricht einer Liste mit einer 4-stelligen Farb-auswahl, also zum Beispiel:

['blue', 'orange', 'red', 'yellow']

und das zweite Element entspricht einer Bewertung, also einem 2-Tupel mit einer Zahl alserster Komponente für die Anzahl der schwarzen Stifte (also richtige Farben an der richti-gen Position) und der Anzahl der weißen Stifte (also der richtigen Farben an der falschenStelle):

(1, 3)

Obige Bewertung bedeutet also: Eine Farbe war richtig und stand auch an der richtigenStelle. Drei andere Farben waren zwar richtig, standen aber an der falschen Position.

Als Nächstes erzeugen wir einen Iterator für alle möglichen k-Permutationen der Farben„colours”:

permutation_iterator = all_colours(colours, number_of_positions)

Dann erzeugen wir eine erste Farbauswahl, also vier Zufallsfarben aus der durch „colours”definierten Liste von möglichen Farben:

current_colour_choice = next(permutation_iterator)

Mit dieser Liste erzeugen wir nun einen „Versuch”, d.h. ein Tupel bestehend aus dieserFarbliste und einer Bewertung. Wir setzen die Bewertung auf (0,0), weil wir nichts Nähe-res wissen und weil dieses Tupel für den folgenden Ablauf gesetzt sein muss:

new_guess = (current_colour_choice, (0,0) )

Die while-Schleife erzeugt Versuche, die nicht im Widerspruch zu vorigen Versuchen ste-hen. Als Endekriterium dient die Bewertung, die die Funktion new_evaluation für eineFarbauswahl zurückliefert:

Die Schleife endet, wenn

■ das Programm die Farbauswahl erfolgreich geraten hat, also das Tupel (number_of_positions, 0), in unserem Fall (4,0), zurückgeliefert wird oder

■ die Antworten des menschlichen Mitspielers widersprüchlich waren, d.h. es wird das Tu-pel (-1,0) zurückgeliefert.

Schauen wir uns nun die Funktion new_evaluation genauer an:

def new_evaluation(current_colour_choice):rightly_positioned, permutated = get_evaluation(current_colour_choice)if rightly_positioned == number_of_positions:

return(current_colour_choice, (rightly_positioned, permutated))

Page 370: 3446435476_Pytho

362 31 Mastermind

if not answer_ok((rightly_positioned, permutated)):print("Input Error: Sorry, the input makes no sense")return(current_colour_choice, (-1, permutated))

guesses.append((current_colour_choice, (rightly_positioned, permutated)))

view_guesses()

current_colour_choice = create_new_guess()if not current_colour_choice:

return(current_colour_choice, (-1, permutated))return(current_colour_choice, (rightly_positioned, permutated))

Die Variablen rightly_positioned und permutated entsprechen der Anzahl der schwarzenund der weißen Stifte. Mit der Funktion get_evaluation wird eine Bewertung vom mensch-lichen Mitspieler erfragt. Falls der Wert von rightly_positioned gleich der Anzahl der Posi-tionen ist, also number_of_positions, sind alle Farben geraten und stehen an der richtigenPosition. Mit der Funktion answer_ok wird überprüft, ob die Bewertung des menschlichenMitspielers Sinn macht. So darf zum Beispiel die Summe der Werte von rightly_positionedund permutated nicht größer als die Anzahl der Positionen number_of_positions sein:

if not answer_ok((rightly_positioned, permutated)):print("Input Error: Sorry, the input makes no sense")return(current_colour_choice, (-1, permutated))

Falls die die Bewertung okay war, wird der Versuch und die Bewertung in der Liste derguesses mit der Methode append aufgenommen.

Anschließend wird mit create_new_guess versucht, eine neue mögliche Farbkombinationzu erzeugen, die im Einklang mit den bisherigen Versuchen steht. Falls nicht wird eine -1im Bewertungstupel übergeben:

current_colour_choice = create_new_guess()if not current_colour_choice:

return(current_colour_choice, (-1, permutated))return(current_colour_choice, (rightly_positioned, permutated))

Im Folgenden finden Sie ein komplettes Listing von mastermind.py:

import randomfrom combinatorics import all_colours

def inconsistent(p, guesses):""" the function checks, if a permutation p, i.e. a list of

colours like p = ['pink', 'yellow', 'green', 'red'] is consistentwith the previous colours. Each previous colour permutation guess[0]compared (check()) with p has to return the same amount of blacks(rightly positioned colours) and whites (right colour at wrongposition) as the corresponding evaluation (guess[1] in thelist guesses) """

for guess in guesses:res = check(guess[0], p)(rightly_positioned, permutated) = guess[1]

Page 371: 3446435476_Pytho

31.4 Mastermind in Python 363

if res != [rightly_positioned, permutated]:return True # inconsistent

return False # i.e. consistent

def answer_ok(a):""" checking of an evaluation given by the human player makes

sense. 3 blacks and 1 white make no sense for example. """(rightly_positioned, permutated) = aif (rightly_positioned + permutated > number_of_positions) \

or (rightly_positioned + permutated < len(colours) -number_of_positions):

return Falseif rightly_positioned == 3 and permutated == 1:

return Falsereturn True

def get_evaluation(guess):""" asks the human player for an evaluation """show_current_guess(guess)rightly_positioned = int(input("Blacks: "))permutated = int(input("Whites: "))return (rightly_positioned, permutated)

def new_evaluation(current_colour_choice):""" This function gets a new colour choice, checks

the consistency of this choice, adds the choice together withthe evaluation as a tuple to the list of guesses.It returns a tuple with the colour choice and the evaluation of this

choice."""rightly_positioned, permutated = get_evaluation(current_colour_choice)if rightly_positioned == number_of_positions:

return(current_colour_choice, (rightly_positioned, permutated))

if not answer_ok((rightly_positioned, permutated)):print("Input Error: Sorry, the input makes no sense")return(current_colour_choice, (-1, permutated))

guesses.append((current_colour_choice, (rightly_positioned, permutated)))

view_guesses()

current_colour_choice = create_new_guess()if not current_colour_choice:

return(current_colour_choice, (-1, permutated))return(current_colour_choice, (rightly_positioned, permutated))

def check(p1, p2):""" check() calculates the number of bulls (blacks) and cows (whites)

of two permutations """blacks = 0whites = 0for i in range(len(p1)):

Page 372: 3446435476_Pytho

364 31 Mastermind

if p1[i] == p2[i]:blacks += 1

else:if p1[i] in p2:

whites += 1return [blacks, whites]

def create_new_guess():""" a new guess is created, which is consistent to the

previous guesses """next_choice = next(permutation_iterator)while inconsistent(next_choice, guesses):

try:next_choice = next(permutation_iterator)

except StopIteration:print("Error: Your answers were inconsistent!")return ()

return next_choice

def show_current_guess(new_guess):""" The current guess is printed to stdout """print("New Guess: ",end=" ")

for c in new_guess:print(c, end=" ")

print()

def view_guesses():""" The list of all guesses with the corresponding evaluations

is printed """print("Previous Guesses:")for guess in guesses:

guessed_colours = guess[0]for c in guessed_colours:

print(c, end=" ")for i in guess[1]:

print(" %i " % i, end=" ")print()

colours = ["red","green","blue","yellow","orange","pink"]guesses = []number_of_positions = 4

permutation_iterator = all_colours(colours, number_of_positions)current_colour_choice = next(permutation_iterator)

new_guess = (current_colour_choice, (0,0) )while (new_guess[1][0] == -1) or (new_guess[1][0] != number_of_positions)

:print(guesses)new_guess = new_evaluation(new_guess[0])

Page 373: 3446435476_Pytho

32 Textklassifikation

32.1 Einführung in die Textklassifikation

BILD 32.1 Textklassifikation als Prisma

Die Klassifikation und Kategorisierung vonDokumenten ist ein Thema der Informati-onswissenschaft, eine Wissenschaft, die sichmit der Untersuchung von Informationenund Wissen beschäftigt. Dabei geht es um dieAuswertung, die Selektion, das Erschließen,das Suchen und Finden sowie das Bereitstel-len und Verwerten von Informationen. Wennes um Dokumente geht, die sich Klassen oderKategorien zuordnen lassen, kann man sichder automatischen Dokumentenklassifikati-on bedienen. Sehr häufig handelt es sich bei den Dokumenten um reine Textdokumente,und dann spricht man von Textklassifikation. Dokumentenklassifikation sind allgemeineDokumente, die auch Bilder, Grafiken, Musik, Partituren usw. enthalten können.

Dies mag ziemlich abstrakt klingen, aber heutzutage gibt es viele Situationen, in denenFirmen die automatische Klassifikation bzw. Kategorisierung von Dokumenten benötigen.Man denke nur beispielsweise an eine große Firma, die jeden Tag Berge von Eingangs-post zu bewältigen hat, sowohl in elektronischer als auch in papiergebundener Form. Üb-licherweise hat ein großer Teil dieser Eingangspost weder einen korrekten Betreff noch eineNamens- oder Abteilungsnennung, aus der eindeutig hervorgeht, wohin, also zu welcherPerson oder Abteilung, das Poststück weitergeleitet werden kann. Jemand muss also diesePost lesen und entscheiden, um welches Anliegen es geht, also beispielsweise „Adressände-rung”, „Beschwerdeschreiben”, „Anfrage zu einem Produkt” usw. Dieser „Jemand” könnteauch ein automatisches Textklassifikationssystem sein.

Die Geschichte der automatischen Textklassifikation geht zurück bis in an den Anfang der1960er-Jahre. Aber erst der unglaubliche Anstieg in verfügbaren Online-Dokumenten inden letzten beiden Jahrzehnten im Zusammenhang mit der Expansion des Internets hatsich das Interesse an der automatischen Dokumentenklassifikation und dem Data Miningerneuert und intensiviert. Anfänglich war die Dokumentenklassifikation auf heuristischeMethoden fixiert, d.h. man versuchte, die Aufgaben dadurch zu lösen, dass man Experten-wissen in Regelwerke packte. Dieser Ansatz stellte sich als hochgradig ineffizient für kom-

Page 374: 3446435476_Pytho

366 32 Textklassifikation

plexe Aufgaben heraus, sodass heutzutage der Fokus auf automatischem Lernen und derAnwendung von Clustering-Methoden liegt.

Die Aufgabe der Textklassifikation besteht darin, Dokumente in Abhängigkeit ihres seman-tischen Inhaltes einer oder mehreren Kategorien oder Klassen zuzuordnen.

Man unterscheidet eine Lernphase und eine Klassifikationsphase. Die Lernphase kannman grob in drei Arten unterteilen:

■ Die notwendige Information für das Lernen, also die korrekte Labelung der Dokumente,erfolgt beim überwachten Lernen durch „Eingriff” eines externen Mechanismus, übli-cherweise menschliches Feedback.

■ Unter halbüberwachtem Lernen (semi-supervised learning) versteht man eine Mi-schung aus überwachtem und nicht überwachtem Lernen.

■ Nicht überwachtes Lernen (automatisches Lernen) erfolgt komplett ohne Einwirkungvon außen.

Wir werden einen Textklassifikator in Python implementieren, der auf dem naiven Bayes-Klassifikator basiert. Naive Bayes ist der am häufigsten benutzte Textklassifikator und stehtim Fokus der Forschung. Ein naiver Bayes-Klassifikator geht von der strengen Unabhän-gigkeit der verwendeten Merkmale aus, d.h. das Vorhandensein oder die Abwesenheit ei-nes bestimmten Merkmals in einer Klasse steht in keiner Beziehung zum Vorhandenseinbzw. Abwesenheit von allen anderen Merkmalen. Eine „naive” Annahme, die dennoch inder praktischen Anwendung zu hervorragenden Ergebnissen führt.

Page 375: 3446435476_Pytho

32.2 Textklassifikation: Aufgabe 367

32.2 Textklassifikation: AufgabeGegeben ist eine Menge von Klassen C = {c1,c2, ...cm} und eine Menge von DokumentenD = {d1,d2, ...dn}.

Die Aufgabe der Textklassifikation besteht nun darin, jedem Paar (ci ,d j ) ∈ C ×D , wobei1 ≤ i ≤ m ∧ 1 ≤ j ≤ n) gilt, einen Wert 0 oder 1 zuzuordnen, d.h. den Wert 0, wenn dasDokument d j nicht zur Klasse ci gehört, ansonsten 1.

Es gibt verschiedene Ansätze, dieses Entscheidungsproblem zu lösen. Die wichtigsten sindwohl:

■ Naive Bayes

■ Support Vector Machine

■ Nearest Neighbour

32.3 Naive-Bayes-Klassifikator

32.3.1 Definition

Ein Naive-Bayes-Klassifikator ist ein probabilistischer Klassifikator, der nach dem engli-schen Mathematiker Thomas Bayes benannt ist. Er leitet sich aus dem Bayes-Theorem ab.Beim Naive-Bayes-Klassifikator wird jedes Objekt, in unserem Fall jedem Textdokument,eine Klasse oder Kategorie zugeordnet, zu der es mit der größten Wahrscheinlichkeit ge-hört. Die zugrunde liegende Annahme beim Naive-Bayes-Klassifikator besteht darin, voneiner strengen Unabhängigkeit der verwendeten Merkmale auszugehen, d.h. das Vorhan-densein oder die Abwesenheit eines bestimmten Merkmales in einer Klasse steht in kei-ner Beziehung zum Vorhandensein bzw. Abwesenheit von allen anderen Merkmalen. Die-se Annahme ist mit dem Namensbestandteil „Naive” gemeint. Eine Voraussetzung, die inder praktischen Anwendung des Naive-Bayes-Konzeptes meist nicht gegeben ist, aber den-noch werden häufig mit diesem „naiven” Ansatz ausreichend gute Ergebnisse erzielt, unddas bei einer schnellen Berechenbarkeit, durch die sich der NBK auszeichnet.

32.3.2 Bayes-Theorem

Definition unabhängiger Ereignisse:

Zwei Ereignisse E und F sind unabhängig, wenn das Auftreten von E die Wahrscheinlich-keit, dass F eintritt, nicht beeinflusst und umgekehrt:

P (E |F ) = P (E) und P (F |E) = P (F )

Die „bedingte Wahrscheinlichkeit” ist die Wahrscheinlichkeit des Eintretens eines Ereignis-ses A unter der Bedingung, dass das Eintreten eines anderen Ereignisses B bereits bekanntist.

Page 376: 3446435476_Pytho

368 32 Textklassifikation

Für die bedingte Wahrscheinlichkeit gilt:

P (A|B) = P (A∩B)

P (B)

Bayes-Theorem:

P (A|B) = P (B |A)P (A)

P (B)

P (A|B) bezeichnet die bedingte Wahrscheinlichkeit für ein Ereignis A unter der Bedingung,dass B gilt. P (A|B) wird im Zusammenhang mit dem Bayes-Theorem häufig auch als A-posteriori-Wahrscheinlichkeit bezeichnet.

P (B) ist die A-priori-Wahrscheinlichkeit für das Ereignis B und P (A) ist die A-priori-Wahrscheinlichkeit für das Ereignis A.

(B |A) bezeichnet die bedingte Wahrscheinlichkeit für ein Ereignis B unter der Bedingung,dass A gilt.

Ein Vorteil des Naive-Bayes-Klassifikators besteht darin, dass er nur eine kleine Trainings-menge benötigt, um die zur Klassifikation notwendigen Parameter einzustellen.

32.4 Formale Herleitung derNaive-Bayes-Klassifikation

Gegeben ist eine Menge von Klassen C = {c1,c2, ...cm} und eine Menge von DokumentenD = {d1,d2, ...dn}.

Jedes Dokument ist mit einer Klasse etikettiert1.

Die Menge D von Dokumenten wird benötigt, um den Klassifikator zu trainieren.

Die Klassifikation besteht darin, die wahrscheinlichste Klasse für ein unbekanntes Doku-ment auszuwählen.

Wir bezeichnen die Anzahl der Vorkommen eines Wortes wt in einem Dokument di mitNi t .

NCt bezeichnet die Anzahl der Vorkommen eines Wortes wt in allen Dokumenten einer

gegebenen Klasse C.

P (di |c j ) ist 1, falls di mit c j etikettiert ist, ansonsten 0.

Die Wahrscheinlichkeit für ein Wort wt unter der Bedingung, dass die Klasse c j gegebenist, wird wie folgt berechnet:

P (wt |c j ) = 1+Nc jt

|V |+|V |∑

sN

C js

1 englisch: labelled

Page 377: 3446435476_Pytho

32.4 Formale Herleitung der Naive-Bayes-Klassifikation 369

Die Wahrscheinlichkeit P (c j ) für eine Klasse c j ist der Quotient aus der Anzahl der Doku-mente der Klasse c j und der Anzahl der Dokumente aller Klassen, d.h. des Lernsets:

P (c j ) =

|D|∑i=1

P (di |c j )

|D|Nun kommen wir zu der Formel, die wir zur Klassifikation eines Dokuments benötigen,d.h. die Wahrscheinlichkeit für eine Klasse c j unter der Bedingung, dass ein Dokument di

gegeben ist:

P (c j |di ) =P (c j )

di∏k=1

P (wdi k|c j )

|C |∑r=1

(P (cr )|di |∏k=1

P (wdi k|cr ))

Leider ist die vorherige Formel für P (c|di ) numerisch nicht stabil, weil der Nenner wegenfortschreitender Rundungsfehler Null werden kann. Wir ändern dies, indem wir den Kehr-wert berechnen und den Ausdruck als Summe von stabilen Quotienten berechnen:

1

P (c|d)

Dies führt zu folgendem Ausdruck:

1

P (c|d)= P (c1)

P (c)

|d |∏k=1

(1+N c1dk

)(|V |+|V |∑s=1

N cs )

(1+N cdk

)(|V |+|V |∑s=1

N c1s )

+ . . .+ P (c|C |)P (c)

|d |∏k=1

(1+Nc|C |dk

)(|V |+|V |∑s=1

N cs )

(1+N cdk

)(|V |+|V |∑s=1

Nc|C |s )

Die folgenden Zwischenschritte haben wir oben ausgelassen:

1

P (c|d)=

|C |∑r=1

(P (cr )|d |∏

k=1P (wk |cr ))

P (c)|d |∏

k=1P (wk |c)

=

|C |∑r=1

(P (cr )|d |∏

k=1

1+N crdk

|V |+|V |∑s=1

N crs

)

P (c)|d |∏

k=1

1+N cdk

|V |+|V |∑s=1

N cs

Page 378: 3446435476_Pytho

370 32 Textklassifikation

=

(P (c1)|d |∏

k=1

1+N c1dk

|V |+|V |∑s=1

N c1s

)

P (c)|d |∏

k=1

1+N cdk

|V |+|V |∑s=1

N cs

+

(P (c2)|d |∏

k=1

1+N c2dk

|V |+|V |∑s=1

N c2s

)

P (c)|d |∏

k=1

1+N cdk

|V |+|V |∑s=1

N cs

+ . . .+

(P (c|C |)|d |∏

k=1

1+N |C |dk

|V |+|V |∑s=1

N |C |s

)

P (c)|d |∏

k=1

1+N cdk

|V |+|V |∑s=1

N cs

32.5 Textklassifikation in PythonPython ist ideal für die Textklassifikation wegen seiner Stringklasse und deren mächtigerMethoden. Weiterhin werden mit dem Modul re leistungsstarke Tools zur Verfügung ge-stellt, die weit über den Rahmen anderer Programmiersprachen hinausgehen.

Die vorliegende Python-Implementierung eines Naive-Bayes-Klassifikators ist nicht auf Ef-fizienz optimiert.

Die Dokumente werden in unserer Implementierung nach dem sogenannten „bag ofwords”-Modell implementiert, was wir im folgenden Diagramm illustrieren:

32.5.1 BagOfWords-Klasse

Die Klasse BagOfWords verwaltet im Wesentlichen ein Dictionary self.__bag_of_words, dasWörter mit ihren Häufigkeiten enthält bzw. zählt.

class BagOfWords(object):""" Implementing a bag of words, words corresponding with their

frequency of usages in a "document" for usage by the Documentclass, DocumentClass class and the Pool class."""

def __init__(self):self.__number_of_words = 0self.__bag_of_words = {}

Page 379: 3446435476_Pytho

32.5 Textklassifikation in Python 371

def __add__(self,other):""" Overloading of the "+" operator to join two BagOfWords """erg = BagOfWords()sum = erg.__bag_of_wordsfor key in self.__bag_of_words:

sum[key] = self.__bag_of_words[key]if key in other.__bag_of_words:

sum[key] += other.__bag_of_words[key]for key in other.__bag_of_words:

if key not in sum:sum[key] = other.__bag_of_words[key]

return erg

def add_word(self,word):""" A word is added in the dictionary __bag_of_words"""self.__number_of_words += 1if word in self.__bag_of_words:

self.__bag_of_words[word] += 1else:

self.__bag_of_words[word] = 1

def len(self):""" Returning the number of different words of an object """return len(self.__bag_of_words)

def Words(self):""" Returning a list of the words contained in the object """return self.__bag_of_words.keys()

def BagOfWords(self):""" Returning the dictionary, containing the words (keys) with

their frequency (values)"""return self.__bag_of_words

def WordFreq(self,word):""" Returning the frequency of a word """if word in self.__bag_of_words:

return self.__bag_of_words[word]else:

return 0

32.5.2 Document-Klasse

Die Klasse Document wird sowohl für die eigentlichen Textdokumente verwendet als auchals Basisklasse für die DocumentClass, also eine Klasse im Sinne der Textklassifikation.

Page 380: 3446435476_Pytho

372 32 Textklassifikation

class Document(object):""" Used both for learning (training) documents and for testing

documents.The optional parameter learn has to be set to True, if a

classificatorshould be trained. If it is a test document learn has to be set

toFalse. """

def __init__(self, vocabulary):self.__name = ""self.__document_class = Noneself._words_and_freq = BagOfWords()Document._vocabulary = vocabulary

def read_document(self,filename, learn=False):""" A document is read. It is assumed, that the document is

eitherencoded in utf-8 or in iso-8859... (latin-1).The words of the document are stored in a Bag of Words, i.e.self._words_and_freq = BagOfWords() """

try:text = open(filename,"r", encoding='utf-8').read()

except UnicodeDecodeError:text = open(filename,"r", encoding='latin-1').read()

text = text.lower()words = re.split("[^\wäöüÄÖÜß]*",text)

self._number_of_words = 0for word in words:

self._words_and_freq.add_word(word)if learn:

Document._vocabulary.add_word(word)

def __add__(self,other):""" Overloading the "+" operator. Adding two documents consists

in adding the BagOfWords of the Documents """res = Document(Document._vocabulary)res._words_and_freq = self._words_and_freq + other.

_words_and_freqreturn res

def vocabulary_length(self):""" Returning the length of the vocabulary """return len(Document._vocabulary)

def WordsAndFreq(self):""" Returning the dictionary, containing the words (keys) withtheir frequency (values) as contained in the BagOfWords attributeof the document"""return self._words_and_freq.BagOfWords()

Page 381: 3446435476_Pytho

32.5 Textklassifikation in Python 373

def Words(self):""" Returning the words of the Document object """d = self._words_and_freq.BagOfWords()return d.keys()

def WordFreq(self,word):""" Returning the number of times the word "word" appeared in thedocument """bow = self._words_and_freq.BagOfWords()if word in bow:

return bow[word]else:

return 0

def __and__(self, other):""" Intersection of two documents. A list of words occuring inboth documents is returned """intersection = []words1 = self.Words()for word in other.Words():

if word in words1:intersection += [word]

return intersection

32.5.3 DocumentClass-Klasse

Diese Klasse implementiert die Klassen im Sinne der Textklassifikation.

class DocumentClass(Document):def __init__(self, vocabulary):

Document.__init__(self, vocabulary)self._number_of_docs = 0

def Probability(self,word):""" returns the probability of the word "word" given the class "

self" """voc_len = Document._vocabulary.len()SumN = 0for i in range(voc_len):

SumN = DocumentClass._vocabulary.WordFreq(word)N = self._words_and_freq.WordFreq(word)erg = 1 + Nerg /= voc_len + SumNreturn erg

def __add__(self,other):""" Overloading the "+" operator. Adding two DocumentClass

objectsconsists in adding the BagOfWords of the DocumentClass objects

"""

Page 382: 3446435476_Pytho

374 32 Textklassifikation

res = DocumentClass(self._vocabulary)res._words_and_freq = self._words_and_freq + other.

_words_and_freq

return res

def SetNumberOfDocs(self, number):self._number_of_docs = number

def NumberOfDocuments(self):return self._number_of_docs

32.5.4 Pool-Klasse

Diese Klasse verwaltet die Dokumentenklassen und das Vokabular. Außerdem befindensich hier die allgemeinen Lern- und Klassifikationsmethoden.

class Pool(object):def __init__(self):

self.__document_classes = {}self.__vocabulary = BagOfWords()

def sum_words_in_class(self, dclass):""" The number of times all different words of a dclass appear in

a class """sum = 0for word in self.__vocabulary.Words():

WaF = self.__document_classes[dclass].WordsAndFreq()if word in WaF:

sum += WaF[word]return sum

def learn(self, directory, dclass_name):""" directory is a path, where the files of the class with the

namedclass_name can be found """

x = DocumentClass(self.__vocabulary)dir = os.listdir(directory)for file in dir:

d = Document(self.__vocabulary)print(directory + "/" + file)d.read_document(directory + "/" + file, learn = True)x = x + d

self.__document_classes[dclass_name] = xx.SetNumberOfDocs(len(dir))

def Probability(self, doc, dclass = ""):

Page 383: 3446435476_Pytho

32.5 Textklassifikation in Python 375

"""Calculates the probability for a class dclass given a documentdoc"""

if dclass:sum_dclass = self.sum_words_in_class(dclass)prob = 0

d = Document(self.__vocabulary)d.read_document(doc)

for j in self.__document_classes:sum_j = self.sum_words_in_class(j)prod = 1for i in d.Words():

wf_dclass = 1 + self.__document_classes[dclass].WordFreq(i)

wf = 1 + self.__document_classes[j].WordFreq(i)r = wf * sum_dclass / (wf_dclass * sum_j)prod *= r

prob += prod * self.__document_classes[j].NumberOfDocuments() / self.__document_classes[dclass].NumberOfDocuments()

if prob != 0:return 1 / prob

else:return -1

else:prob_list = []for dclass in self.__document_classes:

prob = self.Probability(doc, dclass)prob_list.append([dclass,prob])

prob_list.sort(key = lambda x: x[1], reverse = True)return prob_list

def DocumentIntersectionWithClasses(self, doc_name):res = [doc_name]for dc in self.__document_classes:

d = Document(self.__vocabulary)d.read_document(doc_name, learn=False)o = self.__document_classes[dc] & dintersection_ratio = len(o) / len(d.Words())res += (dc, intersection_ratio)

return res

Die obigen Klassen befinden sich in der Datei „NaiveBayes.py” in unserem Beispielver-zeichnis.

Im Beispielverzeichnis befinden sich auch die Verzeichnisse „learn” und „test” mit Lern-und Testsets für die folgende Beispielanwendung zur Textklassifikation. Dabei handelt essich um verschiedene Klassen mit Witzen:

Page 384: 3446435476_Pytho

376 32 Textklassifikation

from NaiveBayes import Poolimport os

DClasses = ["clinton", "lawyer", "math", "medical", "music", "sex"]

base = "learn/"p = Pool()for i in DClasses:

p.learn(base + i, i)

base = "learn/"for i in DClasses:

dir = os.listdir(base + i)for file in dir:

res = p.Probability(base + i + "/" + file)print(i + ": " + file + ": " + str(res))

Startet man das Programm, erhält man das folgende Ergebnis:

bernd@saturn:~/bodenseo/python/beispiele$ python3 NaiveBayes_test.py |head

learn/clinton/clinton20.txtlearn/clinton/clinton15.txtlearn/clinton/clinton56.txtlearn/clinton/clinton36.txtlearn/clinton/clinton10.txtlearn/clinton/clinton1.txtlearn/clinton/clinton14.txtlearn/clinton/clinton69.txtlearn/clinton/clinton25.txtlearn/clinton/clinton34.txt...

und dann folgen die Klassifikationsergebnisse:

...clinton: clinton20.txt: [['clinton', 1.0], ['medical', 9.901960867214498e

-22], ['sex', 7.68058228879274e-28], ['music', 2.592331193036529e-29], ['lawyer', 2.3811580908948818e-29], ['math', 1.948648939490094e-35]]

clinton: clinton15.txt: [['clinton', 0.9999997364637545], ['music',2.441826815920904e-07], ['sex', 1.7744976589393507e-08], ['medical',1.2295982769832164e-09], ['lawyer', 3.674688944524231e-10], ['math',1.1520143389502419e-11]]

clinton: clinton56.txt: [['clinton', 0.9999999994344095], ['music',5.520351011801854e-10], ['math', 5.72601686083638e-12], ['lawyer',5.1459928139047106e-12], ['sex', 2.4074110973205754e-12], ['medical',2.760021719870858e-13]]

clinton: clinton36.txt: [['clinton', 0.9999999992386299], ['music',6.914992628977959e-10], ['lawyer', 6.648015266828207e-11], ['sex',

Page 385: 3446435476_Pytho

32.5 Textklassifikation in Python 377

1.7642685540607824e-12], ['math', 1.5179052741540378e-12], ['medical', 1.0839574950384394e-13]]

clinton: clinton10.txt: [['clinton', 0.9999999999999882], ['music',1.1795522781235521e-14], ['sex', 3.486402118341457e-18], ['medical',7.514449565885636e-19], ['math', 4.419633484697798e-19], ['lawyer',2.613944672153929e-19]]

clinton: clinton1.txt: [['clinton', 1.0], ['lawyer', 3.966110616318117e-39], ['music', 4.071780957518354e-44], ['sex', 3.2215177549148766e-45], ['medical', 1.0029914377606443e-45], ['math',1.6849007485121964e-51]]

clinton: clinton14.txt: [['clinton', 1.0], ['music', 4.6481471984787215e-32], ['medical', 4.914323838547423e-42], ['sex', 1.990905914149136e-42], ['lawyer', 1.4639896765847644e-42], ['math', 1.112918004222222e-46]]

clinton: clinton69.txt: [['clinton', 0.9999999999999825], ['music',1.7595454063410785e-14], ['lawyer', 1.64894907605748e-17], ['math',1.4206270940103888e-18], ['medical', 7.215593474577993e-20], ['sex',4.595073346783043e-21]]

clinton: clinton25.txt: [['clinton', 0.9999999998207063], ['music',1.7923166491945231e-10], ['lawyer', 5.502049137268664e-14], ['sex',5.752336117034771e-15], ['math', 1.1389115763215046e-15], ['medical',4.1549302112730125e-17]]

...

Page 386: 3446435476_Pytho

33 Lösungen zu denAufgaben

33.1 Lösungen zu Kapitel 5(Sequentielle Datentypen)

1. Aufgabe

Wir haben die folgenden Datentypen kennengelernt:

■ str:Zeichenketten, meistens als Strings bezeichnet

■ : list:Listen

■ tuple:Tupel

■ bytes und bytearry:Binärdaten

2. Aufgabe

Wenn die Variable „liste” eine Liste bezeichnet, dann spricht man das erste Element mitliste[0] an.

Beispiel:

>>> liste = [45,87,79]>>> liste[0]45>>>

3. Aufgabe

Nehmen wir an, dass l eine Variable vom Typ list ist, dann können wir das letzte Elementam einfachsten mit l[-1] ansprechen.

Page 387: 3446435476_Pytho

380 33 Lösungen zu den Aufgaben

Falls Sie l[len(l) - 1] als Lösung gefunden hatten, ist dies auch korrekt, aber weniger elegantals obige Lösung.

Beispiel:

>>> l = ["Hallo", "Welt", "Das letzte Element"]>>> l[-1]'Das letzte Element'>>> l[len(l) - 1]'Das letzte Element'>>>

4. Aufgabe

>>> t = (4,7,9)>>> s = "Ich bin ein String">>> l = [45,98,"787",[3,4]]>>> t2 = (4,8,[45,98])

>>> t[0]4>>> t[3]Traceback (most recent call last):File "<stdin>", line 1, in <module>

IndexError: tuple index out of range

Der höchste Index des Tupels entspricht 2, sodass wir oben versucht hatten, auf einen nichtexistierenden Index zuzugreifen, und erhielten daher die Meldung „IndexError: tuple indexout of range”

>>> t(3)Traceback (most recent call last):File "<stdin>", line 1, in <module>

TypeError: 'tuple' object is not callable

Zugriffe auf Tupel oder Listen benötigen eckige Klammern. Mit runden Klammern wird aufFunktionen zugegriffen. Daher die Fehlermeldung „TypeError: ’tuple’ object is not calla-ble”, d.h. t ist „nicht aufrufbar (not callable), da es keine Funktion, sondern ein Tupel ist.

>>> s[4]'b'>>> s[4] = "x"Traceback (most recent call last):File "<stdin>", line 1, in <module>

TypeError: 'str' object does not support item assignment

Strings sind unveränderlich.

>>> l[2][0] = "g"Traceback (most recent call last):File "<stdin>", line 1, in <module>

TypeError: 'str' object does not support item assignment

Page 388: 3446435476_Pytho

33.2 Lösungen zu Kapitel 6 (Dictionaries) 381

Wir haben wieder versucht, einen String zu verändern.

>>> l[3][0] = "g">>> l[45, 98, '787', ['g', 4]]>>> t2[2][0] = 42

5. Aufgabe

Stellt man die Schrittweite des Sclicing-Operators auf step = 2, kann man die beiden ver-borgenen Sätze extrahieren, wenn man einmal mit dem Index 0 und einmal mit dem Index1 beginnt:

>>> s = 'DIenr diesmt Sdienrn eb eisstte HLielhfree rz,u rd eSrelsbiscthh inlafceh iumnmde rn aucnhs eürbee rofbleürssstiegMmaaxcihmte..'

>>> s[::2]'Der ist der beste Lehrer, der sich nach und nach überflüssig macht.'>>> s[1::2]'In dem Sinne ist Hilfe zur Selbsthilfe immer unsere oberste Maxime.'>>>

Vielleicht interessiert es Sie auch, wie wir den String erzeugt hatten. Um das Folgende zuverstehen, müssen Sie allerdings das Kapitel 26 (Listen-Abstraktion/List Comprehension)bereits bearbeitet haben.

>>> s = "Der ist der beste Lehrer, der sich nach und nach überflüssigmacht."

>>> t = "In dem Sinne ist Hilfe zur Selbsthilfe immer unsere obersteMaxime."

>>> s = "".join(["".join(x) for x in zip(s,t)])>>> s'DIenr diesmt Sdienrn eb eisstte HLielhfree rz,u rd eSre lsbiscthh

inlafceh iumnmde rn aucnhs eürbee rofbleürssstieg Mmaaxcihmte..'>>>

33.2 Lösungen zu Kapitel 6 (Dictionaries)

1. Aufgabe

menu = {"en" : {"file":"File", "new":"New","open":"Open", "save":"Save","save as":"Save as", "print preview":"Print Preview", "print":"Print","close":"Close", "exit":"Exit"},

Page 389: 3446435476_Pytho

382 33 Lösungen zu den Aufgaben

"fr" : {"file":"Fichier", "new":"Nouveau","open":"Ouvrir", "save":"Enregistrer","save as":"Enregistrer sous", "print preview":"Apercu avant impressioner", "print":"Imprimer", "close":"Fermer", "exit":"Quitter"},

"de" : {"file":"Datei", "new":"Neu","open":"Öffnen", "save":"Speichern","save as":"Speichern unter", "print preview":"Druckansicht", "print":"Drucken", "close":"Schließen", "exit":"Verlassen"},

"it" : {"file":"File", "new":"Nuovo","open":"Apri", "save":"Salva","save as":"Salva come", "print preview":"Anteprima distampa", "print":"Stampa", "close":"Chiudi", "exit":"Esci"}

}

language = input("Which language? ")

current_dic = menu[language]

print(current_dic)

Ein Aufruf des obigen Programms liefert beispielsweise folgende Ausgaben:

$ python3 nested_dictionaries.pyWhich language? it{'print preview': 'Anteprima di stampa', 'exit': 'Esci', 'save as': '

Salva come', 'file': 'File', 'close': 'Chiudi', 'print': 'Stampa', 'new': 'Nuovo', 'save': 'Salva', 'open': 'Apri'}

2. Aufgabe

board = { ("a",1) : ("Turm", "weiss"),("d",1) : ("Dame", "weiss"),("e",1) : ("König", "weiss"),("f",1) : ("Läufer", "weiss"),("h",1) : ("Turm", "weiss"),("a",2) : ("Bauer", "weiss"),("b",2) : ("Bauer", "weiss"),("c",2) : ("Bauer", "weiss"),("e",2) : ("Bauer", "weiss"),("f",2) : ("Bauer", "weiss"),("g",2) : ("Bauer", "weiss"),("h",2) : ("Bauer", "weiss"),("c",3) : ("Springer", "weiss"),("f",3) : ("Springer", "weiss"),("d",4) : ("Bauer", "weiss"),("h",4) : ("Läufer", "weiss"),("c",6) : ("Bauer", "schwarz"),("d",6) : ("Bauer", "schwarz"),("d",6) : ("Läufer", "schwarz"),("h",6) : ("Bauer", "schwarz"),("a",7) : ("Bauer", "schwarz"),

Page 390: 3446435476_Pytho

33.3 Lösungen zu Kapitel 8 (Verzweigungen) 383

("b",7) : ("Bauer", "schwarz"),("d",7) : ("Springer", "schwarz"),("e",7) : ("Bauer", "schwarz"),("f",7) : ("Bauer", "schwarz"),("g",7) : ("Bauer", "schwarz"),("a",8) : ("Turm", "schwarz"),("d",8) : ("Dame", "schwarz"),("e",8) : ("König", "schwarz"),("f",8) : ("Läufer", "schwarz"),("g",8) : ("Springer", "schwarz"),("h",8) : ("Turm", "schwarz")

}

print(board[("g",8)])

Ruft man das Programm auf, erhält man folgende Ausgabe:

$ python3 chess.py('Springer', 'schwarz')

33.3 Lösungen zu Kapitel 8(Verzweigungen)

1. Aufgabe

#!/usr/bin/python3

jahr = int(input("Jahreszahl: "))

if jahr % 4 == 0:if jahr % 100 == 0:

if jahr % 400 == 0:schaltjahr = True

else:schaltjahr = False

else:schaltjahr = True

else:schaltjahr = False

if schaltjahr:print(jahr, "ist ein Schaltjahr")

else:print(jahr, "ist kein Schaltjahr")

Alternativ zu obiger Lösung kann man die Aufgabe auch wie folgt lösen. In diesem Fallmüssen wir die Variable schaltjahr auf False vorbesetzen:

Page 391: 3446435476_Pytho

384 33 Lösungen zu den Aufgaben

#!/usr/bin/python3

jahr = int(input("Jahreszahl: "))

schaltjahr = Falseif jahr % 4 == 0:

schaltjahr = Trueif jahr % 100 == 0:

schaltjahr = Falseif jahr % 400 == 0:

schaltjahr = True

if schaltjahr:print(jahr, "ist ein Schaltjahr")

else:print(jahr, "ist kein Schaltjahr")

2. Aufgabe

hours = int(input("hours: "))minutes = int(input("minutes: "))seconds = int(input("seconds: "))

if seconds == 59:seconds = 0if minutes == 59:

minutes = 0if hours == 23:

hours = 0else:

hours += 1else:

minutes += 1else:

seconds += 1

# Wandlung in Stringshours = str(hours)if len(hours) < 2:

hours = "0" + hoursminutes = str(minutes)if len(minutes) < 2:

minutes = "0" + minutesseconds = str(seconds)if len(seconds) < 2:

seconds = "0" + seconds

print("New time: " + hours + ":" + minutes + ":" + seconds)

Page 392: 3446435476_Pytho

33.4 Lösungen zu Kapitel 9 (Schleifen) 385

Mit Hilfe einer formatierten Ausgabe kann man das Hinzufügen von Nullen bei einstel-ligen Werten einfacher realisieren. Die obige Lösung hat zum einen den Vorteil, dass siekeinen Vorgriff auf noch nicht behandelte Themen macht, und zum anderen wird die if-Anweisung bei der Wandlung weiter vertieft. Allerdings möchten wir Ihnen die elegantereLösung nicht vorenthalten:

hours = int(input("hours: "))minutes = int(input("minutes: "))seconds = int(input("seconds: "))

if seconds == 59:seconds = 0if minutes == 59:

minutes = 0if hours == 23:

hours = 0else:

hours += 1else:

minutes += 1else:

seconds += 1

print("New time: {0:02d}:{1:02d}:{2:02d}".format(hours,minutes,seconds))

33.4 Lösungen zu Kapitel 9(Schleifen)

1. Aufgabe

Haben Sie die den römischen Zahlen zugrunde liegenden Regeln erkannt?

Die Regeln:

1. Stehen gleiche Zahlen nebeneinander, werden sie addiert.

Beispiele: III = 3 oder XX = 20

2. Es dürfen höchstens drei Grundzahlen nebeneinander stehen. Ausnahme „M”.

3. Steht eine Zahl mit einem kleineren Wert neben einer Zahl mit einem größeren Wert, sowird der kleinere Wert vom größeren subtrahiert.

Beispiele: IV entspricht 5 - 1 also 4 oder XL entspricht 50 - 10.

4. Die Grundzahlen I, X und C dürfen nur von der nächsthöheren Zahl (Zahlzeichen) sub-trahiert werden.

roman_number = input("Bitte römische Zahl eingeben: ")

Page 393: 3446435476_Pytho

386 33 Lösungen zu den Aufgaben

decimal = 0roman_digits = {

'I': 1,'V': 5,'X': 10,'L': 50,'C': 100,'D': 500,'M': 1000

}

previous_value = 0for char in roman_number:

if char in roman_digits:if roman_digits[char] > previous_value:

decimal -= previous_valueelse:

decimal += previous_valueprevious_value = roman_digits[char]

decimal += previous_value

print("Ergebnis: " + str(decimal))

Ein Aufruf des Programms sieht so aus:

$ python3 roman2decimal.pyBitte römische Zahl eingeben: MCDXLVIIIErgebnis: 1448

2. Aufgabe

Es gibt natürlich viele Wege, diese Aufgabe zu lösen. Man hätte auch eine for-Schleife wäh-len können. Wir haben uns für eine Lösung entschieden, bei der wir eine while-Schleifebeenden, sobald die zurückgelegte Entfernung kleiner als eine Schranke eps ist:

sum = 0old_sum = -1eps = 0.0001i = 1

while sum - old_sum > eps:old_sum = sumsum += ii = i / 2print(i, sum)

Die Ausgabe dieses Programms liefert uns folgende Werte:

$ python3 frog.py | more0.5 1

Page 394: 3446435476_Pytho

33.4 Lösungen zu Kapitel 9 (Schleifen) 387

0.25 1.50.125 1.750.0625 1.8750.03125 1.93750.015625 1.968750.0078125 1.9843750.00390625 1.99218750.001953125 1.996093750.0009765625 1.9980468750.00048828125 1.99902343750.000244140625 1.999511718750.0001220703125 1.9997558593756.103515625e-05 1.99987792968753.0517578125e-05 1.99993896484375

Aus den numerischen Werten liegt es nahe zu folgern, dass der Frosch mit seiner Sprung-weise nicht mehr als 2 Meter zurücklegen wird. Damit schafft er es also nicht, die 2,5 mbreite Straße zu überqueren.

Übrigens kann man auch den n-ten Summenwert der folgenden Reihe entnehmen:

1+ 3

5+ 7

4+ 15

8+ . . .

2n −1

2n−1

3. Aufgabe

Eine Lösung sieht wie folgt aus:

koerner = 0

for i in range(64):koerner += 2 ** i

print(koerner)

Startet man dieses Programm, erhält man die in der Aufgabenstellung angegebene Anzahlvon Weizenkörnern:

$ python3 weizenkorn.py18446744073709551615

In obiger Lösung haben wir in jedem Schritt der Schleife eine Exponentiation, die natürlichviel Rechenzeit benötigt. Wir können diese Exponentiation durch eine Summe ersetzen,indem wir eine zusätzliche Variable „zuwachs” einführen, die die Menge der Weizenkörnerpro Schritt beinhaltet:

koerner = 0zuwachs = 1

for i in range(64):koerner += zuwachs

Page 395: 3446435476_Pytho

388 33 Lösungen zu den Aufgaben

zuwachs += zuwachs

print(koerner)

Aber wenn wir nun schon am „schneller machen” sind, dann machen wir es gleich richtigschnell. Um diese Aufgabe zu lösen, haben wir uns ja bewusst unwissend gestellt. Für die-ses Problem gibt es schließlich eine Formel. Die Anzahl der Körner auf n Feldern berechnetsich als:

n∑i=0

2i = 2n+1 −1

Damit brauchen wir für die Lösung der Aufgabe nur noch die interaktive Python-Shell:

>>> 2 ** 64 -118446744073709551615>>>

Für die mathematisch Interessierten: Allgemein gilt für die Summe von Potenzen mit kon-stanter Basis k und steigendem Exponenten:

n∑i=0

ki = kn+1 −1

k −1

33.5 Lösungen zu Kapitel 10(Dateien lesen und schreiben)

1. Aufgabe

Die Variable output ist ein leerer String, wenn wir uns in einer Zeile mit einem Vornamenbefinden. Befinden wir uns in einer Zeile mit einem Nachnamen, enthält die Variable out-put den zugehörigen Vornamen.

fobj_in = open("namen.txt")fobj_out = open("namen2.txt", "w")

output = ""for line in fobj_in:

if output:output += " " + line.rstrip() + "\n"fobj_out.write(output)output = ""

else:output = line.rstrip()

fobj_in.close()fobj_out.close()

Page 396: 3446435476_Pytho

33.5 Lösungen zu Kapitel 10 (Dateien lesen und schreiben) 389

2. Aufgabe

Die Variable „counter” dient dazu die aktuelle Zeilennummer aufzunehmen. Immer wenneine Zeilennummer durch drei teilbar ist, geben wir den Inhalt der vorhergehenden dreiZeilen aus. Die einzige Ausnahme stellt die Zeilennummer 0 dar, denn in diesem Fall kannnoch nichts ausgegeben werden.

fh = open("musicians.txt")counter = 0new_line = ""for line in fh:

line = line.rstrip()if counter % 3 == 0:

if counter:print(new_line)

new_line = lineelif counter % 3 == 1:

new_line += " " + lineelse:

new_line += " (" + line + ")"counter += 1

fh.close()

3. Aufgabe

fh_in = open("musicians.txt")fh_out = open("musicians2.txt", "w")counter = 0new_line = ""for line in fh_in:

line = line.rstrip()if counter % 3 == 0:

if counter:fh_out.write(new_line + "\n")

new_line = lineelif counter % 3 == 1:

new_line += " " + lineelse:

new_line += " (" + line + ")"counter += 1

fh_out.close()fh_in.close()

Page 397: 3446435476_Pytho

390 33 Lösungen zu den Aufgaben

33.6 Lösungen zu Kapitel 11(Listen und Tupel im Detail)

1. Aufgabe

Die folgende Funktion „rev” kehrt die Reihenfolge einer Liste um. Die Funktion ist „de-struktiv”, d.h. sie leert die Eingabeliste.

def rev(lst):revl = []while lst:

el = lst.pop()revl.append(el)

return revl

liste = ["a","b","c","d"]liste = rev(liste)print liste

2. Aufgabe

def flatten(x):"""flatten(sequence) -> list"""

result = []for el in x:

#if isinstance(el,(list, tuple)):if (type(el) == list) or (type(el) == tuple):

result.extend(flatten(el))else:

result.append(el)return result

liste = [["test", ["noch tiefer", "auch"]],1,(2,(2,3,4))]

print(flatten(liste))

Obige Funktion testen wir in der Python-Shell:

bernd@saturn:~/bodenseo/python/beispiele$ python3Python 3.2.3 (default, Oct 19 2012, 19:53:57)[GCC 4.7.2] on linux2Type "help", "copyright", "credits" or "license" for more information.>>> from flatten import flatten>>> liste = [["test", ["noch tiefer", "auch"]],1,(2,(2,3,4))]>>> flatten(liste)['test', 'noch tiefer', 'auch', 1, 2, 2, 3, 4]>>>

Page 398: 3446435476_Pytho

33.6 Lösungen zu Kapitel 11 (Listen und Tupel im Detail) 391

3. Aufgabe

>>> l = [3,31,33,4,41,2,1,10,22]>>> sorted(l, key = str)[1, 10, 2, 22, 3, 31, 33, 4, 41]

4. Aufgabe

def letter_frequency(line):relevant_letters = "abcdefghijklmnopqrstuvwxyz"frequency_dict = {}for char in line.lower():

if char in relevant_letters:if char in frequency_dict:

frequency_dict[char] += 1else:

frequency_dict[char] = 1f = list(frequency_dict.items())f.sort(key = lambda x: x[1],reverse=True)return f

txt = letter_frequency(open("ulysses.txt").read())print(txt)

Started man dieses Programm erhält man die Buchstabenhäufigkeiten des Romans Ulyssesvon James Joyce:

bernd@saturn:~/bodenseo/python/beispiele$ python3 letter_frequency.py[('e', 143221), ('t', 101654), ('a', 94103), ('o', 92704), ('i', 82494),

('n', 81133), ('s', 77656), ('h', 73053), ('r', 70986), ('l', 55522),('d', 49609), ('u', 33767), ('m', 31891), ('c', 30481), ('g', 28189), ('f', 27016), ('w', 26424), ('y', 24571), ('p', 22879), ('b',21413), ('k', 12196), ('v', 9865), ('j', 2404), ('x', 1464), ('q',1343), ('z', 1079)]

bernd@saturn:~/bodenseo/python/beispiele$

5. Aufgabe

Wir geben zuerst die elegantere Lösung mit der lambda-Notation. Weiter unten finden Sieeine Lösung ohne lambda:

umsaetze = [ ('John', 'Miller', 46, 18.67),('Randy', 'Steiner', 48, 27.99),('Tina', 'Baker', 53, 27.23),('Andrea', 'Baker', 40, 31.75),('Eve', 'Turner', 44, 18.99),('Henry', 'James', 50, 23.56)]

Page 399: 3446435476_Pytho

392 33 Lösungen zu den Aufgaben

print("Sortiert nach Verkaufserlös:")for i in sorted(umsaetze, key=lambda x: x[2] * x[3]):

print(i)

print("\nSortiert nach Nachname und Vorname:")for i in sorted(umsaetze, key=lambda x: x[1] + x[0]):

print(i)

Speichern wir das obige Programm unter „umsaetze.py”, erhalten wir folgende Ausgabenbeim Start:

bernd@venus:~/bodenseo/python/beispiele$ python3 umsaetze.pySortiert nach Verkaufserlös:('Eve', 'Turner', 44, 18.99)('John', 'Miller', 46, 18.67)('Henry', 'James', 50, 23.56)('Andrea', 'Baker', 40, 31.75)('Randy', 'Steiner', 48, 27.99)('Tina', 'Baker', 53, 27.23)

Sortiert nach Nachname und Vorname:('Andrea', 'Baker', 40, 31.75)('Tina', 'Baker', 53, 27.23)('Henry', 'James', 50, 23.56)('John', 'Miller', 46, 18.67)('Randy', 'Steiner', 48, 27.99)('Eve', 'Turner', 44, 18.99)bernd@venus:~/bodenseo/python/beispiele$

Das folgende Programm stellt eine Lösung ohne die Verwendung von lambda dar. Wir müs-sen deshalb explizit zwei Funktionen definieren. Sie finden auch diese Lösung in unseremBeispielverzeichnis unter dem Namen „umsaetze2.py”:

umsaetze = [ ('John', 'Miller', 46, 18.67),('Randy', 'Steiner', 48, 27.99),('Tina', 'Baker', 53, 27.23),('Andrea', 'Baker', 40, 31.75),('Eve', 'Turner', 44, 18.99),('Henry', 'James', 50, 23.56)]

def gesamtpreis(x):return x[2] * x[3]

def name(x):return x[1] + x[0]

print("Sortiert nach Verkaufserlös:")for i in sorted(umsaetze, key=gesamtpreis):

print(i)

print("\nSortiert nach Nachname und Vorname:")

Page 400: 3446435476_Pytho

33.7 Lösungen zu Kapitel 15 (Rekursive Funktionen) 393

for i in sorted(umsaetze, key=name):print(i)

33.7 Lösungen zu Kapitel 15(Rekursive Funktionen)

1. Aufgabe

Mathematisch können wir es wie folgt formulieren:

f(1) = 3,f(n+1) = f(n) + 3

Die Funktion in Python:

def mult3(n):if n == 1:

return 3else:

return mult3(n-1) + 3

for i in range(1,10):print(mult3(i))

2. Aufgabe

def sum_n(n):if n== 0:

return 0else:

return n + sum_n(n-1)

3. Aufgabe

def pascal(n):if n == 1:

return [1]else:

line = [1]previous_line = pascal(n-1)for i in range(len(previous_line)-1):

line.append(previous_line[i] + previous_line[i+1])

Page 401: 3446435476_Pytho

394 33 Lösungen zu den Aufgaben

line += [1]return line

print(pascal(6))

Alternativ können wir eine Lösung mit List Comprehension schreiben:

def pascal(n):if n == 1:

return [1]else:

p_line = pascal(n-1)line = [ p_line[i]+p_line[i+1] for i in range(len(p_line)-1)]line.insert(0,1)line.append(1)

return lineprint(pascal(6))

4. Aufgabe

def fib_pascal(n,fib_pos):if n == 1:

line = [1]fib_sum = 1 if fib_pos == 0 else 0

else:line = [1](previous_line, fib_sum) = fib_pascal(n-1, fib_pos+1)for i in range(len(previous_line)-1):

line.append(previous_line[i] + previous_line[i+1])line += [1]if fib_pos < len(line):

fib_sum += line[fib_pos]return (line, fib_sum)

def fib(n):return fib_pascal(n,0)[1]

# and now printing out the first ten Fibonacci numbers:for i in range(1,10):

print(fib(i))

5. Aufgabe

Iterative-Lösung für das Sieb des Eratosthenes für die ersten 100 Zahlen:

from math import sqrt

def sieve(n):

Page 402: 3446435476_Pytho

33.7 Lösungen zu Kapitel 15 (Rekursive Funktionen) 395

# returns all primes between 2 and nprimes = list(range(2,n+1))max = sqrt(n)num = 2while num < max:

i = numwhile i <= n:

i += numif i in primes:

primes.remove(i)for j in primes:

if j > num:num = jbreak

return primes

print(sieve(100))

In diesem Kapitel geht es jedoch um rekursive Funktionen und in der Aufgabenstellunghaben wir auch bei der Berechnung der Primzahlen Rekursion verlangt. Um die folgendeLösung besser verstehen zu können, empfehlen wir unser Kapitel über Listen-Abstraktion(List Comprehension) zu Rate zu ziehen:

from math import sqrtdef primes(n):

if n == 0:return []

elif n == 1:return [1]

else:p = primes(int(sqrt(n)))no_p = [j for i in p for j in range(i*2, n, i)]p = [x for x in range(2, n) if x not in no_p]return p

print(primes(100))

6. Aufgabe

memo = {0:0, 1:1}def fib(n):

if not n in memo:memo[n] = fib(n-1) + fib(n-2)

return memo[n]

def find_index(*x):""" finds the natural number i with fib(i) = n """if len(x) == 1:

# started by user

Page 403: 3446435476_Pytho

396 33 Lösungen zu den Aufgaben

# find index starting from 0return find_index(x[0],0)

else:n = fib(x[1])m = x[0]if n > m:

return -1elif n == m:

return x[1]else:

return find_index(m,x[1]+1)

7. Aufgabe

# code from the previous example with the functions fib() and find_index()

print(" index of a | a | b | sum of squares | index ")print("=====================================================")for i in range(15):

square = fib(i)**2 + fib(i+1)**2print( " %10d | %3d | %3d | %14d | %5d " % (i, fib(i), fib(i+1),

square, find_index(square)))

Die Ausgabe für das vorige Skript sieht wie folgt aus:

index of a | a | b | sum of squares | index=====================================================

0 | 0 | 1 | 1 | 11 | 1 | 1 | 2 | 32 | 1 | 2 | 5 | 53 | 2 | 3 | 13 | 74 | 3 | 5 | 34 | 95 | 5 | 8 | 89 | 116 | 8 | 13 | 233 | 137 | 13 | 21 | 610 | 158 | 21 | 34 | 1597 | 179 | 34 | 55 | 4181 | 1910 | 55 | 89 | 10946 | 2111 | 89 | 144 | 28657 | 2312 | 144 | 233 | 75025 | 2513 | 233 | 377 | 196418 | 2714 | 377 | 610 | 514229 | 29

Page 404: 3446435476_Pytho

33.8 Lösungen zu Kapitel 17 (Alles über Strings . . . ) 397

33.8 Lösungen zu Kapitel 17(Alles über Strings . . . )

1. Aufgabe

Die Lösung besteht darin, dass man zweistufig split anwendet:

fh = open("addresses.txt", encoding="iso-8859-1")

for address in fh:address = address.strip()(lastname, firstname, city, phone) = address.split(';')print(firstname + " " + lastname + ", " + city + ", " + phone)

Die Ausgabe des Programms „split_addresses_mixed.py”:

bernd@saturn:~/bodenseo/python/beispiele$ python3 split_addresses_mixed.py

Meyer Frank, Radolfzell, 07732/43452Rabe Peter, Konstanz, 07531/70021Huber Ottmar, Rosenheim, 08031/7877-0Rabe Anna, Radolfzell, 07732/2343Lindner Oskar, Konstanz, 07531/890List Anna, München, 089/3434544List Anna, München, 089/3434544Huber Franziska, Rosenheim, 08031/787878Rabe Sarah, Konstanz, 07531/343454bernd@saturn:~/bodenseo/python/beispiele$

2. Aufgabe

Die Lösung besteht darin, dass wir einmal ein normales splitt mit maxsize = 1 und ein rsplitmit maxsize = 1 aufrufen:

fh = open("addresses.txt", encoding="iso-8859-1")

l = []for address in fh:

address = address.strip()t = (address.split(';',1)[0], address.rsplit(";",1)[1])l.append(t)

print(l)

Alternativ kann man auch ein split ohne maxsize verwenden und dann auf den ersten undletzten Index zugreifen:

fh = open("addresses.txt", encoding="iso-8859-1")

Page 405: 3446435476_Pytho

398 33 Lösungen zu den Aufgaben

l = []for address in fh:

address = address.strip()t = address.split(';')l.append((t[0], t[-1]))

print(l)

3. Aufgabe

>>> s = """Hat der alte Hexenmeister\nSich doch einmal wegbegeben!\r\nUndnun sollen seine Geister\rAuch nach meinem Willen leben.\n"""

>>> s.splitlines()['Hat der alte Hexenmeister', 'Sich doch einmal wegbegeben!', 'Und nun

sollen seine Geister', 'Auch nach meinem Willen leben.']>>> s = "\n".join(s.splitlines()) + "\n">>> s'Hat der alte Hexenmeister\nSich doch einmal wegbegeben!\nUnd nun sollen

seine Geister\nAuch nach meinem Willen leben.\n'>>> print(s)Hat der alte HexenmeisterSich doch einmal wegbegeben!Und nun sollen seine GeisterAuch nach meinem Willen leben.

>>>

4. Aufgabe

def findnth(str, substr, n):num = 0start = -1while num < n:

start = str.find(substr, start+1)if start == -1:

return -1num += 1

return start

if __name__ == "__main__":s = "abc xyz abc xyz abc xyz"print(findnth(s, "xyz", 1))print(findnth(s, "xyz", 3))

Dieses Programm befindet sich in unserem Beispielverzeichnis unter „findnth.py”. Startetman das Programm erhält man folgende Ausgaben:

Page 406: 3446435476_Pytho

33.8 Lösungen zu Kapitel 17 (Alles über Strings . . . ) 399

bernd@saturn:~/bodenseo/python/beispiele$ python3 findnth.py420bernd@saturn:~/bodenseo/python/beispiele$

5. Aufgabe

Für die Funktion „replacenth” benutzen wir die Funktion „findnth”, die wir im vorigen Bei-spiel erstellt hatten, um die Position des n-ten Vorkommens zu ermitteln. Für die eigentli-che Ersetzung brauchen wir dann lediglich Slicing und die Konkatenation von Strings:

def findnth(str, substr, n):num = 0start = -1while num < n:

start = str.find(substr, start+1)if start == -1: return -1num += 1

return start

def replacenth(source, search, replacement, n):pos = findnth(source, search, n)if pos == -1: return sourcereturn source[:pos] + replacement + source[pos+len(search):]

Beispiel für die Anwendung der Funktion „replacenth”:

bernd@saturn:~/bodenseo/python/beispiele$ python3Python 3.2.3 (default, Oct 19 2012, 19:53:57)[GCC 4.7.2] on linux2Type "help", "copyright", "credits" or "license" for more information.>>> from replacenth import replacenth>>> replacenth("And yes he said yes!","yes","no",2)'And yes he said no!'>>> replacenth("And yes he said yes!","yes","no",1)'And no he said yes!'>>>

Page 407: 3446435476_Pytho

400 33 Lösungen zu den Aufgaben

33.9 Lösungen zu Kapitel 19(Objektorientierte Programmierung)

1. Aufgabe

Ein Objekt der Klasse „robot” wird von den Attributen self.position, self.orientation undself.name definiert:

self.position enthält die Position eines Roboters in einem zweidimensionalen Koordina-tensystem. Die Koordinate wird in Form einer Zweierliste gespeichert.

self.orientation Ein Roboter kann in eine der vier Richtungen „north”, „east”, „south” und„west” gerichtet sein. Das ist dann auch die Richtung, in die er sich bewegt, wenn mandie Methode move auf ihn anwendet.

self.name Alle Dinge sollten einen Namen haben, so auch unsere robot-Objekte. Ansons-ten ist dieses Attribut natürlich nicht von besonderer Wichtigkeit für unsere Klasse.

Sowohl mit der Methode __str__ als auch mit der Methode __repr__ wird ein Objekt derKlasse in eine Stringdarstellung gewandelt. String verändert aber gewissermaßen das in-terne „Aussehen” eines Objektes und wird beispielsweise aufgerufen von print, als print(x)für ein Objekt x der Klasse Robot ruft __str__ auf. Tippt man hingegen nur x in der interak-tiven Shell ein, wird __repr__ aufgerufen.

Ist nur die Methode __str__ aer nicht __repr__ definiert, dann erhält man beispielsweisedie folgenden Ausgaben:

>>> x<robot.Robot object at 0xb71bfcac>>>> print(x)marvin faces north at position [3, 4]>>>

Ist hingegen die Methode __str__ nicht definiert, aber __repr__ definiert, dann wird in bei-den Fällen __repr__ zur Ausgabe verwendet:

>>> x(Marvin, north, [3, 4])>>> print(x)(Marvin, north, [3, 4])>>>

"""Roboterklasse zur Positionsbeschreibung und Veränderung von Objektenin einem zweidimensionalen Koordinatensystem."""

class Robot(object):

def __init__(self, x=0, y=0, orientation = "north", name="marvin"):self.position = [x,y]self.orientation = orientation

Page 408: 3446435476_Pytho

33.9 Lösungen zu Kapitel 19 (Objektorientierte Programmierung) 401

self.name = name

def __str__(self):""" Stringdarstellung einer Instanz """s = self.name + " faces " + self.orientations += " at position " + str(self.position)return s

def __repr__(self):""" Allgemeine Darstellung einer Instanz """s = "(" + self.name + ", " + self.orientations += ", " + str(self.position) + ")"return s

def move(self, distance):""" Methode zum Bewegen eines Roboters in Richtung seineraktuellen Orientierung.

Wird ein Roboter x mit x.move(10) aufgerufen und ist dieserRoboter östlich orientiert, also x.getPosition() == ,,east''und ist beispielsweise [3,7] die aktuelle Position desRoboters, dann bewegt er sich 10 Felder östlich und befindetsich anschließend in Position [3,17].

"""

if self.orientation == "north":self.position[1] += distance

elif self.orientation == "south":self.position[1] -= distance

elif self.orientation == "west":self.position[0] -= distance

elif self.orientation == "east":self.position[0] -= distance

def newOrientation(self, o):""" Mit der Methode newOrientation ändern wir die Orientierungdes Roboters.

o has to be in {"north","south","west","east"}

Falls eine unbekannte Orientierung übergeben wird, wird derRoboter nicht bewegt."""

if o in {"north","south","west","east"}:self.orientation = o

def getOrientation(self):""" Ein Aufruf von x.getOrientation() für einen Roboter x liefertdessen aktuelle Orientierung zurück, also eine der Richtungen

Page 409: 3446435476_Pytho

402 33 Lösungen zu den Aufgaben

"west", "south", "east" oder "north"."""

return self.orientation

def getPosition(self):"""Liefert eine 2er-Liste mit [x,y] zurück."""

return self.position

def setPosition(self,pos):"""Damit kann man den Roboter auf eine neue Position imKoordinatensystem positionieren.

pos muss eine Liste oder ein Tupel mit zwei Elementen sein.Ansonsten wird nichts getan."""

if isinstance(pos, (tuple, list)) and len(pos) == 2:self.position = pos

def rename(self,name):""" Damit kann man dem Roboter einen neuen Namen geben. """self.name = name

def getName(self):""" Liefert den Namen des Roboters zurück. """return self.name

if __name__ == "__main__":from robot import Robotx = Robot(3,4,"north", "Marvin")print(x)x.move(10)x.newOrientation("west")x.move(7)print(x)new_name = "Andrew"print(x.getName() + " will be renamed as " + new_name)x.rename(new_name)print("Hi, this is " + x.getName())x.setPosition([0,0])print(x)

Ruft man obiges Modul direkt auf, erhält man folgende Ausgaben:

$ python3 robot.pyMarvin faces north at position [3, 4]Marvin faces west at position [-4, 14]Marvin will be renamed as AndrewHi, this is AndrewAndrew faces west at position [0, 0]

Page 410: 3446435476_Pytho

33.9 Lösungen zu Kapitel 19 (Objektorientierte Programmierung) 403

2. Aufgabe

"""This module is implementing a Cave Man arithmetic "CaveInt",the simplest numeral system representing the natural numbers.

A number n is represented by a string consisting of n tallymarks (vertical strokes). The number 0 - completely unknownto Neanderthal Man is - is represented by an empty string"""

class CaveInt(object):

def __init__(self,n):""" A CaveInt can be either initialized by an integer or

by a string consisting solely of tally marks.The number 0 and negative numbers are turned into anempty string. A positive integer n will be turned intoa string with n tally marks.

"""self.number = ""if type(n) == int:

self.number = "|" * nelif type(n) == str:

if len(n) == n.count("|"):self.number = n

def __str__(self):""" String representation of a CaveInt """return self.number

def __int__(self):""" A CaveInt is converted into an integer """return len(self.number)

def __add__(self, other):""" Addition of two CaveInt numbers """res = CaveInt(self.number + other.number)return res

def __sub__(self, other):""" Subtraction of two CaveInt numbers """diff = int(self) - int(other)diff = diff if diff > 0 else 0res = CaveInt(diff)return res

def __mul__(self,other):""" Multiplication of two CaveInt numbers """res = CaveInt(self.number * len(other.number))return res

Page 411: 3446435476_Pytho

404 33 Lösungen zu den Aufgaben

def __truediv__(self,other):""" Division is of course integer division """d = int(len(self.number) / len(other.number))res = CaveInt(d)return res

if __name__ == "__main__":x = CaveInt("||||")y = CaveInt(12)print(x,y)

z = x + yprint(z)

z = x - yprint(z)

z = y - xprint(z)

z = CaveInt("")print(z)

z = CaveInt(0)print(z)

z = y / xprint(z)

z = x * yprint(z, int(z))

3. Aufgabe

class Plist(list):

def __init__(self, l):list.__init__(self, l)

def push(self, item):self.append(item)

def splice(self, offset, length, replacement):self[offset:offset+length] = replacement

if __name__ == "__main__":x = Plist([33,456,8,34,99])

Page 412: 3446435476_Pytho

33.10 Lösungen zu Kapitel 20 (Tests und Fehler) 405

x.push(47)print(x)x.splice(2,3,["Hey", "you"])print(x)

Startet man das Programm, erhält man folgende Ausgabe:

$ python3 standardklassen.py[33, 456, 8, 34, 99, 47][33, 456, 'Hey', 'you', 47]

33.10 Lösungen zu Kapitel 20(Tests und Fehler)

1. Aufgabe

Die Formulierungen für den Doctest sind in Ordnung. Das Problem liegt in der Implemen-tierung der Fibonacci-Funktion. Diese rekursive Lösung ist höchst ineffizient. Sie müssennur Geduld haben, bis der Test terminiert. Wie viele Stunden, Tage oder Wochen Sie wartenmüssen hängt von Ihrem Rechner ab.,

33.11 Lösungen zu Kapitel 24(Reguläre Ausdrücke)

1. Aufgabe

(1) abc (a) ab+c?(2) ac (b) a?b*c(3) abbb (c) b+c*(4) bbc (d) ^b+c*$(5) aabcd (e) a.+b?c(6) b (f) b{2,}c?

(g) ^a{1,2}b+.?d*

(a) matcht 1, 3, 5

(b) matcht 1, 2, 4, 5

(c) matcht 1, 3, 4, 5

(d) matcht 4, 6

(e) matcht 1, 5

(f) matcht 4, 6

(g) matcht 1, 3, 5

Page 413: 3446435476_Pytho

406 33 Lösungen zu den Aufgaben

2. Aufgabe

Die Frage lautete, auf welchen der Strings

(1) xx(2) xyxyxyx(3) xxyxy(4) xyxx(5) xxyxyx

der Ausdruck x(xy)*x matcht.

Vielleicht hatten Sie irrtümlich gedacht, dass das Fragezeichen zum regulären Ausdruckgehört. Es gehört natürlich zur Frage.

Antwort: Nur 2 matcht nicht, denn in allen anderen kommt der Teilausdruck „xx” vor!

Würde das Fragezeichen zum Ausdruck gehören, würde der reguläre Ausdruck auf alleStrings passen.

3. Aufgabe

Ein regulärer Ausdruck, der das Problem löst lautet, wenn man annimmt, das der Zip-Codeam Ende einer Zeile stehen muss, so wie es bei US-Briefen der Fall ist:

r"\b[0-9]{5}(-?[0-9]{4})?$"

Ein kleines Testprogramm finden Sie im Folgenden:

import re

destinations = ["Minneapolis, MN 55416","Defreestville NY 12144-7050","Anchorage AK 99577-0727","Juneau AK 99750-0077","Mountain View, CA 94043","Atlanta, GA 30309","Washington TX 778800305"]

erroneous = ["Minneapolis, MN 554169","Defreestville NY 12144-705","Anchorage AK 9957-0727","Juneau AK 99750--0077","Mountain View, CA 94 043","Atlanta, GA 3030-12345","Washington TX 778-800305"]

for dest in destinations + erroneous:if re.search(r"[^-\d][0-9]{5}(-?[0-9]{4})?$",dest):

print(dest + " okay")else:

print(dest + " not okay")

Page 414: 3446435476_Pytho

33.11 Lösungen zu Kapitel 24 (Reguläre Ausdrücke) 407

Startet man obiges Programm, erhält man folgende Ausgabe:

$ python3 us_zip_codes.pyMinneapolis, MN 55416 okayDefreestville NY 12144-7050 okayAnchorage AK 99577-0727 okayJuneau AK 99750-0077 okayMountain View, CA 94043 okayAtlanta, GA 30309 okayWashington TX 778800305 okayMinneapolis, MN 554169 not okayDefreestville NY 12144-705 not okayAnchorage AK 9957-0727 not okayJuneau AK 99750--0077 not okayMountain View, CA 94 043 not okayAtlanta, GA 3030-12345 not okayWashington TX 778-800305 not okay

4. Aufgabe

>>> import re>>> x = open("ulysses.txt").read()>>> words = set(re.findall(r"\b(\w+ship)\b", x))>>> words{'pumpship', 'courtship', 'battleship', 'lordship', 'marksmanship', '

pleasureship', 'chelaship', 'fellowship', 'lightship', 'fathership','Lordship', 'debtorship', 'consulship', 'township', 'friendship', 'worship', 'steamship'}

>>> len(words)17>>>

5. Aufgabe

>>> import re>>> x = open("ulysses.txt").read()>>> words = set(re.findall(r"\b((\w{2,})\2)\b", x))>>> words{('eeee', 'ee'), ('cancan', 'can'), ('dumdum', 'dum'), ('

schschschschschsch', 'schschsch'), ('lala', 'la'), ('ooddleooddle', 'ooddle'), ('chinchin', 'chin'), ('chokeechokee', 'chokee'), ('murmur', 'mur'), ('bangbang', 'bang'), ('nono', 'no'), ('hellohello', 'hello'), ('nyumnyum', 'nyum'), ('rara', 'ra'), ('tartar', 'tar'), ('hiphip', 'hip'), ('geegee', 'gee'), ('jamjam', 'jam'), ('poohpooh', 'pooh'), ('addleaddle', 'addle'), ('usus', 'us'), ('papa', 'pa'), ('looloo', 'loo'), ('curchycurchy', 'curchy'), ('pullpull', 'pull'), ('yumyum', 'yum'), ('whatwhat', 'what'), ('teetee', 'tee'), ('quisquis', 'quis'), ('coocoo', 'coo'), ('rere', 're'), ('puffpuff', 'puff')}

Page 415: 3446435476_Pytho

408 33 Lösungen zu den Aufgaben

>>> words = { w[0] for w in words}>>> words{'whatwhat', 'jamjam', 'yumyum', 'dumdum', 'nyumnyum', 'pullpull', 'rere

', 'usus', 'quisquis', 'addleaddle', 'curchycurchy', 'hellohello', 'looloo', 'nono', 'tartar', 'schschschschschsch', 'papa', 'ooddleooddle', 'cancan', 'murmur', 'teetee', 'rara', 'eeee', 'geegee', 'hiphip', 'bangbang', 'lala', 'chokeechokee', 'chinchin', 'coocoo', 'poohpooh', 'puffpuff'}

6. Aufgabe

import re

x = open("ulysses.txt").read().lower()words = list(re.findall(r"\b([a-z]+)\b",x))

word_frequency = {}

for word in words:l = len(word)if l in word_frequency:

word_frequency[l] += 1else:

word_frequency[l] = 1

print("Wortlänge Häufigkeit")word_frequency_list = list(word_frequency.items())for (wl, freq) in sorted(word_frequency_list):

print("{0:8d} {1:8d}".format(wl,freq))

Startet man obiges Programm, erhält man unten stehende Ausgabe:

$ python3 word_lengths.py Wortlänge Häufigkeit1 149302 430643 591224 470675 325136 237067 195618 119899 790110 483411 270612 142613 71414 29515 11316 5317 30

Page 416: 3446435476_Pytho

33.11 Lösungen zu Kapitel 24 (Reguläre Ausdrücke) 409

18 1619 620 721 622 123 224 125 326 227 128 130 133 134 136 237 139 169 1

Möchte man nur die Häufigkeitsverteilungen für verschiedene Wörter nach Wortlängenberechnen, braucht man nur die Zeile

words = list(re.findall(r"\b([a-z]+)\b",x))

durch folgende Zeile zu ersetzen:

words = set(re.findall(r"\b([a-z]+)\b",x))

Man benutzt also eine Menge statt einer Liste. Dadurch werden automatisch mehrfacheVorkommen eines Wortes eliminiert. Das Ergebnis sieht nun deutlich anders aus:

$ python3 word_lengths.pyWortlänge Häufigkeit

1 262 1413 7324 20385 32796 43317 45668 42739 339910 244111 161912 93813 49014 21415 10016 4217 2318 1319 620 6

Page 417: 3446435476_Pytho

410 33 Lösungen zu den Aufgaben

21 422 123 224 125 326 227 128 130 133 134 136 237 139 169 1

33.12 Lösungen zu Kapitel 25(lambda, map, filter und reduce)

1. Aufgabe

orders = [ ["34587","Learning Python, Mark Lutz", 4, 40.95],["98762","Programming Python, Mark Lutz", 5, 56.80],["77226","Head First Python, Paul Barry", 3, 32.95]]

min_order = 100invoice_totals = list(map(lambda x: x if x[1] >= min_order else (x[0], x

[1] + 10) ,map(lambda x: (x[0],x[2] * x[3]), orders)))

print(invoice_totals)

2. Aufgabe

from functools import reduce

orders = [ ["34587",("5464", 4, 9.99), ("8274",18,12.99), ("9744", 9,44.95)],

["34588",("5464", 9, 9.99), ("9744", 9, 44.95)],["34588",("5464", 9, 9.99)],["34587",("8732", 7, 11.99), ("7733",11,18.99), ("9710", 5,

39.95)] ]

min_order = 100

Page 418: 3446435476_Pytho

33.13 Lösungen zu Kapitel 26 (Listen-Abstraktion/List Comprehension) 411

invoice_totals = list(map(lambda x: [x[0]] + list(map(lambda y: y[1]*y[2], x[1:])), orders))

invoice_totals = list(map(lambda x: [x[0]] + [reduce(lambda a,b: a + b, x[1:])], invoice_totals))

invoice_totals = list(map(lambda x: x if x[1] >= min_order else (x[0], x[1] + 10), invoice_totals))

print (invoice_totals)

33.13 Lösungen zu Kapitel 26(Listen-Abstraktion/ListComprehension)

1. Aufgabe

>>> n = 30>>> [(x, y) for x in range(1,n+1) for y in range(1,n+1) if not x % y and

x != y ][(2, 1), (3, 1), (4, 1), (4, 2), (5, 1), (6, 1), (6, 2), (6, 3), (7, 1),

(8, 1), (8, 2), (8, 4), (9, 1), (9, 3), (10, 1), (10, 2), (10, 5),(11, 1), (12, 1), (12, 2), (12, 3), (12, 4), (12, 6), (13, 1), (14,1), (14, 2), (14, 7), (15, 1), (15, 3), (15, 5), (16, 1), (16, 2),(16, 4), (16, 8), (17, 1), (18, 1), (18, 2), (18, 3), (18, 6), (18,9), (19, 1), (20, 1), (20, 2), (20, 4), (20, 5), (20, 10), (21, 1),(21, 3), (21, 7), (22, 1), (22, 2), (22, 11), (23, 1), (24, 1), (24,2), (24, 3), (24, 4), (24, 6), (24, 8), (24, 12), (25, 1), (25, 5),(26, 1), (26, 2), (26, 13), (27, 1), (27, 3), (27, 9), (28, 1), (28,2), (28, 4), (28, 7), (28, 14), (29, 1), (30, 1), (30, 2), (30, 3),(30, 5), (30, 6), (30, 10), (30, 15)]

2. Aufgabe

Die Wahrscheinlichkeit dafür, dass die Summe der Augenzahlen 7 ist berechnet sich ausdem Quotienten der Anzahl aller Würfe mit Summe 7 („casts_of_sum_7”) dividiert durchdie Anzahl aller möglichen Würfe („all_casts”):

>>> all_casts = [ (x,y) for x in range(1,7) for y in range(1,7)]>>> casts_of_sum_7 = [ x for x in all_casts if sum(x) == 7 ]>>> casts_of_sum_7[(1, 6), (2, 5), (3, 4), (4, 3), (5, 2), (6, 1)]>>> probability7 = len(casts_of_sum_7) / len(all_casts)>>> probability70.16666666666666666

Page 419: 3446435476_Pytho

412 33 Lösungen zu den Aufgaben

3. Aufgabe

def prob_for_sum(number):all_casts = [ (x,y) for x in range(1,7) for y in range(1,7)]casts_of_sum = [ x for x in all_casts if sum(x) == number ]return len(casts_of_sum) / len(all_casts)

if __name__ == "__main__":for i in range(14):

print("sum: {0:2d}, prob: {1:5.2f} %".format(i,prob_for_sum(i)

*100 ) )

Startet man obiges Programm, erhält man folgende Ausgabe:

$ python3 sum_of_dices_prob.pysum: 0, prob: 0.00 %sum: 1, prob: 0.00 %sum: 2, prob: 2.78 %sum: 3, prob: 5.56 %sum: 4, prob: 8.33 %sum: 5, prob: 11.11 %sum: 6, prob: 13.89 %sum: 7, prob: 16.67 %sum: 8, prob: 13.89 %sum: 9, prob: 11.11 %sum: 10, prob: 8.33 %sum: 11, prob: 5.56 %sum: 12, prob: 2.78 %sum: 13, prob: 0.00 %

33.14 Lösungen zu Kapitel 27(Generatoren und Iteratoren)

1. Aufgabe

def fibonacci():"""Ein Fibonacci-Zahlen-Generator"""

a, b = 0, 1while True:

yield aa, b = b, a + b

f = fibonacci()counter = 0

Page 420: 3446435476_Pytho

33.14 Lösungen zu Kapitel 27 (Generatoren und Iteratoren) 413

for x in f:print x,counter += 1if (counter > 10):

break

2. Aufgabe

def city_generator():cities = ["Paris","Berlin","London","Vienna"]for city in cities:

yield city

def round_robin(g):while True:

for i in g():yield i

if __name__ == "__main__":for i in round_robin(city_generator):

print(i)

Dies Ausgabe dieses Programms sieht wie folgt aus:

ParisBerlinLondonViennaParisBerlinLondonViennaParisBerlinLondonViennaParis...

3. Aufgabe

def city_generator():cities = ["Paris","Berlin","London","Vienna"]for city in cities:

yield city

Page 421: 3446435476_Pytho

414 33 Lösungen zu den Aufgaben

def pendulum(g):while True:

l = []for i in g():

l.append(i)yield i

while l:yield l.pop()

if __name__ == "__main__":for i in pendulum(city_generator):

print(i)

Starten wir dieses Programm, erhalten wir die folgende Ausgabe:

ParisBerlinLondonViennaViennaLondonBerlinParisParisBerlinLondonViennaViennaLondonBerlinParis...

4. Aufgabe

def numbers():for i in range(1,10):

yield i

def pair_sum(g):it = g()previous = next(it)while True:

try:x = next(it)yield x + previousprevious = x

except StopIteration:it = g()

Page 422: 3446435476_Pytho

33.15 Lösungen zu Kapitel 29 (NumPy) 415

if __name__ == "__main__":for i in pair_sum(numbers):

print(i, end=", ")

Die Ausgabe sieht wie folgt aus:

3, 5, 7, 9, 11, 13, 15, 17, 10, 3, 5, 7, 9, 11, 13, 15, 17, 10, 3, 5, 7,9, 11, ...

33.15 Lösungen zu Kapitel 29 (NumPy)

1. Aufgabe

Wenn zwei Vektoren senkrecht zueinander stehen, muss das Skalarprodukt 0 ergeben, weilder cos π

2 gleich Null ist.

Wir prüfen dies für die beiden Vektorpaare:

>>> x = np.array([2,7,-3])>>> y = np.array([-2,1,1])>>> np.dot(x,y)0>>> x = np.array([2,3,-1])>>> y = np.array([-5,4,2])>>> np.dot(x,y)0>>>

2. Aufgabe

>>> import numpy as np>>> x = np.array([5,9,-5])>>> y = np.array([-3,0,6])>>> abs_x = np.sqrt(np.dot(x,x))>>> abs_y = np.sqrt(np.dot(y,y))>>> cos_xy = np.dot(x,y) / (abs_x * abs_y)>>> angle = np.arccos(cos_xy)>>> angle2.1970314908665638>>> angle * 360 / np.pi / 2125.88063188398918>>>

Page 423: 3446435476_Pytho

416 33 Lösungen zu den Aufgaben

3. Aufgabe

>>> A = np.array([[-2,-2,2,1],[-3,4,0,-2],[-1,2,3,2],[3,2,3,2]])>>> b = np.array([-5, -1, 0, 4])>>> x = np.linalg.solve(A,b)>>> xarray([ 1., 1., -1., 1.])>>>

4. Aufgabe

Es sollten die Nullstellen für das Polynom

f (x) = 4 · x3 +12 · x2 −7 · x −5

berechnet werden:

>>> import numpy as np>>> f = np.poly1d([4,12,-7,-5])>>> np.roots(f)array([-3.40604443, 0.84193743, -0.43589299])>>>

5. Aufgabe

>>> import numpy as np>>> f = np.poly1d([2,-3,-2,-1])>>> f.integ()poly1d([ 0.5, -1. , -1. , -1. , 0. ])>>> f.deriv()poly1d([ 6, -6, -2], dtype=int32)>>>

Das Integral F von f lautet also:

F (x) = 1

2·x4 −1 · x3 −1 · x2 −1 · x

Die Ableitung von f lautet:

f ′(x) = 2 · x3 −3 · x2 −2 · x −1

Page 424: 3446435476_Pytho

Stichwortverzeichnis

∧ bei regulären Ausdrücken 269. Match eines beliebigen Zeichens 266$ bei regulären Ausdrücken 269

Aabort 226abspath 240access 227__add__ 403Allgemeine Klasse → BasisklasseAnagramm– als Beispiel einer Permutation 309Andrew 402Angestelltenklasse → Personklasseappend 75Archivdatei erzeugen 251Arrays 328– flach machen → NumPy– konkatenieren → NumPy– ones() 332– umdimensionieren → NumPy– zeros() 332assertAlmostEqual 207assertCountEqual 207AssertEqual 206assertEqual 207assertFalse 208assertGreater 208assertGreaterEqual 208assertIn 208AssertionError 205assertIs 208assertIsInstance 209assertIsNone 209assertIsNot 209assertItemsEqual 209assertLess 208assertLessEqual 208assertListEqual 209

assertNotRegexpMatches 209assertTrue 208assertTupleEqual 209assoziative Arrays 33assoziatives Feld 33atime 243Attribute 162– private 162– protected 162– public 162AttributeError 172Aufspalten von Zeichenketten 132Auskellern 75Ausnahme– StopIteration 306Ausnahmebehandlung 149– except 149– finally 153– try 149Automatentheorie 261

BBarry 195basename 241Bash 212Basisklasse 169Bayes-Theorem 367bedingte Anweisungen 55Bibliothek 90Bierdeckelarithmetik 191Bierdeckelnotation 191binäre Operatoren 187Binärsystem 191Boehm 195Bourne-Shell 212Bruch– kürzen 349– Kürzungszahl 349

Page 425: 3446435476_Pytho

418 Stichwortverzeichnis

– Repräsentierung in Python|hyperpage 348– vollständig gekürzte Form 349Bruchklasse 347Bruchrechnen 348Bulls and Cows 358bztar-Datei → Archivdatei erzeugen

CCalendar-Klasse 177CalendarClock-Klasse 177Caret-Zeichen 267Cast 24Casting 24CaveInt 403 → Neanderthal-Arithmetikcenter 144chdir 227chmod 227Chomsky-Grammatik 117chown 228chroot 228clear → DictionaryCLI 212Clock-Klasse 177close 218Closure 318Codeblock 59combinatorics.py 359commonprefix 241copy 249 → Dictionarycopy2 249copyfile 249copyfileobj 249copymode 249copystat 250copytree 250count 78, 139ctime 243

DDatei 69– lesen 69– öffnen 69– schreiben 71– zum Schreiben öffnen 71Dateibaum– rekursiv durchwandern 240Dateibearbeitung mit os 215Dateideskriptor 218, 219Dateigröße 244Datenkapselung 157Datentypen 21Deadly Diamond of Death 176

Decorator → DekorateurDefinition einer Variablen 21Dekorateur 318– Beispiel Fakultätsfunktion 320– Benutzung in Python 319– zur Implementierung eine Memoisation 320– zur Überprüfung von Funktionsargumenten

320deriv 343derivation 343Destruktor 160Dezimalsystem 191Diamond-Problem 176Dictionary 33– aus Listen erzeugen 43– clear 38– copy 38– flache Kopie 38, 101– fromkeys 38– get 39– items 39– keys 39– nested Dictionaries 37– pop 40– popitem 40– setdefault 40– tiefe Kopie 38, 101– update 41– verschachtelte Dictionaries 37dirname 241doctest 200Dokumentenklassifikation 365Donald Michie → MemoisationDrei-Finger-Regel 340dup 220dynamische Attribute 168dynamische Typdeklaration 23

EEingabeprompt 9Einheitsmatrix 339Einkellern 75Einschubmethode → Hook-Methodeendliche Automaten 261, 267erben → Vererbungerrare humanum est 195erweiterte Zuweisungen 188Euklidischer Algorithmus 349except 149execl 225execle 226execlp 225

Page 426: 3446435476_Pytho

Stichwortverzeichnis 419

execlpe 226execv 224execve 224execvp 222execvpe 223exists 242expandvars 242explizite Typumwandlung 24extend 76extsep 228

FFakultätsfunktion 117– iterative Lösung 119– rekursive Implementierung 118– unter Benutzung eines Dekorateurs 320Fehler– Semantik 196– Syntax 195Fehlerarten 195fib → Fibonacci-Modulfiblist → Fibonacci-ModulFibonacci 120Fibonacci-Folge → Fibonacci-Zahlen– rekursive Berechnung 126Fibonacci-Modul 197– fib-Funktion 197– fiblist-Funktion 197Fibonacci-Zahlen– formale mathematische Definition 120– Generator 314, 412– Modul zur Berechnung der Fibonacci-Zahlen

197– rekursive Berechnung mit Dekorateuren 318– rekursive Funktion 119filter 289finally 153find 78, 139Finite State Machine 268firstn-Generator 312flache Kopie 38, 101for-Schleife 62– optionaler else-Teil 62– StopIteration 306– Unterschied zur while-Schleife 63– Vergleich zu C 62fork 228, 255Forking 255forkpty 228formale Sprachen 261fromkeys → Dictionary

Frosch– Aufgabe mit Summenbildung 66– Straßenüberquerung 66frozensets 49fully qualified 90functools 294Funktion 103Funktionsabschluss 318

Gganze Zahlen 22Ganzzahlen 22Generator– all_colours() 359– Allgemein 305– CLU 305– Endlosschleife in 307– firstn 312– Icon 305– isclice zur Ausgabe von unendlichen

Generatoren 312– k-Permuationen aus Zufallspermutation 359– k-Permutationen 359– k_permutations() 359– pair_sum 315– pendulum 314– Permutationen 309, 359– permutations() 359– round_robin 314– send-Methode 312– Werte empfangen 312– yield 307– zur Erzeugung von Selektionen 310Generator-Ausdrücke 313Generator-Comprehension 302Generatoren-Abstraktion 302get → Dictionaryget_archive_formats 251getatime 243getcwd 229, 247getcwdb 229getegid 229getenv 213getenvb 213geteuid 229get_exec_path 228getgid 229getgroups 229getloadavg 229getlogin 230getmtime 243getpgid 230

Page 427: 3446435476_Pytho

420 Stichwortverzeichnis

getpgrp 230getpid 230getppid 230getresgid 230getresuid 230getsize 244 → os.pathgetter 166getuid 230get_unpack_formats 251ggT 349Girokonto 170Gleichungssysteme 340grafische Veranschaulichung von Matrixwerten→ Hinton-Diagramm

größter gemeinsamer Teiler 349Gruppierungen 274GUI 212gztar-Datei → Archivdatei erzeugen

HHash 33Hinton-Diagramm 323Hook-Methode 206Hooks → Hook-Methode

Iif-Anweisung 55ignore_patterns 251implizite Typumwandlung 25import-Anweisung 90in– Dictionaries 36index 78, 139index-Funktionen 220Inheritance → Vererbung__init__.py 95inneres Produkt 335insert 79Instanz 155Instanzattribute 168integ 343Integer 22interaktive Shell– Befehlsstack 13– Starten unter Linux 9– Starten unter Windows 10– Unterstrich 12inverse Matrix 339isabs 244isalnum 145isalpha 145isdigit 145

isdir 245isfile 245isinstance 25islice 312– als Ersatz für firstn 312islink 245islower 145ismount 246isspace 145istitle 145isupper 145itemgetter 84items → Dictionary__iter__ 306Iteration– for-Schleife 305Iteratoren 305– for-Schleife 305itertools 312– islice 312

Jjoin 139, 246Junit 204

Kkanonischer Pfad 247Kapselung 157Keller 75Kellerspeicher 75, 188KeyError 35keys → Dictionarykill 230killpg 231Klasse 156– Erzeugen von Objekten 158– Kopf 158– Körper 158– minimale Klasse in Python 158Klassenattribute 168– Zähler zum zählen der Objekte 168Kombination 310Kombinatorik-Modul 359Kompilierung von regulären Ausdrücken 280Komponententest 197Konstruktor 160Kontoklasse 158, 170– mit Unterklassen 170Kontrollstrukturen 55Kopieren– von Unterlisten 100– von verschachtelten Listen 97

Page 428: 3446435476_Pytho

Stichwortverzeichnis 421

Kreuzprodukt 335, 340Kuchenklasse 155Kundenklasse → PersonklasseKürzen 349

Llambda 289Länge eines Vektors 335Leonardo von Pisa → Fibonacci-Zahlenlexists 246Liber Abaci 120Lieferant → Personklasselinalg → NumPylineare Algebra 340lineare Gleichungssysteme 340linesep 231link 231Linux 212– Python unter 9list– append 75– extend 76– find 78– index 78– pop 75– sort 82List Comprehension 297– Kreuzprodukt 299– pythagoreisches Tripel 299– Sieb des Eratosthenes 300– Syntax 298– Vergleich mit konventionellem Code 298– Wandlung Celsius in Fahrenheit 299– Weitere Beispiele 299– zugrundeliegende Idee 299list:count 78list:insert 79list:remove 77listdir 231list_iterator 306Listen-Abstraktion 297– Kreuzprodukt 299– pythagoreisches Tripel 299– Sieb des Eratosthenes 300– Syntax 298– Vergleich mit konventionellem Code 298– Wandlung Celsius in Fahrenheit 299– Weitere Beispiele 299– zugrundeliegende Idee 299ljust 144Lösung von Gleichungssystemen 340lower 143

lseek 219lstat 231

M__main__ 197major 231make_archive 251makedev 232makedirs 232map 289, 292Mapping 33Marvin 402Mastermind 357Match eines beliebigen Zeichens 266Match-Objekte 274Matching-Problem 264– Over Matching 264– Under Matching 264MATLAB 323Matrix-Klasse → NumPyMatrix-Produkt → NumPyMatrizenaddition 333Matrizenmultiplikation 333, 337Matrizensubtraktion 333maxsplit– split 132Mehrfachvererbung 174– Beispiel CalendarClock 177– Tiefensuche 174Memoisation 123, 317Memoization → MemoisationMemoize 319memoize-Funktion 318Menge 47Mengen-Abstraktion 301Mengenlehre 47Methode 155, 159– Overloading → Überladen– Overwriting → Überschreiben– Überladen 171– Überschreiben 171– Unterschiede zur Funktion 159minor 231mkdir 232mkfifo 233Modul– dir() 93– Dokumentation 94– dynamisch geladene C-Module 91– eigene Module schreiben 93– Inhalt 93– lokal 90

Page 429: 3446435476_Pytho

422 Stichwortverzeichnis

– math 90– Modularten 91– Namensraum 90– re 263– Suchpfad 92modulare Programmierung 89modulares Design 89Modularisierung 89Modultest 197Modultests– unter Benutzung von __name__ 197Monty Python 34move 252mtime 243__mul__ 403

NNaiver Bayes-Klassifikator 366, 367__name__ 197Namensraum– umbenennen 91Neanderthal-Arithmetik 191Nested Dictionaries 37next 306nice 233Noam Chomsky 117normcase 246normpath 247Nullstellen berechnen 343Numarray → NumPyNumeric → NumPyNumeric Python → NumPyNumPy 323– Array mit Nullen und Einsen initialisieren

332– Array neue Dimension hinzufügen 332– Arrays 325– Arrays flach machen 327– Arrays konkatenieren 330– Arrays umdimensionieren 328– deriv 343– Drei-Finger-Regel 340– Einheitsmatrix 339– eye 339– Gleichungssysteme 340– inneres Produkt 335– integ 343– inv 339– inverse Matrix 339– Kreuzprodukt 335, 340– Länge eines Vektors 335– linalg 340

– lineare Algebra 340– lineare Gleichungssysteme 340– Lösung von Gleichungssystemen 340– Matrix-Klasse 336– Matrix-Produkt 337– Matrixarithmetik 333– Matrizenaddition 333– Matrizenmultiplikation 333, 337– Matrizensubtraktion 333– Nullstellen 341– Nullstellen berechnen 343– ones() 332– ones_like() 333– polynomial-Paket 341– Punktprodukt 335– Rechtssystem 340– roots 343– Skalarprodukt 335– Vektoraddition 334– Vektorprodukt 340– Vektorsubtraktion 334– Winkel zwischen Vektoren 335– zeros() 332– zeros_like() 333

OOberklasse 169object 169Objekt 155Objektorientierte Programmiersprache 155Objektorientierte Programmierung 155ones_like() 333OOP 155– Datenkapselung 157– Kapselung 157– Kuchenklasse 155open 216open() 69openpty 219operator-Modul 84– itemgetter 84Operator-Überladung 187Operatoren überladen– binäre Operatoren 187– erweiterte Zuweisungen 188– unäre Operatoren 188os– abort 226– access 227– chdir 227– chmod 227– chown 228

Page 430: 3446435476_Pytho

Stichwortverzeichnis 423

– chroot 228– close 218– dup 220– execl 225– execle 226– execlp 225– execlpe 226– execv 224– execve 224– execvp 222– execvpe 223– extsep 228– fork 228– forkpty 228– getcwd 229– getcwdb 229– getegid 229– getenv 213– getenvb 213– geteuid 229– get_exec_path 228– getgid 229– getgroups 229– getloadavg 229– getlogin 230– getpgid 230– getpgrp 230– getpid 230– getppid 230– getresgid 230– getresuid 230– getuid 230– kill 230– killpg 231– linesep 231– link 231– listdir 231– lseek 219– lstat 231– major 231– makedev 232– makedirs 232– minor 231– mkdir 232– mkfifo 233– nice 233– open 216– openpty 219– popen 233– putenv 214– read 218– readlink 235

– remove 235– removedirs 235– rename 236– renames 236– rmdir 237– sep 241– setegid 237– seteuid 237– setgid 237– setgroups 229– setpgid 237– setpgrp 237– setregid 237– setresgid 237– setresuid 237– setreuid 237– setsid 237– setuid 237– stat 237– stat_float_times 238– strerror 238– supports_bytes_environ 215– symlink 238– sysconf 239– system 239– times 239– umask 239– uname 239– unlink 239– unsetenv 215– urandom 239– utime 240– wait 240– wait3 240– waitpid 240– walk 240– write 215os-Modul 212, 226os.path 240os.python– abspath 240– basename 241– commonprefix 241– dirname 241– exists 242– expandvars 242– getatime 243– getmtime 243– getsize 244– isabs 244– isdir 245– isfile 245

Page 431: 3446435476_Pytho

424 Stichwortverzeichnis

– islink 245– ismount 246– join 246– lexists 246– normcase 246– normpath 247– realpath 247– relpath 247– samefile 248– split 248– splitdrive 248– splitext 248Over Matching 264Overloading → Überladenoverloading 172Overwriting → Überschreiben

PPaket 95Paketkonzept 95Parameterliste 104partion 139Pascalsches Dreieck 124– Fibonacci-Zahlen 125peek 75Permutationen 309– mit Wiederholungen 309– ohne Wiederholungen 309Permutations-Generator 309Personklasse → VererbungPfadname– absolut 244– relativ 244Polymorphismus 185Polynome 341pop 75, 188 → Dictionarypopen 233popitem → DictionaryPrivate Attribute– lesender und schreibender Zugriff mittels

Methoden 166Programmiersprache– Unterschied zu Skriptsprache 17Properties 166Punktprodukt 335push 75, 188putenv 214

QQuantoren 272

Rrandom-Methode– sample 311read 218readlink 235re-Modul 263realpath 247Rechtssystem 340reduce 294Regeln zur Interpretation von römischen

Zahlen 385register_archive_format 253register_unpack_format 253reguläre Ausdrücke 261– Alternativen 279– Anfang eines Strings matchen 269– beliebiges Zeichen matchen 266– compile 280– Ende eines Strings matchen 269– Kompilierung 280– optionale Teile 271– Qunatoren 272– Teilstringsuche 263– Überlappungen 263– vordefinierte Zeichenklassen 268– Wiederholungen von Teilausdrücken 272– Zeichenauswahl 266reguläre Auswahl– Caret-Zeichen, Zeichenklasse 267reguläre Mengen 261reguläre Sprachen 261Rekursion 117– Beispiel aus der natürlichen Sprache 117rekursiv → Rekursionrekursive Funktion 117, 118relpath 247remove 77, 235removedirs 235rename 236renames 236replace 142__repr__ 400– Unterschied zu __str__ 400rfind 139rindex 139rjust 144rmdir 237rmtree 253Robot-Klasse 400Roboterklasse 189römische Zahlen 65roots 343

Page 432: 3446435476_Pytho

Stichwortverzeichnis 425

Rossum, Guido van 289round_robin 314rsplit 136– Folge von Trennzeichen 137rstrip 143Rückwärtsreferenzen 274

Ssamefile 248sample 311Schachbrett mit Weizenkörnern 66Schaltjahre 58Schleifen 55, 59– Endekriterium 59– for-Schleife 62– Schleifendurchlauf 59– Schleifenkopf 59– Schleifenkörper 59– Schleifenzähler 59– while-Schleife 60Schleifenzähler 59Schlüssel– Dictionary 33– zulässige Typen 36Scientific Python → SciPySciPy 323Sekunden– Additon zu Uhrzeiten 58Semantik 196– Fehler 196semantische Fehler 196send-Methode 312sep 241set 47set comprehension 301setdefault → Dictionarysetegid 237seteuid 237setgid 237setgroups 229setpgid 237setpgrp 237setregid 237setresgid 237setresuid 237setreuid 237sets– copy 50– difference_update 51– discard 51– intersection 52– isdisjoint 52

– issubset 53– issuperset 53– Operationen auf sets 49– pop 53– remove 52setsid 237setter 166setuid 237setUp-Methode 206Shell 211– Bash 212– Bourne 212– C-Shell 212– CLI 212– GUI 212– Herkunft und Bedeutung des Begriffes 211Shihram 66shutil– copy 249– copy2 249– copyfile 249– copyfileobj 249– copymode 249– copystat 250– copytree 250– get_archive_formats 251– get_unpack_formats 251– ignore_patterns 251– make_archive 251– move 252– register_archive_format 253– register_unpack_format 253– rmtree 253– unpack_archive 253– unregister_archive 254– unregister_unpack_format 254shutil-Modul 249Sieb des Eratosthenes 300– Rekursive Berechnung 125– Rekursive Funktion mit Mengen-Abstraktion

301Simula 67 155Skalarprodukt 335Skriptsprache– Unterschied zu Programmiersprache 17sort 82– eigene Sortierfunktionen 83– reverse 83– Umkehrung der Sortierreihenfolge 83sorted 82Sparbuch 170spezialisierte Klasse → Unterklasse

Page 433: 3446435476_Pytho

426 Stichwortverzeichnis

splice 192split 132, 248– Folge von Trennzeichen 137– maxsplit 132splitdrive 248splitext 248splitlines 138Sprachfamilie 261Stack 75– Stapelspeicher 75Standardbibliothek 90Standardklassen als Basisklassen 188Stapelspeicher 75, 188stat 237stat_float_times 238statische Attribute 168statische Typdeklaration 23Stephen Cole Kleene 261StopIteration 306__str__ 349, 400– Unterschied zu __repr__ 400strerror 238Strings– Suchen und Ersetzen 142String-Tests 145Stringmethoden– Alles in Großbuchstaben 143– Alles in Kleinbuchstaben 143– capitalize 143– center 144– count 139– find 139– index 139– isalnum 145– isalpha 145– isdigit 145– islower 145– isspace 145– istitle 145– isupper 145– ljust 144– lower 143– replace 142– rfind 139– rindex 139– rjust 144– rstrip 143– String-Tests 145– strip 143– title 143– upper 143– zfill 144

strip 143Strukturierungselement 103__sub__ 403Subklasse → UnterklasseSuchen und Ersetzen 142Suchen von Teilstrings 139Suchstrategie bei Mehrfachvererbung 174Summe von n Zahlen 60– Berechnung mit while-Schleife 60SUnit 204supports_bytes_environ 215symlink 238, 245syntaktische Fehler 195Syntax 195– Fehler 195Syntaxfehler 195sysconf 239system 239Systemprogrammierung 211

Ttar-Datei → Archivdatei erzeugenTDD → test-driven developmenttearDown-Methode 206Tests 195test first development 204test-driven development 204TestCase 206– Methoden 206– setUp-Methode 206– tearDown-Methode 206testCase 204Testgesteuerte Entwicklung 204Testgetriebene Entwicklung 202, 204Testmethoden 206Textklassifikation 365Textverarbeitung 131Theoretische Informatik 261tiefe Kopie 38, 101Tiefensuche 174times 239__truediv__ 403try 149Tupel– leere 80– mit einem Element 80– Packing 80– Unpacking 80type 25type conversion 24TypeError 199– unhashable type 37

Page 434: 3446435476_Pytho

Stichwortverzeichnis 427

Typumwandlung 24– explizit 24– implizit 25

UÜberladen 171– von Methoden 185Überlappungen 263Überschreiben 171umask 239Umgebungsvariablen 213uname 239unäre Operatoren 188Unärsystem 191Unary System → UnärsystemUnder Matching 264unhashable type 37unittest 204 → ModultestUnix 212unlink 239unpack_archive 253unregister_archive_format 254unregister_unpack_format 254unsetenv 215Unterklasse 156, 169Unterstrich– Bedeutung in der interaktiven Shell 12update → Dictionaryupper 143urandom 239utime 240

VVariablen 21Variation 310Vektoraddition 334Vektorprodukt 340Vektorsubtraktion 334

Vererbung 158, 169– Beispiel Angestelltenklasse, die von Person

erbt 169– Beispiel Kundenklasse, die von Person erbt

169– Beispiel Lieferantenklasse, die von Person

erbt 169– Beispiel Personenklasse 169Vererbungsbaum 174verschachtelte Dictionaries 37Verzeichnis löschen 237Verzweigungen 55Vollstandige Rekursion 117

Wwait 240wait3 240waitpid 240walk 240Weizenkornaufgabe 66while-Schleife 60– optionaler else-Teil 61Winkel zwischen Vektoren 335write 215

Yyield 307– im Vergleich zur return-Anweisung 307

ZZahlenratespiel 61Zeichenauswahl 266Zeitrechnung 58zeros_like() 333zfill 144zip-Datei → Archivdatei erzeugenZufallspermutation 359Zustandsautomat 268Zustandsmaschine 268Zuweisung 21

Page 435: 3446435476_Pytho

  III

Page 436: 3446435476_Pytho

Der Diplom-Informatiker Bernd

KLEIN ist Inhaber des interna tio -

nal agierenden Schulungsanbie-

ters Bodenseo. Er studierte

Informatik an der Universität

des Saarlandes. Im Laufe seines

Studiums und seiner beruflichen

Tätigkeit hat er sich mit vielen

Programmiersprachen intensiv

beschäftigt, in den letzten Jahren

jedoch auf Python spezialisiert.

Besondere Anerkennung finden

seine Python-Webseiten

www.python-kurs.eu und

www.python-course.eu.

ISBN 978-3-446-43547-6€ 24,99 [D] | € 25,70 [A]

EINFÜHRUNG IN PYTHON 3 //■ Besonders geeignet für Programmieranfänger,

aber auch für Umsteiger von anderen Sprachen

wie C, C++ oder Perl

■ Systematische und praxisnahe Einführung

in die Kunst der Programmierung

■ Praxisnahe Übungen mit ausführlich dokumen-

tierten Musterlösungen zu jedem Kapitel

Kann man in einer Woche programmieren lernen?

Genauer gesagt: Kann man in einer Woche mit

Python programmieren lernen? Es ist möglich!

Dies erfährt der Autor des Buches regelmäßig in

seinen fünftägigen Python-Kursen, die sowohl

von Programmier-Neulingen als auch von Program-

mierern mit Erfahrung in anderen Programmier-

sprachen besucht werden.

Die wesentlichen Begriffe und Techniken der Pro-

grammierung und die zugrundeliegenden Ideen

werden hier anschaulich erklärt. Zu den Problem-

stellungen werden typische Beispiele zur Verdeut -

lichung verwendet, die sich leicht auf andere

An wendungsfälle übertragen lassen. Und die

Übungsaufgaben mit ausführlich dokumentierten

Musterlösungen helfen nicht nur, den Stoff zu

vertiefen, sondern zeigen vor allem auch exempla-

rische Vorgehensweisen, die in vielen anderen

Bereichen Verwendung finden können.

In diesem Buch werden alle grundlegenden Sprach -

elemente von Python 3 behandelt und auf dieser

Basis auch weiterführende Themen wie System-

programmierung, Threads, Forks, Ausnahme -

behandlung und Modultests. In interessanten

Anwendungsfällen werden alle erlernten Konzepte

zusammengeführt: Design einer Klasse »Bruch«

für Bruchrechnung, Implementierung des Spieles

Mastermind und eine komplette Einführung in

die automatische Dokumentenklassifikation.

www.hanser-fachbuch.de/computer

AUS DEM INHALT //

■ Erste Schritte mit Python

■ Datentypen: Listen, Tupel,

Mengen und Dictionaries

■ Verzweigungen

■ Schleifen mit while und for

■ Modularisierung

■ Funktionen und rekursive

Funktionen

■ Listen- und Mengenabstraktion

im Vergleich zu »lambda«,

»map« und »filter«

■ Text- und Dateiverarbeitung

■ Reguläre Ausdrücke

■ Ausnahmebehandlung

■ Objektorientierte

Programmierung

■ Module

■ Anwendungsbeispiele:

Bruchklasse, Mastermind

und Textklassifikation