Zu diesem Buch – sowie zu vielen weiteren dpunkt.büchern – können Sie auch das entsprechende E-Book im PDF-Format herunterladen. Werden Sie dazu einfach Mitglied bei dpunkt.plus+: www.dpunkt.plus |
Ein Best-Practice-Buch für sauberes Programmieren
Al Sweigart
Lektorat: Gabriel Neumann
Lektoratsassistenz: Anja Weimer
Übersetzung: Volkmar Gronau, G&U Language & Publishing Services GmbH, Flensburg, www.GundU.com
Copy-Editing: Claudia Lötschert, www.richtiger-text.de
Satz: Ulrich Borstelmann, www.borstelmann.de
Herstellung: Stefanie Weidner
Umschlaggestaltung: Helmut Kraus, www.exclam.de, nach der Originalvorlage von No Starch Press
Bibliografische Information der Deutschen Nationalbibliothek
Die Deutsche Nationalbibliothek verzeichnet diese Publikation in der Deutschen Nationalbibliografie; detaillierte bibliografische Daten sind im Internet über http://dnb.d-nb.de abrufbar.
ISBN:
Print978-3-86490-874-3
PDF978-3-96910-677-8
ePub978-3-96910-678-5
mobi978-3-96910-679-2
1. Auflage 2022
Translation Copyright für die deutschsprachige Ausgabe © 2022 dpunkt.verlag GmbH
Wieblinger Weg 17
69123 Heidelberg
Copyright © 2021 by Al Sweigart. Title of English-language original: Beyond the Basic Stuff with Python: Best Practices for Writing Clean Code, ISBN 978-1-59327-966-0,
published by No Starch Press Inc. 245 8th Street, San Francisco, California United States 94103.
The German-language edition Copyright © 2022 by dpunkt.verlag under license by No Starch Press Inc.
All rights reserved.
Hinweis:
Dieses Buch wurde auf PEFC-zertifiziertem Papier aus nachhaltiger Waldwirtschaft gedruckt. Der Umwelt zuliebe verzichten wir zusätzlich auf die Einschweißfolie.
Schreiben Sie uns:
Falls Sie Anregungen, Wünsche und Kommentare haben, lassen Sie es uns wissen: hallo@dpunkt.de.
Die vorliegende Publikation ist urheberrechtlich geschützt. Alle Rechte vorbehalten. Die Verwendung der Texte und Abbildungen, auch auszugsweise, ist ohne die schriftliche Zustimmung des Verlags urheberrechtswidrig und daher strafbar. Dies gilt insbesondere für die Vervielfältigung, Übersetzung oder die Verwendung in elektronischen Systemen.
Es wird darauf hingewiesen, dass die im Buch verwendeten Soft- und Hardware-Bezeichnungen sowie Markennamen und Produktbezeichnungen der jeweiligen Firmen im Allgemeinen warenzeichen-, marken- oder patentrechtlichem Schutz unterliegen.
Alle Angaben und Programme in diesem Buch wurden mit größter Sorgfalt kontrolliert. Weder Autor noch Verlag noch Übersetzer können jedoch für Schäden haftbar gemacht werden, die in Zusammenhang mit der Verwendung dieses Buches stehen.
5 4 3 2 1 0
Der Autor
Der Fachgutachter
Danksagung
Einleitung
Wer dieses Buch lesen sollte und warum
Über dieses Buch
Ihr Weg zur Programmierung
1Fehlermeldungen und Recherche
Python-Fehlermeldungen verstehen
Tracebacks untersuchen
Fehlermeldungen recherchieren
Fehler vermeiden mit Lintern
Um Hilfe bitten
Geben Sie gleich ausreichend Informationen, um Rückfragen zu vermeiden
Formulieren Sie Ihre Fragen als Fragen
Stellen Sie Ihre Fragen auf einer geeigneten Website
Geben Sie das Problem in der Überschrift an
Erklären Sie, was der Code tun soll
Geben Sie die vollständige Fehlermeldung an
Teilen Sie Ihren Code vollständig mit
Gestalten Sie Ihren Code durch saubere Formatierung lesbar
Beschreiben Sie, was Sie bereits versucht haben
Beschreiben Sie Ihre Ausstattung
Ein Beispiel für eine gut gestellte Frage
Zusammenfassung
2Die Einrichtung der Umgebung und die Befehlszeile
Das Dateisystem
Pfade in Python
Das Benutzerverzeichnis
Das aktuelle Arbeitsverzeichnis
Absolute und relative Pfade
Programme und Prozesse
Die Befehlszeile
Ein Terminalfenster öffnen
Programme an der Befehlszeile ausführen
Befehlszeilenargumente
Python-Code mit –c an der Befehlszeile ausführen
Python-Programme an der Befehlszeile ausführen
Py.exe
Befehle aus einem Python-Programm heraus ausführen
Tipparbeit durch Tabulatorvervollständigung sparen
Der Befehlsverlauf
Gebräuchliche Befehle
PATH und andere Umgebungsvariablen
Umgebungsvariablen anzeigen
Die Umgebungsvariable PATH
Die Umgebungsvariable PATH in der Befehlszeile ändern
Ordner in Windows dauerhaft zu PATH hinzufügen
Ordner in macOS und Linux dauerhaft zu PATH hinzufügen
Python-Programme außerhalb der Befehlszeile ausführen
Python-Programme in Windows ausführen
Python-Programme in macOS ausführen
Python-Programme in Ubuntu Linux ausführen
Zusammenfassung
3Codeformatierung mit Black
Wie man Freunde und Kollegen vergrault
PEP 8 und andere Stilrichtlinien
Horizontale Abstände
Leerzeichen zur Einrückung verwenden
Abstände innerhalb einer Zeile
Vertikale Abstände
Beispiel für vertikale Abstände
Empfohlene Vorgehensweisen für vertikale Abstände
Black: Der kompromisslose Codeformatierer
Black installieren
Black an der Befehlszeile ausführen
Black für einzelne Abschnitte Ihres Codes ausschalten
Zusammenfassung
4Aussagekräftige Namen
Groß- und Kleinschreibung
Namenskonventionen in PEP 8
Namen geeigneter Länge
Zu kurze Namen
Zu lange Namen
Leicht zu findende Namen
Scherze, Wortspiele und Anspielungen vermeiden
Integrierte Namen nicht überschreiben
Die allerschlechtesten Variablennamen
Zusammenfassung
5Codegerüche
Duplizierter Code
Magische Zahlen
Auskommentierter und toter Code
Print-Debugging
Variablen mit numerischen Suffixen
Klassen statt Funktionen oder Module
Verschachtelte Listennotation
Leere except-Blöcke und nichtssagende Fehlermeldungen
Legenden über Code Smells
Legende: Funktionen sollten nur eine return-Anweisung am Ende aufweisen
Legende: Funktionen mit einer try-Anweisung dürfen keine anderen Anweisungen enthalten
Legende: Flag-Argumente sind schlecht
Legende: Globale Variablen sind schlecht
Legende: Kommentare sind unnötig
Zusammenfassung
6Pythonischer Code
Python-Zen
Aussagekräftige Einrückungen
Leistung mit dem Modul timeit messen
Häufig falsch angewendete Syntax
Verwenden Sie enumerate() statt range()
Verwenden Sie with statt open() und close()
Verwenden Sie is statt == zum Vergleich mit None
Stringformatierung
Verwenden Sie Rohstrings bei einer großen Anzahl von Backslashes
F-Strings
Flache Kopien von Listen
Pythonischer Umgang mit Dictionarys
Verwenden Sie get() und setdefault() für Dictionarys
Verwenden Sie collections.defaultdict für Standardwerte
Verwenden Sie Dictionarys statt switch-Konstruktionen
Bedingte Ausdrücke: Pythons »hässlicher« ternärer Operator
Variablenwerte
Zuweisungs- und Vergleichsoperatoren verketten
Eine Variable auf Gleichheit mit mehreren Werten prüfen
Zusammenfassung
7Programmierjargon
Definitionen
Die Sprache Python und der Interpreter Python
Garbage Collection
Literale
Schlüsselwörter
Objekte, Werte und Identitäten
Elemente
Veränderbare und unveränderbare Objekte
Indizes, Schlüssel und Hashes
Container-, Folgen-, Maps- und Set-Datentypen
Dunder- oder magische Methoden
Module und Pakete
Aufrufbare Objekte und Objekte erster Klasse
Häufig verwechselte Begriffe
Anweisungen und Ausdrücke
Blöcke, Klauseln und Rümpfe
Variablen und Attribute
Funktionen und Methoden
Iterierbare Objekte und Iteratoren
Syntax-, Laufzeit- und semantische Fehler
Parameter und Argumente
Implizite und explizite Typumwandlung
Eigenschaften und Attribute
Bytecode und Maschinencode
Skripte und Programme, Skriptsprachen und Programmiersprachen
Bibliotheken, Frameworks, SDKs, Engines und APIs
Zusammenfassung
Literatur
8Häufige Fallstricke in Python
Elemente zu Listen hinzufügen und entfernen
Veränderbare Werte kopieren
Standardargumente
Strings zusammenbauen
Sortierung mit sort()
Genauigkeit von Fließkommazahlen
Verkettung von Ungleichheitsoperatoren
Das Komma in einelementigen Tupeln
Zusammenfassung
9Exotische Eigenarten von Python
256 ist 256, aber 257 ist nicht 257
String-Interning
Die Bedeutung von ++ und -- in Python
Alles von nichts
Boolesche Werte als Integer
Verkettung unterschiedlicher Operatoren
Schwerelosigkeit in Python
Zusammenfassung
10Zweckmäßige Funktionen
Funktionsnamen
Der Umfang von Funktionen
Funktionsparameter und -argumente
Standardargumente
Argumente mit * und ** an Funktionen übergeben
Variadische Funktionen mit * erstellen
Variadische Funktionen mit ** erstellen
Wrapper-Funktionen mit * und ** erstellen
Funktionale Programmierung
Nebenwirkungen
Funktionen höherer Ordnung
Lambda-Funktionen
Zuordnung und Filterung mit Listennotation
Der Datentyp von Rückgabewert
Ausnahmen auslösen oder Fehlercodes zurückgeben
Zusammenfassung
11Kommentare, Docstrings und Typhinweise
Kommentare
Formatierung von Kommentaren
Inline-Kommentare
Erklärende Kommentare
Kommentare zur Gliederung
»Lessons-Learned-Kommentare«
Rechtliche Hinweise
Professionelle Formulierung
Codetags und TODO-Kommentare
Magische Kommentare und Quelldateicodierung
Docstrings
Typhinweise
Tools zur statischen Analyse
Typhinweise für mehrere Typen
Typhinweise für Listen, Dictionarys u. Ä
Rückportierung von Typhinweisen mithilfe von Kommentaren
Zusammenfassung
12Versionssteuerung mit Git
Commits und Repositorys in Git
Neue Python-Projekte mit Cookiecutter erstellen
Git installieren
Git-Benutzername und E-Mail-Adresse angeben
GUI-Werkzeuge für Git installieren
Der Arbeitsablauf in Git
Der Dateistatus in Git
Wozu gibt es bereitgestellte Dateien?
Ein Git-Repository erstellen
Zu verfolgende Dateien hinzufügen
Einzelne Dateien ignorieren
Änderungen mit Commit bestätigen
Änderungen mit git diff vor dem Commit einsehen
Änderungen mit git difftool in einer GUI-Anwendung einsehen
Häufigkeit von Commits
Dateien löschen
Dateien umbenennen und verschieben
Das Commitprotokoll einsehen
Frühere Versionen wiederherstellen
Unbestätigte lokale Änderungen rückgängig machen
Bereitstellung einer Datei aufheben
Die letzten Commits zurücknehmen
Zurücksetzen einer einzelnen Datei zu einem bestimmten Commit
Den Commitverlauf ändern
GitHub und git push
Ein bestehendes Repository auf GitHub übertragen
Ein GitHub-Repository klonen
Zusammenfassung
13Leistungsmessung und Algorithmusanalyse
Das Modul timeit
Der Profiler cProfile
Komplexitätsanalyse
Ordnungen
Ein Bücherregal als Metapher für Ordnungen
Worst Case und Best Case
Die Ordnung Ihres Codes bestimmen
Warum Terme niedriger Ordnungen und Koeffizienten keine Rolle spielen
Beispiele für die Komplexitätsanalyse
Die Ordnung gängiger Funktionsaufrufe
Komplexitätsanalyse im Überblick
Die Ordnung spielt bei kleinem n keine Rolle – und n ist gewöhnlich klein
Zusammenfassung
14Praxisprojekte
Turm von Hanoi
Die Ausgabe
Der Quellcode
Den Code schreiben
Vier gewinnt
Die Ausgabe
Der Quellcode
Den Code schreiben
Zusammenfassung
15Klassen
Formulare als Veranschaulichung
Objekte aus Klassen erstellen
Eine einfache Klasse erstellen: WizCoin
Methoden, __init__() und der Parameter self
Attribute
Private Attribute und private Methoden
Die Funktion type() und das Attribut __qualname__
OOP- und Nicht-OOP-Code im Vergleich
Klassen für reale Objekte
Zusammenfassung
16Vererbung
Wie Vererbung funktioniert
Methoden überschreiben
Die Funktion super()
Komposition statt Vererbung
Nachteile der Vererbung
Die Funktionen isinstance() und issubclass()
Klassenmethoden
Klassenattribute
Statische Methoden
Wann brauchen Sie Klassenmerkmale und statische Methoden?
Schlagwörter der objektorientierten Programmierung
Kapselung
Polymorphismus
Wann Sie die Vererbung nicht nutzen sollten
Mehrfachvererbung
Die Reihenfolge der Methodenauflösung
Zusammenfassung
17Pythonische OOP: Eigenschaften und Dunder-Methoden
Eigenschaften
Attribute in Eigenschaften umwandeln
Set-Methoden zur Datenvalidierung
Schreibgeschützte Eigenschaften
Wann Sie Eigenschaften verwenden sollten
Dunder-Methoden
Dunder-Methoden zur Stringdarstellung
Numerische Dunder-Methoden
Reflektierte numerische Dunder-Methoden
Direkte Dunder-Methoden für erweiterte Zuweisungsoperatoren
Dunder-Methoden für Vergleiche
Zusammenfassung
Stichwortverzeichnis
Für meinen Neffen Jack
Al Sweigart ist Softwareentwickler und Fachbuchautor und lebt in Seattle. Python ist seine bevorzugte Programmiersprache, für die er bereits mehrere Open-Source-Module entwickelt hat. Er hat mehrere Programmierbücher für Einsteiger geschrieben, darunter Routineaufgaben mit Python automatisieren und Coole Spiele mit Scratch 3, die ebenfalls beim dpunkt.verlag erschienen sind. Die englische Originalversion seiner Bücher steht unter einer Creative-Commons-Lizenz kostenlos auf seiner Website https://www.inventwithpython.com/ zur Verfügung. Seine Katze Zophie wiegt 11 Pfund.
Kenneth Love ist Programmierer und Lehrer und richtet Konferenzen aus. Er ist Mitarbeiter von Django und ein PSF Fellow. Zurzeit arbeitet er als technischer Leiter und Softwareingenieur für O’Reilly Media.
Es ist irreführend, dass nur mein Name auf dem Titelbild steht, denn ohne die Bemühungen vieler anderer Personen hätte es dieses Buch nie gegeben. Ich möchte meinem Herausgeber Bill Pollock, meinen Lektoren Frances Saux, Annie Choi, Meg Sneeringer und Jan Cash, Herstellungsleiterin Maureen Forys, Korrektorin Anne Marie Walker und Chefredakteurin Barbara Yien danken. Ein weiteres Dankeschön geht an Josh Ellingson für ein weiteres hervorragendes Titelbild, an meinen Fachgutachter Kenneth Love und an all die großartigen Freunde, die ich in der Python-Community kennengelernt habe.
Hello again, world! In den späten 90er-Jahren habe ich als programmierender Teenager und Möchtegernhacker immer die neueste Ausgabe von 2600: The Hacker Quarterly studiert. Eines Tages fand ich endlich den Mut, an dem von der Zeitschrift organisierten monatlichen Treffen in meiner Heimatstadt teilzunehmen, und war beeindruckt davon, wie viel diese Leute alle wussten! (Später erkannte ich jedoch, dass viele von ihnen über weit mehr Selbstvertrauen als echte Kenntnisse verfügten.) Das ganze Treffen über lauschte ich ehrfürchtig nickend dem, was die anderen zu sagen hatten, und versuchte, den Unterhaltungen zu folgen. Danach war ich entschlossen, jede Gelegenheit zu nutzen, um mehr über Computer, Programmierung und Netzwerksicherheit zu lernen, sodass ich mich beim nächsten Treffen an den Gesprächen beteiligen konnte.
Beim nächsten Treffen hörte ich jedoch weiterhin nur zu und fühlte mich im Vergleich zu allen anderen minderbemittelt. Also fasste ich erneut den Vorsatz, zu lernen und klug genug zu werden, um mitzuhalten. Ich vertiefte mein Wissen Monat für Monat, hatte aber immer das Gefühl, hinterherzuhinken. Schließlich erkannte ich, wie umfangreich das Gebiet der Informatik war, und befürchtete, niemals genug wissen zu können.
Ich wusste damals zwar schon mehr als meine Schulfreunde, doch meine Kenntnisse reichten mit Sicherheit nicht aus, um als Softwareentwickler arbeiten zu können. In den 90er-Jahren gab es Google, YouTube und Wikipedia noch nicht, aber ich hätte auch gar nicht gewusst, wie ich sie hätte nutzen sollen. Es war mir nie klar, womit ich mich als Nächstes befassen sollte. Stattdessen lernte ich, Hallo-Welt-Programme in verschiedenen Programmiersprachen zu schreiben. Dabei hatte ich jedoch nie das Gefühl, echte Fortschritte zu machen. Ich wusste nicht, wie ich jemals über die Grundlagen hinauskommen sollte.
Zur Softwareentwicklung gehört weit mehr als Schleifen und Funktionen. Wenn Sie einen Anfängerkurs absolviert oder ein Programmierbuch für Einsteiger gelesen haben und sich nach weiterführenden Informationen umsehen, landen Sie aber leider meistens nur bei weiteren Hallo-Welt-Tutorials. Programmierer nennen diese Phase die Wüste der Verzweiflung: Sie bewegen sich ziellos durch unterschiedliche Lernstoffe, ohne das Gefühl zu haben, Fortschritte zu machen. Für Material, das sich an Anfänger richtet, wissen Sie schon zu viel, aber es fehlt Ihnen an Erfahrung, um sich den komplizierteren Themen zu widmen.
Wenn Sie sich in dieser Wüste befinden, kommen Sie sich wie ein Hochstapler vor. Sie haben nicht das Gefühl, ein »richtiger« Programmierer zu sein oder Code so schreiben zu können, wie »richtige« Programmierer es tun. Dieses Buch habe ich für Personen geschrieben, denen es genau so geht. Wenn Sie die Grundlagen von Python kennen, hilft es Ihnen, ein besserer Softwareentwickler zu werden und dieses Gefühl der Verzweiflung zu überwinden.
Dieses Buch richtet sich an Leser, die bereits einen Grundkurs in Python absolviert haben und mehr lernen möchten. Bei diesem Grundkurs kann es sich um mein Buch Routineaufgaben mit Python automatisieren (dpunkt.verlag, 2020), das Buch Python 3 Crashkurs von Eric Matthes (dpunkt.verlag, 2020) oder auch eine Onlineschulung handeln.
Solche Einführungen können Ihre Begeisterung für die Programmierung wecken, allerdings gibt es danach immer noch viel zu lernen. Wenn Sie das Gefühl haben, noch nicht das Niveau eines professionellen Programmierers erreicht zu haben, und nicht wissen, wie Sie dorthin gelangen sollen, ist dies das richtige Buch für Sie.
Vielleicht haben Sie ja auch in einer anderen Sprache zu programmieren gelernt und möchten nun unmittelbar in das Python-Umfeld mit allen seinen Tools wechseln, ohne die typischen Hello-World-Grundlagen wiederzukäuen. In diesem Fall brauchen Sie nicht erst Hunderte von Seiten durchzuarbeiten, die die grundlegende Syntax erklären. Es reicht, wenn Sie sich den Artikel »Learn Python in Y Minutes« auf https://learnxinyminutes.com/docs/python ansehen oder Eric Matthes’ Python-Spickzettel von »Python Crash Course – Cheat Sheet« auf https://ehmatthes.github.io/pcc/cheatsheets/README.html herunterladen, bevor Sie dieses Buch in Angriff nehmen.
In diesem Buch geht es nicht nur um Python-Syntax für Fortgeschrittene, sondern auch um die Verwendung der Befehlszeile und von Tools wie Codeformatierern, Lintern und Versionssteuerung, die auch professionelle Entwickler einsetzen.
Ich erkläre Ihnen, was Code gut lesbar macht und wie Sie sauberen Code schreiben können. Zur Veranschaulichung dieser Prinzipien stelle ich einige Programmierprojekte vor. Dies soll zwar kein Lehrbuch in Informatik werden, aber dennoch werden wir uns auch mit der O-Notation und objektorientierter Entwicklung beschäftigen.
Ein Buch allein reicht nicht aus, um jemanden zu einem professionellen Softwareentwickler zu machen. Ich habe dieses Buch geschrieben, um Ihre Kenntnisse zu vertiefen und Ihnen dadurch auf diesem Weg weiterzuhelfen. Dabei spreche ich auch einige Themen an, die Sie anderenfalls nur nach und nach durch Erfahrung lernen würden. Dieses Buch verschafft Ihnen eine solide Grundlage, sodass Sie gut für neue Herausforderungen gerüstet sind.
Ich rate Ihnen zwar dazu, das Buch von vorn bis hinten zu lesen, aber wenn ein Thema Sie besonders interessiert, können Sie auch gern zu dem betreffenden Kapitel vorblättern.
Teil I: Erste Schritte
Teil II: Werkzeuge, Techniken und bewährte Vorgehensweisen
Teil III: Objektorientiertes Python
Auf dem Weg vom Anfänger zum erfahrenen Programmierer fühlt man sich oft so, als ob man versucht, aus einem unter hohem Druck stehenden Feuerwehrschlauch zu trinken. Angesichts des großen Angebots an Lernstoff sorgen sich viele, ihre Zeit mit ungeeigneten Programmieranleitungen zu vergeuden.
Nachdem Sie dieses Buch gelesen haben (oder vielleicht sogar schon während der Lektüre), sollten Sie sich die folgenden weiteren einführenden Werke ansehen:
Die technischen Aspekte sind aber nur eine Stärke von Python. Rund um diese Programmiersprache ist auch eine bunte Community gewachsen. Sie ist für die gut zugängliche Dokumentation und den Support verantwortlich, die unter Programmiersprachen ihres Gleichen suchen. Es gibt jährliche PyCon-Konferenzen sowie viele regionale Zusammenkünfte mit vielfältigen Vorträgen, die sich an Programmierer mit unterschiedlicher Erfahrung richten. Diese Vorträge können Sie sich auch kostenlos online auf https://pyvideo.org/ ansehen. Um Vorträge zu Ihren Interessengebieten zu finden, schlagen Sie auf der Seite Tags nach.
Um sich intensiver mit den anspruchsvolleren Aspekten der Syntax von Python und der Standardbibliothek zu beschäftigen, empfehle ich Ihnen die Lektüre der folgenden Titel:
Viel Glück auf Ihrem Weg zur besseren Programmierung! Fangen wir an!
Vermenschlichen Sie niemals Computer – das können die nämlich überhaupt nicht leiden! Aber im Ernst: Wenn Ihnen ein Computer eine Fehlermeldung präsentiert, dann liegt das nicht daran, dass Sie ihn beleidigt hätten. Computer sind zwar die anspruchsvollsten Werkzeuge, mit denen die meisten von uns jemals zu tun bekommen, aber sie sind und bleiben nun mal nichts anderes als Werkzeuge.
Dennoch sind wir schnell geneigt, diesen Werkzeugen Schuld zuzuschieben. Wenn Sie programmieren lernen, sind Sie dabei zum größten Teil auf sich selbst gestellt. Dabei kann man sich schnell als Versager fühlen, wenn man immer noch mehrmals täglich im Internet nachforscht, auch wenn man sich bereits seit Monaten mit Python beschäftigt. Aber selbst professionelle Softwareentwickler suchen im Internet oder schlagen in der Dokumentation nach, um Fragen zur Programmierung zu klären.
Sofern Sie nicht über die Geldmittel oder die guten Kontakte verfügen, um einen Privatlehrer zu engagieren, der Ihnen alle Fragen rund ums Programmieren beantwortet, stehen Ihnen nur Ihr Computer, die Suchmaschinen im Internet und Ihre eigene Geschicklichkeit zur Verfügung. Glücklicherweise aber wurden die Fragen, die Sie haben, mit hoher Wahrscheinlichkeit schon einmal gestellt. Für Programmierer ist es viel wichtiger, selbstständig Antworten zu finden, als irgendwelche Algorithmen oder Datenstrukturen zu kennen. In diesem Kapitel erfahren Sie, wie Sie sich diese unverzichtbare Fähigkeit aneignen können.
Bei der Konfrontation mit einer Fehlermeldung, die einen Riesenwust von Technoblabla enthält, neigen viele Programmierer in einem ersten Impuls heraus dazu, sie völlig zu ignorieren. Allerdings versteckt sich in dieser Meldung die Antwort auf die Frage, was bei dem Programm schiefläuft. Um diese Antwort zu finden, sind zwei Schritte erforderlich: Sie müssen die Ablaufverfolgung (Traceback) untersuchen und die Fehlermeldung im Internet recherchieren.
Python-Programme stürzen ab, wenn der Code eine Ausnahme auslöst, die nicht von einer except-Anweisung behandelt wird. Wenn das geschieht, zeigt Python die Meldung dieser Ausnahme und ein Traceback (oder auch Stacktrace oder Ablaufverfolgung) an. Darin ist die Stelle in dem Programm angegeben, an der die Ausnahme ausgelöst wurde, sowie der Ablauf der Funktionsaufrufe, die dorthin geführt haben.
Um das Lesen von Tracebacks zu üben, geben Sie das folgende fehlerhafte Programm ein und speichern es als abcTraceback.py. Die Zeilennummern dienen hier nur zur Orientierung und gehören nicht mit zu dem Programm.
1. def a():
2. print('Start of a()')
3. b() # Ruft b() auf.
4.
5. def b():
6. print('Start of b()')
7. c() # Ruft c() auf.
8.
9. def c():
10. print('Start of c()')
11. 42 / 0 # Verursacht eine Division durch null.
12.
13. a() # Ruft a() auf.
In diesem Programm ruft a() die Funktion b() auf und diese wiederum c() . Innerhalb von c() aber führt der Ausdruck 42 / 0 zu einem Fehler aufgrund der Division durch null. Wenn Sie dieses Programm ausführen, erhalten Sie folgende Ausgabe:
Start of b()
Start of c()
Traceback (most recent call last):
File "abcTraceback.py", line 13, in <module>
a() # Ruft a() auf.
File "abcTraceback.py", line 3, in a
b() # Ruft b() auf.
File "abcTraceback.py", line 7, in b
c() # Ruft c() auf.
File "abcTraceback.py", line 11, in c
42 / 0 # Verursacht eine Division durch null.
ZeroDivisionError: division by zero
Sehen wir uns diese Ablaufverfolgung nun Zeile für Zeile an. Dabei fangen wir mit der folgenden Zeile an:
Traceback (most recent call last):
Diese Meldung besagt, dass als Nächstes ein Traceback folgt. Die Angabe most recent call last bedeutet, dass die Funktionsaufrufe in chronologischer Reihenfolge aufgeführt werden, also von der zuerst bis zu der zuletzt aufgerufenen Funktion.
Die nächste Zeile gibt dann den ersten Funktionsaufruf aus:
File "abcTraceback.py", line 13, in <module>
a() # Ruft a() auf.
Diese beiden Zeilen bilden eine Frame-Übersicht und geben die Informationen an, die sich in einem Frame-Objekt befinden. Beim Aufruf einer Funktion werden die Daten der lokalen Variablen sowie die Stelle im Code, zu der nach dem Ende des Funktionsaufrufs zurückgesprungen werden soll, in einem solchen Objekt gespeichert. Frame-Objekte enthalten also die Daten der lokalen Variablen und weitere Daten, die im Zusammenhang mit einem Funktionsaufruf stehen. Sie werden erstellt, wenn eine Funktion aufgerufen wird, und zerstört, wenn die Funktion die Steuerung zurückgibt. Das Traceback enthält eine Frame-Übersicht für jeden Frame bis zum Absturz. Dieser Übersicht können wir entnehmen, dass der Funktionsaufruf in Zeile 13 von abcTracebyck.py erfolgte. Die Angabe <module> teilt uns außerdem mit, dass sich die Zeile im globalen Gültigkeitsbereich befindet. Mit einer Einrückung um zwei Stellen wird außerdem die Zeile 13 ausgegeben.
Die nächsten vier Zeilen enthalten zwei weitere Frame-Übersichten:
File "abcTraceback.py", line 3, in a
b() # Ruft b() auf.
File "abcTraceback.py", line 7, in b
c() # Ruft c() auf.
Die Angabe line 3, in a sagt uns, dass b() in Zeile 3 innerhalb der Funktion a() aufgerufen wurde. Das wiederum führte dazu, dass in Zeile 7 innerhalb von b() die Funktion c() aufgerufen wurde. Beachten Sie, dass die Aufrufe von print() in Zeile 2, 6 und 10 im Traceback nicht erscheinen, obwohl sie vor den Funktionsaufrufen ausgeführt wurden. In einer Ablaufverfolgung werden nur die Zeilen mit den Funktionsaufrufen bis zum Auslösen der Ausnahme ausgegeben.
Die letzte Frame-Übersicht gibt die Zeile an, in der die nicht behandelte Ausnahme ausgelöst wurde, gefolgt vom Namen und der Meldung dieser Ausnahme:
File "abcTraceback.py", line 11, in c
42 / 0 # Verursacht eine Division durch null.
ZeroDivisionError: division by zero
Die angegebene Nummer ist diejenige der Zeile, in der Python den Fehler bemerkt hat. Die eigentliche Ursache des Fehlers kann sich jedoch durchaus weiter vorn befinden.
Fehlermeldungen sind berüchtigt für ihre Kürze und Rätselhaftigkeit. Die drei Wörter division by zero werden Ihnen nicht viel sagen, sofern Sie nicht wissen, dass eine Division durch null mathematisch unmöglich ist und einen häufigen Bug in Software darstellt. In diesem Programm ist der Fehler nicht sehr schwer zu finden. Ein Blick auf die Codezeile in der Frame-Übersicht genügt, um zu erkennen, wo in dem Code 42 / 0 die Division durch null erfolgt.
Sehen wir uns nun aber ein kniffligeres Beispiel an. Geben Sie den folgenden Code in einen Texteditor ein und speichern Sie ihn als zeroDevideTraceback.py:
def spam(number1, number2):
return number1 / (number2 - 42)
spam(101, 42)
Wenn Sie dieses Programm ausführen, erhalten Sie folgende Ausgabe:
Traceback (most recent call last):
File "zeroDivideTraceback.py", line 4, in <module>
spam(101, 42)
File "zeroDivideTraceback.py", line 2, in spam
return number1 / (number2 - 42)
ZeroDivisionError: division by zero
Die Fehlermeldung ist die gleiche wie zuvor, aber die Division durch null in return number1 / (number2 - 42) lässt sich nicht so leicht erkennen. Der Operator / zeigt Ihnen, dass hier eine Division erfolgt, und offensichtlich wird der Ausdruck (number2 - 42) zu 0 ausgewertet. Daraus können Sie schließen, dass die Funktion spam() fehlschlägt, wenn der Parameter number2 auf 42 gesetzt wird.
Manchmal gibt ein Traceback auch eine Zeile aus, die hinter der eigentlichen Ursache des Bugs liegt. Betrachten Sie dazu das folgende Programm, bei dem in der ersten Zeile die schließende Klammer fehlt:
print('Hello.'
print('How are you?')
Die Fehlermeldung aber weist auf ein Problem in der zweiten Zeile hin:
File "example.py", line 2
print('How are you?')
^
SyntaxError: invalid syntax
Der Grund dafür ist, dass der Python-Interpreter den Syntaxfehler erst bemerkt, wenn er die zweite Zeile liest. Das Traceback zeigt Ihnen, wo etwas schiefgeht, aber dort liegt nicht immer die Ursache des Fehlers. Wenn die Frame-Übersicht Ihnen nicht genügend Informationen gibt, um den Bug zu erkennen, oder wenn die wahre Ursache in einer vorherigen Zeile steckt, dann müssen Sie das Programm mit einem Debugger durchgehen oder Protokollmeldungen durchsuchen, um den Fehler zu finden. Eine Internetrecherche der Fehlermeldung kann Ihnen dabei entscheidende Hinweise geben.
Fehlermeldungen sind sehr kurz und oft nicht einmal ganze Sätze. Da Programmierer sie häufiger zu Gesicht bekommen, sind sie eher als Gedächtnisstützen gedacht und weniger als ausführliche Erklärungen. Wenn Sie eine neue Fehlermeldung zum ersten Mal sehen, können Sie sie kopieren und in eine Suchmaschine einfügen. Dadurch erhalten Sie oft ausführliche Erklärungen darüber, was die Meldung besagt und was die möglichen Ursachen sein können. Abbildung 1–1 zeigt die Ergebnisse einer Suche nach python »ZeroDivisonError: division by zero«. Die Anführungszeichen sorgen dafür, dass Vorkommen der genauen Formulierung gefunden werden, und der Zusatz python engt die Suche sinnvoll ein.
Fehlermeldungen zu recherchieren, hat nichts mit Schummeln zu tun. Niemand kann sich sämtliche Fehlermeldungen merken, die in einer Programmiersprache auftreten können. Professionelle Softwareentwickler nutzen täglich das Internet, um Fragen zur Programmierung zu klären.
Es ist sinnvoll, bei der Suche den Teil der Fehlermeldung auszuschließen, der sich ausschließlich auf Ihren Code bezieht. Betrachten Sie zum Beispiel die folgenden Fehlermeldungen:
>>> print(employeRecord)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'employeRecord' is not defined
>>> 42 - 'hello'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for -: 'int' and 'str'
In diesem Beispiel wurde eine Variable als employeRecord falsch geschrieben, was einen Fehler hervorruft . Da der Bezeichner employeRecord in der Meldung NameError: name 'employeRecord' is not defined nur in Ihrem Code eine Bedeutung hat, sollten Sie nicht nach dem gesamten Text der Meldung suchen, sondern nur nach python »NameError: name« »is not defined«. In der letzten Zeile beziehen sich die Angaben 'int' und 'str' in der Fehlermeldung auf die Werte 42 und 'hello', weshalb es sinnvoll ist, nur nach python »TypeError: unsupported operand type(s)« zu suchen. Wenn Sie mit solchen verkürzten Suchbegriffen keinen Erfolg haben, können Sie versuchen, nach dem kompletten Text der Fehlermeldung zu forschen.
Besser, als Fehler zu beheben, ist es natürlich, sie von vornherein zu vermeiden. Sogenannte Linter sind Anwendungen, die Ihren Quellcode analysieren und Sie auf mögliche Fehler hinweisen. Der Name ist übrigens von den Fusselsieben von Waschmaschinen und Trocknern abgeleitet. Auch wenn Linter nicht sämtliche Fehler finden können, sind sie doch in der Lage, mithilfe einer statischen Analyse (also einer Untersuchung des Quellcodes, ohne ihn auszuführen) gängige Probleme etwa aufgrund von falschen Schreibungen aufzuspüren. (In Kapitel 11 werden wir uns ansehen, wie Sie Typhinweise zur statischen Analyse einsetzen können.) Viele Texteditoren und integrierte Entwicklungsumgebungen (IDEs) verfügen über einen Linter, der im Hintergrund läuft und Probleme in Echtzeit aufzeigen kann (siehe Abb. 1–2).
Da Linter Sie fast sofort auf Fehler hinweisen, tragen sie zu einer erheblichen Steigerung Ihrer Produktivität bei. Ohne sie müssten Sie das Programm erst ausführen, um festzustellen, dass es abstürzt, und dann das Traceback lesen und die Zeile in Ihrem Quellcode finden, in der sich der Tippfehler befindet. Ein solcher Ausführungs-Korrektur-Zyklus kann außerdem immer nur einen solchen Fehler auf einmal finden. Ein Linter dagegen macht Sie auf mehrere Fehler zugleich aufmerksam, und zwar direkt im Editor, sodass Sie sofort die Zeile sehen, um die es geht.
Wenn Ihr Editor oder Ihre IDE nicht über einen Linter verfügt, aber Plug-ins unterstützt, gibt es mit hoher Wahrscheinlichkeit einen Linter dafür. Solche Plug-ins nutzen für die Analyse gewöhnlich ein Lintingmodul wie Pyflakes o. Ä. Pyflakes können Sie auf https://pypi.org/project/pyflakes/ oder mit pip install --user pyflakes installieren. Die Mühe lohnt sich.
Hinweis
Unter Windows können Sie die Befehle python und pip verwenden, aber unter macOS und Linux gelten diese Befehle nur für Python Version 2. Für Version 3 müssen Sie dort python3 und pip3 schreiben. Denken Sie daran, wenn in diesem Buch python oder pip erwähnt werden.
IDLE, die im Lieferumfang von Python enthaltene IDE, hat keinen Linter und bietet auch nicht die Möglichkeit, einen zu installieren.
Wenn Sie ein Problem weder durch eine Recherche im Internet noch mithilfe eines Linters lösen können, bleibt Ihnen noch die Möglichkeit, im Internet um Hilfe zu bitten. Dabei müssen Sie jedoch die Etikette beachten und Ihre Fragen möglichst effizient stellen. Wenn sich erfahrene Softwareentwickler schon bereit erklären, Ihre Fragen kostenlos zu beantworten, dann sollten Sie deren Zeit tunlichst nicht verschwenden.
Fremde um Hilfe beim Programmieren zu bitten, sollte immer die letzte Möglichkeit sein. Es können Stunden oder gar Tage vergehen, bis jemand auf Ihre Frage antwortet – wenn überhaupt. Es geht viel schneller, im Web danach zu suchen, ob andere bereits die gleiche Frage gestellt haben, und dann die Antworten zu lesen. Onlinedokumentation und Suchmaschinen wurden schließlich erfunden, um den Aufwand für die Beantwortung von Fragen zu reduzieren.
Wenn Sie aber bereits alle anderen Möglichkeiten ausgeschöpft haben und andere Menschen um Hilfe bei Ihren Programmierproblemen bitten müssen, sollten Sie unbedingt die folgenden häufig zu beobachtenden Fehler vermeiden: