- Lernen
- Informatik & Informationswissenschaften
- Programmierung
- Python-Programmierung – Schleifen, Funktionen, Listen und Dateien
- Python-Module, Fehlersuche und Tests
Grundlagen: Python-Module, Fehlersuche und Tests
math und random, schreibe eigene Module und installiere Pakete von PyPI mit pip. Lies anschließend Tracebacks und Fehlermeldungen, finde Programmfehler durch schrittweise Fehlersuche und prüfe deine Funktionen mit assert-Anweisungen und ersten Unit-Tests.AnfängerEinheit 9 von 10 in Kurs: Python-Programmierung – Schleifen, Funktionen, Listen und Dateien
Was du lernst
- Binde fertigen Code mit import-Anweisungen in ein Programm ein, verwende Standardbibliotheksmodule wie
mathundrandom, teile eigenen Code in Module auf und installiere Pakete von Drittanbietern mit pip.- Code mit import-Anweisungen in ein Programm einbindenVerwende
import,from … importundasrichtig, greife mit Punktnotation auf Namen zu und unterscheide Importfehler. - Mit math, random und anderen Standardmodulen rechnen und simulierenVerwende die wichtigsten Funktionen von
mathundrandom, vermeide häufige Fehler und lerne einige weitere alltägliche Standardbibliotheksmodule kennen. - Eigene Module schreiben und importierenTeile Code auf eigene
.py-Dateien auf, importiere sie, vermeide Namenskonflikte und verwendeif __name__ == "__main__":, damit eine Datei sowohl ausgeführt als auch importiert werden kann. - Pakete mit pip installieren und verwaltenInstalliere, untersuche, aktualisiere und entferne Pakete von Drittanbietern von PyPI, importiere sie unter dem richtigen Namen und löse die üblichen Installationsprobleme.
- Code mit import-Anweisungen in ein Programm einbinden
- Nutze Pythons Fehlerausgabe, um herauszufinden, wo und warum ein Programm gescheitert ist, und unterscheide Syntaxfehler, Laufzeitfehler und Logikfehler.
- Einen Traceback von unten nach oben lesenFinde den Ausnahmetyp, die Meldung, die fehlerauslösende Zeile und die Aufrufkette, die dorthin geführt hat.
- Häufige Fehler und ihre Ursachen erkennenOrdne die häufigsten Ausnahmetypen ihren üblichen Ursachen und Lösungen zu und erkenne Logikfehler, die keine Meldung erzeugen.
- Einen Traceback von unten nach oben lesen
- Finde die Ursache eines Programmfehlers durch schrittweises Vorgehen statt durch Raten und untersuche ein laufendes Programm mit print-Anweisungen und einem Debugger.
- Bei der Fehlersuche schrittweise vorgehenReproduziere einen Programmfehler, stelle eine Hypothese auf und prüfe sie, grenze die Ursache ein, behebe sie und teste erneut.
- Ein Programm mit print-Anweisungen, Ablauftabellen und einem Debugger untersuchenBeobachte, wie sich Werte während der Codeausführung ändern, indem du Ausgaben hinzufügst, den Ablauf von Hand nachvollziehst oder an Haltepunkten pausierst.
- Bei der Fehlersuche schrittweise vorgehen
- Verwende
assert, um festzuhalten, was wahr sein muss, schreibe kleine automatisierte Tests für deine Funktionen und führe sie mit dem Standardmodulunittestaus und lies ihre Ergebnisse.- Annahmen mit assert-Anweisungen prüfenSchreibe
assert-Anweisungen mit klaren Meldungen und erkenne, wann Zusicherungen das richtige Werkzeug sind und wann nicht. - Erste Unit-Tests für eigene Funktionen schreibenSchreibe Testfunktionen mit Zusicherungen, wähle typische Fälle, Grenzwertfälle und Randfälle und nutze fehlgeschlagene Tests, um Fehler zu finden und zu beheben.
- Tests mit unittest organisieren und ausführenSchreibe Tests mit
unittest.TestCase, führe sie aus und lies den Bericht über bestandene Tests, fehlgeschlagene Tests und Testfehler.
- Annahmen mit assert-Anweisungen prüfen
Python-Module, Fehlersuche und Tests einfach erklärt
1Module wiederverwenden und Pakete installieren
Mit importiertem Code kann ein kleines Programm Berechnungen, Simulationen und Werkzeuge nutzen, die anderswo geschrieben wurden. Du wählst eine Importform, arbeitest mit Funktionen der Standardbibliothek und ordnest deine eigenen Funktionen so an, dass eine andere Datei sie wiederverwenden kann. Bei der Installation stellt sich eine weitere Frage: In welchem Python wird ein Paket eines Drittanbieters installiert? Wenn du diesen Zusammenhang verstehst, kannst du ein fehlendes Modul leichter diagnostizieren und die Namen in jedem Programm übersichtlich halten.
1.1 Ein Modul oder ausgewählte Namen importieren
Ein Modul enthält Python-Code, den ein anderes Programm wiederverwenden kann. Die meisten Module sind .py-Dateien. Die Standardbibliothek wird mit Python geliefert; Code von Drittanbietern muss separat installiert werden. Ein Paket bündelt zusammengehörige Module, meist in einem Ordner.
Den Aufruf passend zum Import schreiben
Ein Import erzeugt Namen in deinem Programm. Die gewählte Form bestimmt, wie du den importierten Code aufrufst.
| Anweisung | Erzeugter Name | Beispielaufruf |
|---|---|---|
import math | math | math.sqrt(16) |
from math import sqrt | sqrt | sqrt(16) |
import statistics as stats | stats | stats.mean([2, 4, 9]) |
from random import randint as roll | roll | roll(1, 6) |
Punktnotation bedeutet, einen Namen innerhalb eines Moduls oder Objekts nachzuschlagen. In math.sqrt(16) findet Python sqrt innerhalb von math und ruft es mit 16 auf. Das Ergebnis ist 4.0. Die Konstante math.pi braucht keine Aufrufklammern, weil sie ein Wert und keine Funktion ist.
Nach import math löst sqrt(16) allein einen NameError aus: Das Programm hat einen Namen für das Modul, aber keinen eigenen Namen für sqrt. Nach alleinstehendem from math import sqrt löst der umgekehrte Fehler math.sqrt(16) ebenfalls einen NameError aus, weil math nicht gebunden wurde. Ein Alias ersetzt den üblichen Namen der importierten Bindung: import statistics as stats erzeugt stats, nicht beide Namen.
Dieselbe Berechnung auf zwei Arten durchführen
Ein rechtwinkliges Dreieck hat die Seitenlängen 3 und 4. Quadriere zuerst die Seitenlängen und addiere sie; ziehe dann die Quadratwurzel.
import math
length = math.sqrt(3**2 + 4**2)
print(length) # 5.0
Der Ausdruck innerhalb des Aufrufs wird zu 9 + 16, dann zu 25; sqrt gibt 5.0 zurück. Mit from math import sqrt schreibst du stattdessen length = sqrt(3**2 + 4**2). Der vorangestellte Modulname macht die Herkunft einer Funktion sichtbar und hält die übrigen Namen des Moduls aus dem Namensraum deines Programms heraus. Die Bindung math selbst kann weiterhin eine frühere Variable namens math ersetzen.
Namen übersichtlich halten
Vermeide from math import *. Dieser Wildcard-Import bringt öffentliche Namen in dein Programm, ohne sie einzeln zu nennen. Zum Beispiel ändert e = 5, gefolgt von diesem Import, den Wert von e zu 2.718281828459045. Ein scheinbar unbeteiligter Import kann daher eine spätere Berechnung verändern. PEP 8 empfiehlt explizite Importe und setzt sie an den Anfang, mit einem Modul pro Zeile: zuerst die Standardbibliothek, danach Pakete von Drittanbietern, zuletzt eigene Module.
Python führt den Code auf Modulebene beim ersten Import eines Moduls während eines Programmlaufs aus. Spätere Importe verwenden das geladene Modul wieder; sie starten es nicht erneut.
Anhand des Fehlers die Lösung wählen
| Fehler | Was du prüfen solltest |
|---|---|
ModuleNotFoundError bei import mathh | Schreibweise und Installation des Moduls |
ImportError bei from math import square_root | Der vom Modul angeforderte Name |
AttributeError bei math.sqroot(16) | Der Name nach dem Punkt |
Führe in der interaktiven Shell import math und dann dir(math) aus, um die Namen des Moduls aufzulisten. Führe help(math.sqrt) aus, um die Dokumentation einer Funktion zu lesen. Die offizielle Python-Bibliotheksreferenz erklärt ihre Argumente und ihr Ergebnis.
Wichtige Erkenntnisse
import moduleerzeugt eine Modulbindung; auf den Inhalt greifst du mit Punktnotation zu.from module import nameerzeugt die ausgewählte Bindung; mitaswählst du einen Alias.- Explizite Importe halten die Herkunft von Namen erkennbar und verringern unbeabsichtigtes Ersetzen.
- Importfehler unterscheiden zwischen einem nicht gefundenen Modul, einem nicht verfügbaren importierten Namen und einem fehlenden Attribut.
dir()listet Namen auf;help()erklärt, wie du einen davon verwendest.
1.2 Funktionen aus math und random auswählen
Die Standardbibliothek stellt getestete Werkzeuge für Berechnungen und Simulationen bereit. Welche Funktion du wählst, hängt vom benötigten Ergebnis ab: einer ganzzahligen Anzahl, einem gerundeten Messwert, einem zufälligen Element oder einer neu angeordneten Liste.
Runden hat mehrere Bedeutungen
Nach import math gibt math.sqrt(16) die Gleitkommazahl 4.0 zurück. math.pi und math.e liefern die Konstanten 3.141592653589793 und 2.718281828459045. math.factorial(5) multipliziert die ganzen Zahlen von 1 bis einschließlich 5 und gibt 120 zurück.
| Ausdruck | Ergebnis | Bedeutung |
|---|---|---|
math.floor(7.9) | 7 | Größte ganze Zahl, die nicht größer als der Wert ist |
math.ceil(2.1) | 3 | Kleinste ganze Zahl, die nicht kleiner als der Wert ist |
math.floor(-2.5) | -3 | Abrunden erfolgt in Richtung minus unendlich |
int(-3.5) | -3 | Die Umwandlung schneidet in Richtung null ab |
round(2.5) | 2 | Ein Wert genau in der Mitte wird zur geraden Zahl gerundet |
round(3.5) | 4 | Die benachbarte gerade ganze Zahl ist 4 |
Für 23 Quadratmeter Wand und 10 Quadratmeter pro Farbdose ergibt die Division 2.3 Dosen. Um ganze Dosen zu kaufen, brauchst du math.ceil(23 / 10), das 3 zurückgibt. round(2.3) ergibt 2 und damit zu wenig Farbe.
Die eingebaute Funktion round() braucht keinen Import. Für einen Kreis mit Radius 2 ergibt round(math.pi * 2 ** 2, 2) den Wert 12.57. Runden ändert die Genauigkeit einer Zahl; das ist eine andere Aufgabe, als genügend ganze Behälter zu kaufen.
Definitionsbereiche und Vergleiche von Gleitkommazahlen prüfen
math.sqrt(-1) löst ValueError aus, weil es für diese Eingabe keine reelle Quadratwurzel gibt. Auch bei berechneten Gleitkommazahlen ist Vorsicht nötig: Aufgrund der binären Speicherung ist 0.1 + 0.2 == 0.3 gleich False. math.isclose(0.1 + 0.2, 0.3) ist True; verwende es für einen näherungsweisen Vergleich berechneter Gleitkommazahlen.
Die Zufallsoperation auswählen
Das Modul random erzeugt mit einem Algorithmus Pseudozufallsergebnisse.
| Aufruf | Ergebnis |
|---|---|
random.randint(1, 6) | Eine ganze Zahl von 1 bis einschließlich 6 |
random.randrange(1, 6) | Eine ganze Zahl von 1 bis einschließlich 5 |
random.random() | Eine Gleitkommazahl ab 0,0 und kleiner als 1,0 |
random.uniform(a, b) | Eine Gleitkommazahl zwischen den angegebenen Endpunkten |
random.choice(items) | Ein Element |
random.sample(items, k) | Elemente von k Positionen, ohne Zurücklegen |
random.shuffle(items) | Ordnet die vorhandene Liste neu; gibt None zurück |
Schreibe random.shuffle(cards) in eine eigene Zeile. Weist du das Ergebnis wieder cards zu, geht die Bindung an die Liste verloren, weil sie durch None ersetzt wird. Positionen zu ziehen ist außerdem etwas anderes, als unterschiedliche Werte zu verlangen: random.sample([1, 1], 2) gibt [1, 1] zurück.
Eine reproduzierbare Simulation ausführen
import random
sevens = 0
for _ in range(1000):
if random.randint(1, 6) + random.randint(1, 6) == 7:
sevens += 1
print(sevens)
Sechs von 36 gleich wahrscheinlichen Paaren ergeben zusammen 7, deshalb liegt die Anzahl meist nahe bei 167, ändert sich aber zwischen den Läufen. Um eine Folge für die Fehlersuche zu reproduzieren, rufe vor dem Erzeugen der Werte random.seed(42) auf und verwende dieselbe Python-Version und Aufruffolge. Verwende für Passwörter oder Sicherheitstoken secrets.
Weitere alltägliche Werkzeuge finden
statistics.mean([2, 4, 9]) gibt 5 zurück; statistics.median([2, 4, 9]) gibt 4 zurück. datetime.date.today() liefert das heutige Datum. Nach import sys enthält sys.argv den Skriptnamen gefolgt von seinen Argumenten: Der Aufruf python greet.py Ada liefert ['greet.py', 'Ada'].
Wichtige Erkenntnisse
floor,ceil,intundroundbeantworten unterschiedliche Fragen zum Runden.- Verwende
math.isclose, wenn du berechnete Gleitkommazahlen vergleichst. randintschließt beide Endpunkte ein;randrangeschließt seine Endgrenze aus.samplewählt Positionen ohne Zurücklegen aus, sodass doppelte Eingabewerte wiederholt auftreten können.shuffleverändert eine Liste direkt und gibtNonezurück.- Ein fester Startwert hilft, zufälliges Verhalten zu reproduzieren; für Sicherheitstoken brauchst du
secrets.
1.3 Eine Datei wiederverwendbar machen, ohne ihr Programm zu starten
Eine Funktion lässt sich leichter wiederverwenden, wenn sie in einem Modul mit klarer Aufgabe steht. Ein anderes Programm kann diese Funktion importieren und ein Test kann sie prüfen, ohne dass jemand die gesamte Anwendung ausführen muss.
Die Funktion vom aufrufenden Code trennen
Speichere diesen Code als geometry.py:
"""Calculations for simple shapes."""
import math
def circle_area(radius):
"""Return the area of a circle with the given radius."""
return math.pi * radius ** 2
Speichere im selben Ordner diesen aufrufenden Code als main.py:
import geometry
print(geometry.circle_area(2))
Führe python main.py aus. Python findet geometry, erstellt dessen Funktion und anschließend führt der aufrufende Code diese Funktion aus. Das ausgegebene Ergebnis ist 12.566370614359172. Importiere ohne die Endung .py. Ein gültiger Modulname verwendet Buchstaben, Ziffern und Unterstriche und beginnt mit einem gültigen Zeichen; my_tools.py funktioniert mit import my_tools, während import my-tools ungültige Syntax ist.
Die Zeichenketten am Anfang des Moduls und der Funktion sind Docstrings. Sie beschreiben den Zweck des Codes und erscheinen in help(geometry) und help(geometry.circle_area).
Verstehen, was ein Import ausführt
Beim ersten Import führt Python die Anweisungen des Moduls auf Modulebene der Reihe nach aus. Eine def-Anweisung erstellt eine Funktion; sie führt deren Körper nicht aus. Ein ungeschütztes print(circle_area(2)) am Ende von geometry.py würde dagegen beim Import eine Ausgabe erzeugen.
Dieser Nebeneffekt ist wichtig, wenn ein Modul Eingabeaufforderungen oder eine Programmschleife enthält. Ein Test, der nur circle_area benötigt, sollte nicht das Programm starten und dessen Eingabeaufforderungen beantworten müssen.
Den Start schützen, die Funktion verfügbar halten
Füge dies unterhalb der wiederverwendbaren Funktion in geometry.py ein:
def main():
print(circle_area(2))
if __name__ == "__main__":
main()
Python setzt __name__ für jedes Modul. Bei einer direkten Ausführung mit python geometry.py wird es auf "__main__" gesetzt, sodass die Bedingung wahr ist und main() aufgerufen wird. Beim Import von geometry wird es auf "geometry" gesetzt, sodass Python den Aufruf überspringt. Die Definitionen oberhalb der Schutzbedingung bleiben in beiden Fällen verfügbar.
Setze circle_area selbst nicht innerhalb der Schutzbedingung. Importierender Code braucht diese Definition auch dann, wenn die Startbedingung falsch ist. Die Schutzbedingung steuert, wann die Anwendung startet; sie hindert Python nicht daran, das Modul zu laden.
Wissen, wo Python sucht
Python prüft eingebaute Module wie sys und dann der Reihe nach die Ordner in sys.path. Normalerweise steht der Ordner des laufenden Skripts an erster Stelle dieses Pfads, gefolgt von den Ordnern der Standardbibliothek und der installierten Pakete. Eigene Dateien neben dem ausgeführten Skript aufzubewahren, ist eine einfache Anordnung für ein Anfängerprojekt. Ein Modul außerhalb des Suchpfads kann durch einen gewöhnlichen Import nicht gefunden werden.
Diese Reihenfolge kann auch ein Bibliotheksmodul verdecken. Speicherst du dein Programm als random.py, kann dessen import random deine Datei statt der Bibliothek laden. Ein späteres random.randint(1, 6) löst dann AttributeError aus. Benenne die Datei um, zum Beispiel in dice.py; Python 3.13 und spätere Versionen können der Fehlermeldung einen Hinweis zum Umbenennen hinzufügen.
Ein geladenes Modul wird innerhalb desselben Laufs wiederverwendet. Änderst du in einer interaktiven Shell die Datei und importierst sie erneut, bleibt die geladene Version erhalten. Starte die Shell neu, bevor du deine Änderungen testest.
Wichtige Erkenntnisse
- Importiere deine eigene
.py-Datei über ihren Modulnamen ohne Dateiendung. - Ein Import führt Anweisungen auf Modulebene aus, während Funktionskörper auf Aufrufe warten.
- Halte wiederverwendbare Definitionen außerhalb des
__main__-Schutzes und den Programmstart innerhalb davon. - Eine lokale Datei kann ein gleichnamiges Bibliotheksmodul verdecken.
- Starte eine interaktive Shell neu, um ein bearbeitetes Modul erneut zu testen.
1.4 Pakete für das verwendete Python installieren
Pakete von Drittanbietern ergänzen Code, der nicht mit Python geliefert wird. PyPI, der Python Package Index unter https://pypi.org, beherbergt diese Projekte. pip lädt sie herunter und installiert sie für eine bestimmte Python-Installation.
Erst das Terminal, dann Python verwenden
Ein Terminalbefehl und eine Python-Anweisung gehören an unterschiedliche Stellen. Die Eingabeaufforderung >>> bedeutet, dass du dich in Pythons interaktiver Shell befindest. Gibst du dort pip install requests ein, erhältst du SyntaxError; Python kann es nicht als Anweisung lesen.
Führe in deinem Terminal aus:
python -m pip install cowsay
Schreibe danach Folgendes in ein Python-Programm:
import cowsay
cowsay.cow("Hello, Ada")
Das Programm gibt eine Kuh aus Textzeichen neben einer Sprechblase mit der Nachricht aus. Die Installation macht das Paket verfügbar; der Import erzeugt die Bindung des Programms an das Paket.
pip auf deinen Interpreter abstimmen
Die Form -m pip weist das angegebene Python an, sein eigenes pip auszuführen. Verwende den Befehl, mit dem du deine Programme startest: unter macOS oder Linux häufig python3, unter Windows py.
python3 -m pip install cowsay
python3 game.py
Beide Zeilen wählen denselben Python-Befehl. Meldet eine Installation Erfolg, aber ein Skript löst weiterhin ModuleNotFoundError aus, vergleiche das zur Installation verwendete Python mit dem, das das Skript ausführt. Ein Editor kann einen anderen Interpreter ausgewählt haben als dein Terminal. Verwende das pip des ausgewählten Interpreters, bevor du den Import änderst.
Eine Installation untersuchen und pflegen
| Terminalbefehl | Aufgabe |
|---|---|
python -m pip list | Installierte Pakete und Versionen auflisten |
python -m pip show requests | Details einschließlich des Installationsorts anzeigen |
python -m pip install --upgrade requests | Auf die neueste verfügbare Version aktualisieren |
python -m pip install "requests==2.32.3" | Genau diese Version auswählen |
python -m pip uninstall requests | Das Paket entfernen |
Eine genaue Versionsangabe ist nützlich, wenn du das Verhalten genau dieser Version brauchst. Mit show kannst du prüfen, an welchem Ort pip tatsächlich etwas geändert hat.
Den Namen in der Dokumentation prüfen
Der für pip eingegebene Distributionsname kann sich vom Importnamen unterscheiden. Installiere beautifulsoup4 und verwende dann import bs4; installiere Pillow und verwende dann import PIL. Wiederholt einen Importnamen zu installieren, ist keine sinnvolle Lösung, wenn die Dokumentation eine anders benannte Distribution angibt.
Pythons Standardbibliothek, einschließlich math, random und unittest, wird bereits mit Python geliefert. Diese Module müssen nicht mit pip installiert werden.
Eine verwaltete Installation beachten
Einige Betriebssysteme und Paketverwaltungen schützen ihr Python mit einem externally-managed-environment-Fehler. Das bedeutet, dass eine direkte Installation gesperrt ist, damit die verwaltete Installation konsistent bleibt. Verwende eine virtuelle Umgebung, also einen eigenen Python- und Paketordner für das Projekt. Erzwinge die Installation nicht mit sudo pip install.
Jeder kann auf PyPI veröffentlichen. Prüfe vor der Installation die genaue Schreibweise und lies die Projektseite und die Dokumentation. Ähnlich aussehende Namen können Versuche von Typosquatting sein. Bevorzuge etablierte, gepflegte Pakete und installiere die, die dein Programm tatsächlich braucht.
Wichtige Erkenntnisse
- Führe pip-Befehle in einem Terminal und Importe in Python-Code aus.
- Verwende
python -m pipmit demselben Interpreterbefehl, der das Programm ausführt. - Prüfe den Importnamen in der Dokumentation; er kann vom Installationsnamen abweichen.
- Verwende pip, um ein Paket zu untersuchen, zu aktualisieren, eine Version auszuwählen oder es zu entfernen.
- Ein verwaltetes Python kann eine virtuelle Umgebung für das Projekt erforderlich machen.
- Prüfe Schreibweise und Dokumentation des Pakets, bevor du Code von Drittanbietern installierst.
2Fehler und Tracebacks lesen
Pythons Fehlerausgabe hält sowohl die fehlgeschlagene Operation als auch die Aufrufe fest, die dorthin geführt haben. Wenn du diese Hinweise der Reihe nach liest, findest du einen sinnvollen Ausgangspunkt, statt die erste Zeile zu ändern, die du erkennst. Du verfolgst einen Traceback bis zu seiner Eingabe zurück, unterscheidest häufige Ausnahmetypen und erkennst den weniger auffälligen Fall, in dem ein Programm normal läuft, aber das falsche Ergebnis liefert. Das erwartete Verhalten gibt dir etwas Konkretes, das du prüfen kannst.
2.1 Einen Traceback bis zur fehlerhaften Eingabe zurückverfolgen
Ein Traceback zeigt dir, welche Aufrufe aktiv waren, als eine unbehandelte Ausnahme das Programm stoppte. Er gibt dir sowohl einen Ausgangspunkt für das Lesen als auch einen Weg zurück zu dem Wert, der den Fehler verursacht hat.
Mit der letzten Zeile beginnen
Betrachte dieses vollständige Programm in scores.py:
def average(values):
return sum(values) / len(values)
def report(scores):
avg = average(scores)
print(f"Average: {avg:.1f}")
report([])
Die letzte Zeile seines Fehlerberichts lautet ZeroDivisionError: division by zero. Sie nennt den Ausnahmetyp und beschreibt die fehlgeschlagene Operation. Beginne dort und lies dann nach oben bis zur Codestelle direkt darüber.
Ein vereinfachter Traceback ohne Markierungen der Ausdrücke lautet:
Traceback (most recent call last):
File "scores.py", line 8, in <module>
report([])
File "scores.py", line 5, in report
avg = average(scores)
File "scores.py", line 2, in average
return sum(values) / len(values)
ZeroDivisionError: division by zero
Jeden Aufrufrahmen als aktiven Aufruf lesen
Ein Aufrufrahmen nennt Datei, Zeilennummer und Funktion, gefolgt vom relevanten Code. in <module> bezeichnet Code auf Modulebene. Der älteste Aufruf steht oben, der neueste unten. Liest du von unten, kehrst du diese Reihenfolge um: So kannst du zuerst fragen, was fehlgeschlagen ist, und danach, wie die Ausführung dorthin gelangt ist.
- Zeile 2 dividiert durch
len(values). Damit dieser Divisor null ist, mussvaluesleer sein. - Zeile 5 ruft
average(scores)auf, untersuche also die vonreportübergebenen Wertescores. - Zeile 8 übergibt
[], die leere Liste. Das erklärt, wie der Divisor null in die Berechnung gelangt ist.
Die fehlerauslösende Zeile und die Ursache hängen zusammen, können aber an unterschiedlichen Stellen liegen. Die Division zu ersetzen, ohne zu entscheiden, was eine leere Liste bedeuten soll, würde die Frage nach dem gewünschten Verhalten verdecken. Entweder muss der aufrufende Code eine leere Liste vermeiden oder das Verhalten der Funktion bei leerer Eingabe muss festgelegt werden.
Hervorhebungen nutzen, ohne von ihrer genauen Form abzuhängen
Python 3.11 und spätere Versionen können die Markierungen ^ und ~ ergänzen, um den fehlerauslösenden Teil eines Ausdrucks zu zeigen. Sie helfen, wenn eine Zeile mehrere Operationen enthält. Die genaue Hervorhebung variiert zwischen Versionen, doch die Aufrufstellen und die Ausnahmemeldung geben weiterhin die Lesereihenfolge vor.
Ein Traceback kann Aufrufrahmen aus der Standardbibliothek oder einem installierten Paket enthalten. Finde den untersten Aufrufrahmen in deinem eigenen Code und untersuche die dort übergebenen Argumente. Ein Aufrufrahmen einer Bibliothek gibt einen Hinweis auf die Stelle; er belegt nicht, dass die Bibliothek den Programmfehler enthält.
Eine Syntaxfehlermeldung erkennen
Ein Syntaxfehler im gestarteten Skript verhindert dessen Ausführung. Python meldet eine Datei, eine Zeile, eine Markierung und eine Meldung ohne die übliche Traceback-Kopfzeile. Zum Beispiel fehlt bei if True der Doppelpunkt; bei total = sum([1, 2, 3] bleibt eine Klammer offen.
Die markierte Stelle zeigt, wo Python bemerkt hat, dass es den Code nicht analysieren kann. Sieht diese Zeile gültig aus, prüfe die vorhergehende Zeile auf ein nicht geschlossenes Anführungszeichen oder eine offene Klammer. Wenn das gestartete Skript nicht ausgeführt wurde, gibt es keine Kette von Laufzeitaufrufen, die du verfolgen könntest.
Ein gewöhnlicher Programmlauf stoppt bei seiner ersten unbehandelten Ausnahme. Behebe dieses Problem und führe das Programm erneut aus, um später auftretende Probleme sichtbar zu machen.
Wichtige Erkenntnisse
- Lies zuerst Ausnahmetyp und Meldung, dann den fehlerauslösenden Aufrufrahmen und seine Aufrufer.
- Aufrufrahmen reichen vom ältesten aktiven Aufruf oben bis zum neuesten unten.
- Verfolge den falschen Wert zurück; seine Quelle kann vor dem Absturz liegen.
- Ein Syntaxfehler im gestarteten Skript wird vor dessen Ausführung gemeldet.
2.2 Falsche Namen, Datentypen, Werte und Ergebnisse unterscheiden
Der Name eines Fehlers grenzt die Suche ein, und seine Meldung liefert Details zu den beteiligten Werten. Ein Programm kann auch falsch sein, ohne eine Ausnahme auszulösen. Deshalb gehört es weiterhin zur Fehlersuche, die erwartete Ausgabe zu überprüfen.
Drei Arten von Problemen unterscheiden
| Art | Was geschieht | Beispiel |
|---|---|---|
| Syntaxfehler | Python kann den Code nicht analysieren | Ein fehlender Doppelpunkt nach if |
| Laufzeitfehler | Die Ausführung erreicht eine ungültige Operation | Division durch null |
| Logikfehler | Die Ausführung endet mit falschem Verhalten | Eine Schleife überspringt einen Preis |
IndentationError ist eine Art von SyntaxError. Ein Funktionskörper muss eingerückt sein; ein fehlender eingerückter Block stoppt den Code vor der Ausführung. Prüfe auch auf nicht geschlossene Anführungszeichen oder Klammern und falsch geschriebene Schlüsselwörter.
Namen und Datentypen genau lesen
Ein NameError bedeutet, dass das Programm einen Namen an dieser Stelle nicht kennt. In prnt("hi") ist die eingebaute Funktion print gemeint. Ein Name kann auch vor der Zuweisung verwendet worden sein oder ein Import kann fehlen.
Ein TypeError bedeutet, dass eine Operation einen ungeeigneten Datentyp oder ein Aufruf die falsche Anzahl von Argumenten erhalten hat. "Age: " + 30 mischt eine Zeichenkette und eine ganze Zahl. Schreibe "Age: " + str(30) oder f"Age: {30}". Benötigt area die Argumente w und h, ist auch area(3) ein TypeError, diesmal wegen eines fehlenden Arguments.
Die Meldung unterscheidet diese Ursachen. In can only concatenate str (not "int") to str nennt Python die Datentypen, die aufeinandergetroffen sind. Nach age = input("Age: ") ist age Text. age + 1 schlägt fehl; bei einer ganzzahligen Eingabe führt int(age) + 1 eine Rechnung aus.
Ein ValueError bedeutet, dass der Datentyp der Eingabe akzeptiert wird, ihr Wert aber ungeeignet ist. int("12.5") erhält eine Zeichenkette, also einen akzeptierten Eingabetyp, aber der Text stellt keine ganze Zahl in der erforderlichen Form dar. math.sqrt(-1) enthält einen weiteren ungeeigneten Wert für eine Berechnung mit reellen Zahlen.
Positionen, Schlüssel und Attribute prüfen
| Ausnahme | Beispiel | Ursache |
|---|---|---|
IndexError | [4, 6, 10][3] | Der letzte gültige Index ist 2 |
KeyError | {"apples": 3}["pears"] | Dem Wörterbuch fehlt "pears" |
ZeroDivisionError | 10 / 0 | Der Divisor ist null |
AttributeError | math.sqroot(16) | Dem Modul fehlt sqroot |
ModuleNotFoundError | import mathh | Unter diesem Namen wird kein Modul gefunden |
ImportError | from math import square_root | Dem Modul fehlt dieser importierte Name |
Divisoren mit dem Wert null führen auch bei // und % zu Fehlern. Eine fehlende Listenposition und ein fehlender Wörterbuchschlüssel betreffen unterschiedliche Zugriffe, auch wenn in beiden Fällen etwas Fehlendes angefordert wird.
Ein plausibel aussehendes Ergebnis prüfen
def total_price(prices):
total = 0
for i in range(1, len(prices)):
total += prices[i]
return total
Für [4, 6, 10] gibt dies 16 statt 20 zurück. Es gibt keine Ausnahme: Die Indizes 1 und 2 existieren, also ist jede Operation zulässig. Die Schleife beginnt zu spät und überspringt Index 0. Der Fehler wird sichtbar, wenn du die Ausgabe mit der beabsichtigten Summe vergleichst.
Schlage bei einer unbekannten Ausnahme ihren Typ und die allgemeine Meldung in der offiziellen Python-Dokumentation nach. Entferne deine eigenen Pfade und Eingabewerte aus einer Suchanfrage; sie erklären selten die allgemeine Regel.
Wichtige Erkenntnisse
- Syntaxfehler verhindern die Analyse des Codes; Laufzeitfehler treten während der Ausführung auf; Logikfehler führen zu falschem Verhalten.
NameErrorbetrifft eine Bindung;TypeErrorbetrifft einen Datentyp oder Aufrufargumente.ValueErrorbetrifft einen ungeeigneten Wert eines akzeptierten Datentyps.IndexError,KeyErrorundAttributeErrorbeziehen sich auf unterschiedliche Arten des Zugriffs.- Der Vergleich der Ausgabe mit einem erwarteten Ergebnis kann einen Logikfehler ohne Traceback aufdecken.
3Die Ursache eines Programmfehlers finden
Ein falsches Ergebnis lässt sich leichter untersuchen, wenn du es reproduzieren und genau sagen kannst, wie es sich vom erwarteten Ergebnis unterscheidet. Du machst aus dieser Beobachtung eine überprüfbare Vermutung, grenzt die Suche auf ein kleines Stück Code ein und untersuchst die entscheidenden Werte. Ausgaben, Ablauftabellen und die Bedienelemente eines Debuggers bieten verschiedene Möglichkeiten, die Ausführung zu beobachten. Nach einer gezielten Korrektur hilft die Prüfung zuvor funktionierender Fälle zu erkennen, welches Verhalten die Änderung beeinflusst hat.
3.1 Eine Ursache prüfen, bevor du den Code änderst
Ein Programmfehler ist ein Unterschied zwischen beabsichtigtem und tatsächlichem Verhalten. Bei der Fehlersuche und Fehlerbehebung findest und behebst du die Ursache. Eine hilfreiche Fehlersuche macht aus einer Beobachtung eine überprüfbare Erklärung und ändert den Code anschließend aufgrund von Belegen.
Den Fehler wiederholt auslösen
Halte eine konkrete Eingabe und das Ergebnis fest, das sie erzeugt. Verwende für die Preisfunktion, die ihre Schleife bei Index 1 beginnt, [4, 6, 10]. Die erwartete Summe ist 20; die tatsächliche Summe ist 16. Anhand dieser konkreten Werte kannst du prüfen, ob sich jede Untersuchung noch auf denselben Fehler bezieht.
Die Differenz beträgt 4 und das tatsächliche Ergebnis ist gleich 6 + 10. Eine konkrete Hypothese lautet, dass der erste Preis übersprungen wird. Diese Hypothese sagt etwas voraus, das du überprüfen kannst: Die Schleife wird Index 0 nie besuchen.
Sechs Schritte befolgen
- Reproduzieren: Finde eine Eingabe, die den Fehler zuverlässig zeigt.
- Erwartetes und tatsächliches Ergebnis nennen: Berechne das erwartete Ergebnis unabhängig.
- Eine Hypothese aufstellen: Nenne eine mögliche Ursache und das daraus erwartete Verhalten.
- Sie prüfen: Untersuche Werte mit Ausgaben, einer Ablauftabelle oder einem Debugger.
- Die Ursache beheben: Nimm eine gezielte Änderung vor.
- Erneut testen: Führe die fehlschlagende Eingabe und zuvor funktionierende Fälle aus.
Gib den Index innerhalb der Schleife aus. Für diese Eingabe sind die einzigen Indizes 1 und 2, was die Vorhersage bestätigt. Wenn range bei 0 beginnt, wird das übersprungene Element berücksichtigt. Durchläufst du die Preise direkt, musst du keinen Index verwalten:
def total_price(prices):
total = 0
for price in prices:
total += price
return total
Die korrigierte Funktion gibt 20 für [4, 6, 10], 5 für [5] und 0 für [] zurück. Der Fall mit einem Element ist nützlich, weil die fehlerhafte Schleife dafür zuvor 0 zurückgab.
Zu jedem Ergebnis 4 zu addieren würde die beobachtete Summe korrigieren, aber die Ursache bestehen lassen. Für [5] würde diese Behelfslösung 4 zurückgeben; der fehlende Betrag hängt von der Eingabe ab. Prüfe, ob eine vorgeschlagene Korrektur alle Belege erklärt, bevor du sie beibehältst.
Eine größere Suche eingrenzen
Bei Teile und herrsche prüfst du an einer Stelle während der Ausführung die Werte, von denen das falsche Ergebnis abhängt. Ist ein benötigter Wert bereits falsch, untersuche den früheren Code, der ihn erzeugt hat. Sind die benötigten Werte noch richtig, verfolge ihre spätere Verarbeitung. Wiederhole das innerhalb des verbleibenden Abschnitts.
Du kannst auch eine Funktion einzeln aufrufen oder vorübergehend nicht zugehörigen Code entfernen. Ein minimales reproduzierbares Beispiel enthält den kürzesten Code und die kleinste Eingabe, die noch fehlschlagen. Trat ein Problem zuerst bei Hunderten von Datensätzen auf, erleichtert es jede Untersuchung, einen oder zwei auslösende Datensätze zu finden.
Prüfen, was die Daten tatsächlich sind
Eine angezeigte 5 kann die Zeichenkette "5" sein. "5" + "3" ergibt "53", während die Addition von Zahlen 8 ergibt. Untersuche type(value), um den Datentyp zu prüfen. Verwende repr(value), um Leerzeichen und Zeilenumbruchzeichen sichtbar zu machen, die eine gewöhnliche Ausgabe verbergen kann, wie in ' Ada'.
Erkläre den Code laut, Zeile für Zeile, einschließlich der Frage, was jede Schleife tatsächlich durchläuft und was jede Funktion zurückgibt. Das ist Fehlersuche mit einer Gummiente: Einen Schritt zu erklären, deckt oft die Lücke zwischen der beabsichtigten und der geschriebenen Operation auf.
Ändere jeweils nur eine Ursache, damit du das Ergebnis mit der Änderung in Verbindung bringen kannst. Teste sowohl die zuvor funktionierenden Fälle als auch den fehlgeschlagenen Fall erneut.
Wichtige Erkenntnisse
- Reproduziere den Programmfehler und halte erwartetes und tatsächliches Ergebnis fest, bevor du etwas änderst.
- Eine hilfreiche Hypothese sagt etwas voraus, das du überprüfen kannst.
- Behebe die Ursache mit einer Änderung und teste dann fehlgeschlagene und zuvor funktionierende Fälle erneut.
- Verwende ein minimales Beispiel oder Zwischenwerte, um die Suche einzugrenzen.
- Prüfe Datentypen und verborgene Zeichen, wenn Werte irreführend aussehen.
3.2 Wertänderungen mit Ausgaben und einem Debugger beobachten
Die endgültige Antwort eines Programms verbirgt den Weg, auf dem sie entstanden ist. Vorübergehende Ausgaben, eine Ablauftabelle und ein Debugger lassen dich diesen Weg sehen und eine konkrete Hypothese dazu prüfen, wo etwas schiefgeht.
Einen Wert zu einem bekannten Zeitpunkt ausgeben
Beschrifte diagnostische Ausgaben, damit die Bedeutung einer Zahl klar ist. Python 3.8 und spätere Versionen unterstützen diese f-String-Form:
print(f"{i=} {total=}")
Setze sie bei der fehlerhaften Preisschleife unmittelbar vor total += prices[i]. Mit den Preisen [4, 6, 10] gibt sie aus:
i=1 total=0
i=2 total=6
Dies sind die Summen vor jeder Addition. Nach der ersten Ausgabe addiert die Schleife 6; nach der zweiten addiert sie 10. Eine Ausgabe nach der Addition würde andere Summen zeigen. Entscheide vor der Deutung der Ausgabe, welcher Zeitpunkt deine Frage beantwortet.
Denselben Code von Hand nachvollziehen
Eine Ablauftabelle hält Variablen Schritt für Schritt fest. Für diese Schleife:
Index i | Addierter Preis | Summe vorher | Summe nachher |
|---|---|---|---|
| 1 | 6 | 0 | 6 |
| 2 | 10 | 6 | 16 |
Es gibt keinen Schleifendurchlauf für Index 0. Folge den tatsächlich im Code angegebenen Start- und Endwerten, auch wenn du bereits weißt, was die Schleife tun sollte. Eine Tabelle hilft, deine Erwartung vom tatsächlichen Geschehen zu unterscheiden.
An einem Haltepunkt pausieren
Ein Debugger hält ein laufendes Programm an, damit du seinen aktuellen Zustand untersuchen kannst. Ein Haltepunkt markiert eine Stelle, an der pausiert werden soll. Klicke in Editoren wie Thonny und VS Code neben eine Zeilennummer und starte den Debug-Modus; klicke in IDLE mit der rechten Maustaste auf die Zeile und wähle Set Breakpoint.
Wähle nach dem Anhalten einen Schritt passend zu der Frage, die du untersuchst:
| Befehl | Wirkung |
|---|---|
| Prozedurschritt | Führt die aktuelle Zeile einschließlich einer aufgerufenen Funktion aus |
| Einzelschritt | Steigt in eine Funktion ein, die in dieser Zeile aufgerufen wird |
| Funktion verlassen | Führt diese Funktion zu Ende und pausiert nach der Rückkehr |
| Fortsetzen | Führt das Programm bis zu einem weiteren Haltepunkt oder bis zum Ende aus |
Übergibt der aufrufende Code korrekte Argumente, erhält aber eine falsche Antwort, steige in die Funktion ein. Musst du nur das zurückgegebene Ergebnis sehen, führe den Aufruf als Prozedurschritt aus. Untersuche die Werte vor und nach der Zeile, die den Fehler erzeugen könnte.
Pythons textbasierten Debugger verwenden
Füge breakpoint() unmittelbar vor dem Code ein, den du untersuchen möchtest. Bei der Standardkonfiguration des Debuggers startet die Ausführung des Programms an dieser Stelle pdb. Gib diese Befehle an seiner Eingabeaufforderung (Pdb) ein:
p totalzeigttotalan.nführt die nächste Zeile als Prozedurschritt aus.ssteigt in einen Funktionsaufruf ein.csetzt die Ausführung fort.qbeendet die Fehlersuche.
Das sind Debugger-Befehle, keine Zeilen zum Kopieren in dein Programm.
Ausgegebene Werte von Rückgabewerten unterscheiden
Eine Funktion kann die richtige Zahl ausgeben und dennoch None zurückgeben, weil ihr return total fehlt. Untersuche den Wert, den der aufrufende Code erhält, wenn der Fehler nach dem Funktionsaufruf auftritt. Eine korrekte Ausgabe innerhalb der Funktion belegt nicht, dass der aufrufende Code diese Zahl erhalten hat.
Verwende Ausgaben für schnelle Hinweise über mehrere Schleifendurchläufe hinweg und den Debugger, um einen bestimmten Zeitpunkt zu untersuchen. Entferne nach der Fehlerbehebung vorübergehende Ausgaben und breakpoint()-Aufrufe, damit das fertige Programm normal läuft.
Wichtige Erkenntnisse
- Beschrifte Ausgaben zur Fehlersuche und beachte, ob sie vor oder nach einer Aktualisierung erfolgen.
- Eine Ablauftabelle verfolgt tatsächliche Variablenänderungen Schritt für Schritt.
- Haltepunkte pausieren die Ausführung; Schrittbefehle steuern, wie viel Code als Nächstes ausgeführt wird.
breakpoint()öffnet standardmäßig pdb;p,n,s,cundqsind Debugger-Befehle.- Untersuche Rückgabewerte, wenn eine Funktion korrekt ausgibt, aber der aufrufende Code fehlschlägt.
- Entferne vorübergehende diagnostische Ausgaben und Haltepunkte nach der Korrektur.
4Deine Funktionen mit Tests prüfen
Mit automatisierten Prüfungen kannst du ein nützliches Beispiel jedes Mal wiederholen, wenn sich eine Funktion ändert. Du schreibst Zusicherungen mit klaren Erwartungen, wählst Eingaben an den Grenzen einer Regel und verhinderst, dass Tests die Anwendung starten, die sie importieren. Das Standardframework unittest gibt dann jedem Test einen Namen und ein eigenes Ergebnis. Das Lesen von fehlgeschlagenen Tests und Testfehlern verbindet das Testen mit der Fehlersuche: Der Test reproduziert ein Problem und die Untersuchung findet seine Ursache.
4.1 Prüfungen schreiben, die bei falscher Annahme fehlschlagen
Eine Zusicherung hält etwas fest, das wahr sein muss, wenn dein Code korrekt funktioniert. Sie kann einen Programmfehler nahe an seiner Entstehungsstelle stoppen oder das Ergebnis einer Funktion mit dem erwarteten Wert vergleichen.
Eine Bedingung und eine optionale Meldung schreiben
assert letter_grade(90) == "A", "90 should be an A"
Python wertet die Bedingung aus. Gibt letter_grade(90) den Wert "A" zurück, ist die Bedingung wahr und die Ausführung läuft ohne Ausgabe weiter. Gibt die Funktion "B" zurück, löst Python AssertionError mit der Meldung 90 should be an A aus. Der Traceback zeigt auf die fehlgeschlagene Prüfung.
Auch ohne die Meldung prüft assert letter_grade(90) == "A" das Ergebnis. Die letzte Ausnahmezeile enthält AssertionError, während der Traceback den Ausdruck zeigt. Eine hilfreiche Meldung nennt die Erwartung, die nicht erfüllt wurde; sie sollte dir helfen, die geprüfte Regel zu finden.
Einen internen Fehler früh stoppen
Angenommen, eine interne Berechnung benötigt eine nicht leere Liste:
def average(values):
assert len(values) > 0, "values must not be empty"
return sum(values) / len(values)
Eine leere Liste löst nun vor der Division die Zusicherung aus. Die Prüfung dokumentiert die interne Annahme der Funktion und zeigt direkt auf deren Verletzung. Sie gibt einer leeren Liste weder ein arithmetisches Mittel noch entscheidet sie, wie eine Anwendung auf fehlende Benutzerdaten reagieren soll.
Die Eingabevalidierung aktiv halten
Pythons Option -O entfernt assert-Anweisungen. Ein Programm, das sich beim Zurückweisen externer Eingaben auf eine Zusicherung verlässt, kann sich daher bei optimierter Ausführung anders verhalten. Prüfe Benutzereingaben und andere externe Daten mit gewöhnlicher Ablaufsteuerung:
def require_score(score):
if score < 0 or score > 100:
raise ValueError("score must be 0 to 100")
return score
Hier meldet raise ValueError(...) einen ungeeigneten Wert. Die if-Anweisung wird auch mit -O ausgeführt. Wähle eine Zusicherung für eine interne Annahme über das Programm und eine aktive Eingabeprüfung für Daten, die von außen fehlerhaft eingegeben werden können.
Eine Prüfung vermeiden, die nie fehlschlagen kann
assert ist eine Anweisung, keine Funktion. Schreibe die Bedingung und die Meldung durch ein Komma getrennt:
assert x > 10, "x too small"
assert (x > 10, "x too small") erzeugt ein Tupel mit zwei Elementen. Ein nicht leeres Tupel ist auch dann wahr, wenn sein erstes Element False ist, sodass die Zusicherung besteht. Python kann davor warnen, dass diese Zusicherung immer wahr ist. Klammern um die Bedingung allein, wie in assert (x > 10), erzeugen dieses Tupel nicht; das Komma innerhalb der Klammern ist das Problem.
Verwende == für den Vergleich. assert total = 20 ist ungültige Syntax, weil = einen Wert zuweist.
Zahlen mit der beabsichtigten Genauigkeit vergleichen
Verwende für eine berechnete Gleitkommazahl eine Prüfung auf annähernde Gleichheit:
import math
assert math.isclose(0.1 + 0.2, 0.3)
Ein exakter Vergleich schlägt hier fehl, weil sich die gespeicherte Gleitkommasumme geringfügig von 0.3 unterscheidet. Ein einfacher automatisierter Test wendet dieselbe Idee einer Zusicherung auf eine Funktion an: assert total_price([4, 6, 10]) == 20. Der erwartete Wert ergibt sich aus der Regel der Funktion, sodass die Prüfung eine falsche Implementierung aufdecken kann.
Wichtige Erkenntnisse
assert condition, messagelöstAssertionErroraus, wenn die Bedingung falsch ist.- Zusicherungen können interne Annahmen und erwartete Funktionsergebnisse prüfen.
- Verwende für externe Eingaben
ifund eine Fehlermeldung oder Ausnahme;-Oentfernt Zusicherungen. - Ein Komma innerhalb von
assert (condition, message)erzeugt ein als wahr ausgewertetes Tupel. - Verwende
==für Gleichheit undmath.isclosefür den Vergleich berechneter Gleitkommazahlen.
4.2 Tests auswählen, die einen Programmfehler aufdecken können
Ein Unit-Test prüft eine kleine Codeeinheit, meist eine Funktion, mit einer gewählten Eingabe und einem erwarteten Ergebnis. Automatisierte Tests machen es leicht, dieselben Prüfungen nach Änderungen zu wiederholen. So helfen sie, erneut auftretende Fehler in zuvor funktionierendem Verhalten zu erkennen.
Mit der Spezifikation beginnen
Angenommen, eine Bewertungsfunktion muss für Punktzahlen ab 90 "A", für Punktzahlen ab 80 bis unter 90 "B" und unter 80 "C" zurückgeben. Die fehlerhafte erste Bedingung lautet score > 90.
Tests sollten mehrere Aufgaben erfüllen:
- Typische Fälle prüfen gewöhnliche Eingaben innerhalb jedes Bereichs, etwa 95 und 85.
- Grenzwertfälle prüfen die Werte, an denen eine Regel wechselt, etwa 90 und 89 oder 80 und 79.
- Randfälle prüfen Eingaben wie null oder eine leere Sammlung, wenn die Regeln der Funktion diese zulassen.
Ermittle die erwartete Antwort anhand dieser Regeln. Übernimmst du die aktuelle Antwort des Programms in einen Test, kann dessen Fehler erhalten bleiben.
Die Tests neben das Modul legen
Speichere die Tests in test_grade.py und die Funktion in grade.py. Der Anwendungsstart in grade.py gehört unter den __main__-Schutz, damit der Import der Funktion das Programm nicht startet.
from grade import letter_grade
def test_letter_grade():
assert letter_grade(95) == "A"
assert letter_grade(90) == "A"
assert letter_grade(85) == "B"
Bei der fehlerhaften Regel besteht die erste Zusicherung. Die zweite schlägt fehl, weil 90 nicht größer als 90 ist; die Ausführung stoppt dort, vor der dritten Zusicherung in dieser Funktion. Die Änderung der ersten Bedingung zu score >= 90 korrigiert diesen Grenzfall. Führe alle Tests erneut aus, um den betroffenen Fall und das frühere Verhalten zu prüfen.
Sicherstellen, dass die Tests tatsächlich laufen
Eine Testfunktion zu definieren, ruft sie nicht auf. Wenn du kein Testwerkzeug zum Ausführen verwendest, ergänze test_letter_grade() am Ende der Testdatei und führe dann python test_grade.py aus. Um pytest zu verwenden, installiere es mit python -m pip install pytest und führe dann pytest test_grade.py im Terminal aus. Es findet die Testfunktionen und meldet ihre Ergebnisse. Verzichte auf manuelle Aufrufe, wenn ein Testwerkzeug die Ausführung übernimmt.
Ein Test, der lediglich letter_grade(90) aufruft, enthält keinen Vergleich. Er kann erfolgreich enden, obwohl die zurückgegebene Antwort falsch ist; er schlägt nur fehl, wenn der Aufruf eine unerwartete Ausnahme auslöst. Eine Zusicherung verbindet den Aufruf mit dem geforderten Verhalten.
Rückgabewerte und Eigenschaften zufälliger Ergebnisse testen
Eine Funktion, die eine Summe nur ausgibt, gibt None zurück. Ein Vergleich wie assert print_total([4, 6]) == 10 schlägt daher auch dann fehl, wenn auf dem Bildschirm 10 erscheint. Lass die Berechnung ihr Ergebnis zurückgeben und überlasse main die Ausgabe.
Zufällige Ergebnisse brauchen eine andere Erwartung. Von einer Würfelfunktion ohne festgelegten Startwert kann nicht verlangt werden, bei jedem Aufruf genau dieselbe Augenzahl zurückzugeben. Prüfe, ob jeder von 100 Aufrufen einen zulässigen Wert von 1 bis einschließlich 6 zurückgibt, oder lege vor einer reproduzierbaren Folge einen Startwert fest. Besteht der Test der Schranken, sind die Schranken für diese Aufrufe geprüft; er belegt nicht, dass jede Augenzahl gleich wahrscheinlich ist.
Wenn ein neuer Programmfehler auftritt, ergänze vor der Korrektur der Funktion einen Test, der wegen dieses Fehlers fehlschlägt. Behalte diesen Regressionstest, damit spätere Änderungen dasselbe Verhalten erneut prüfen. Bestandene Tests zeigen, was bei den getesteten Fällen geschah; weitere Eingaben können dennoch einen anderen Fehler aufdecken.
Wichtige Erkenntnisse
- Leite erwartete Ergebnisse unabhängig von der Implementierung aus der Spezifikation ab.
- Teste gewöhnliche Eingaben, Regelgrenzen und relevante Randfälle.
- Testfunktionen brauchen einen Aufruf oder ein Testwerkzeug zur Ausführung; Definitionen allein führen keine Prüfungen durch.
- Zusicherungen müssen Rückgabewerte mit dem beabsichtigten Verhalten vergleichen.
- Teste geeignete Eigenschaften zufälliger Ausgaben oder richte eine reproduzierbare Folge ein.
- Behalte für jeden neu gefundenen Programmfehler einen Test, um Regressionen zu erkennen.
4.3 Eine unittest-Testdatei ausführen und auswerten
Python enthält unittest, ein Testframework, das Tests findet, ausführt und einzelne Ergebnisse meldet. Es muss nicht mit pip installiert werden. Seine Klassenstruktur kannst du als Vorlage verwenden, während du das Testen von Funktionen lernst.
Unabhängige Testmethoden schreiben
Schreibe für eine Funktion in grade.py Folgendes in test_grade.py:
import unittest
from grade import letter_grade
class TestLetterGrade(unittest.TestCase):
def test_top(self):
self.assertEqual(letter_grade(95), "A")
def test_boundary(self):
self.assertEqual(letter_grade(90), "A")
def test_low(self):
self.assertEqual(letter_grade(50), "C")
if __name__ == "__main__":
unittest.main()
TestLetterGrade erbt die Testwerkzeuge von unittest.TestCase. Jeder Test ist eine Methode, deren Name mit test beginnt. self bezeichnet das aktuelle Testobjekt; belasse es in der Parameterliste jeder Methode und vor Aufrufen von Zusicherungsmethoden. Du kannst diese Struktur kopieren, ohne eigene Klassen zu entwerfen.
Tests laufen standardmäßig in alphabetischer Reihenfolge ihrer Methodennamen, also kommt test_boundary vor test_low und test_top. Sorge dafür, dass jeder Test seine benötigte Eingabe selbst bereitstellt und unabhängig von vorherigen Tests funktioniert.
Eine Zusicherungsmethode auswählen
| Methode | Prüfung |
|---|---|
self.assertEqual(actual, expected) | Werte sind gleich |
self.assertNotEqual(a, b) | Werte unterscheiden sich |
self.assertTrue(condition) | Eine Bedingung ist wahr |
self.assertFalse(condition) | Eine Bedingung ist falsch |
self.assertIn(item, collection) | Das Element gehört zur Sammlung |
self.assertAlmostEqual(a, b) | Die Differenz ergibt standardmäßig auf 7 Nachkommastellen gerundet null |
Bevorzuge beim Vergleich von Werten assertEqual(a, b) gegenüber assertTrue(a == b): Der Bericht über einen Fehlschlag zeigt die beiden Werte. Die unittest-Methoden behalten ihre Prüfungen auch bei, wenn Python mit -O läuft.
Um eine erwartete Ausnahme zu prüfen, setze den Aufruf in assertRaises:
def test_score_too_high(self):
with self.assertRaises(ValueError):
letter_grade(120)
Dieser Test setzt voraus, dass die Regel der Funktion diese Punktzahl mit ValueError zurückweist. Eine Rückgabe ohne Ausnahme lässt die Prüfung fehlschlagen; eine andere Ausnahme als ein Zusicherungsfehler ist ein Testfehler.
Den Bericht ausführen und lesen
Führe python test_grade.py aus, um das geschützte unittest.main() der Datei auszuführen. Oder führe python -m unittest aus, um test*.py-Dateien im aktuellen Ordner zu finden. python -m unittest -v gibt einzelne Testnamen und ihre Ergebnisse aus.
Im Kurzbericht bedeutet . bestanden, F bedeutet, dass eine Zusicherung fehlgeschlagen ist, und E bedeutet, dass eine andere Ausnahme aufgetreten ist. Bei einer Bewertungsfunktion mit score > 90 erzeugen die drei obigen Tests F... Der Bericht zum fehlgeschlagenen Test test_boundary zeigt 'B' != 'A': Das tatsächliche Ergebnis ist "B", während das erwartete Ergebnis "A" ist. Die Zusammenfassung meldet drei Tests und einen fehlgeschlagenen Test.
Prüfe vor einer Änderung der Funktion die Erwartung des Tests anhand der Bewertungsregeln. Eine falsche Erwartung erfordert eine Korrektur des Tests. Laufen keine Tests, prüfe die Methodennamen, den Schutz für die direkte Ausführung und den Startbefehl. Vermeide außerdem, deine Datei unittest.py zu nennen, da sie das Standardmodul verdeckt.
Wichtige Erkenntnisse
- Setze
test-Methoden in eine Klasse, die vonunittest.TestCaseerbt. - Verwende
self.assertEqualund andere Zusicherungsmethoden für klare Prüfungen und Berichte. assertRaisesprüft eine erwartete Ausnahme; für Vergleiche von Gleitkommazahlen kannst duassertAlmostEqualverwenden.- Führe eine geschützte Testdatei direkt aus oder verwende
python -m unittestzur automatischen Testerkennung. - Lies
Fals fehlgeschlagene Zusicherung undEals andere Ausnahme. - Prüfe einen fehlgeschlagenen Test anhand der Spezifikation, bevor du die Funktion änderst.
- Jeder Test muss unabhängig von der alphabetischen Ausführungsreihenfolge laufen.
Python-Module, Fehlersuche und Tests: Beispiele
1Ein Programm aus wiederverwendbaren Teilen erstellen
20 Schritte
- 1EinführungMit wiederverwendbarem Code beginnen
Ein Programm kann Code aus Pythons Standardbibliothek, einer anderen selbst geschriebenen Datei oder einem installierten Paket aufrufen. Die erste Frage ist, welcher Name dem Programm nach dem Import zur Verfügung steht.
Du verfolgst eine Berechnung vom Import bis zum Rückgabewert, richtest dann ein wiederverwendbares Modul ein und verbindest eine Paketinstallation mit ihrem Interpreter. Prüfe dabei, ob ein Ergebnis ein Wert ist, den du behältst, oder eine Änderung an einer vorhandenen Liste.
- 2ErklärungDer Import bestimmt die Art des Aufrufs
Diese Importe stellen dieselbe Funktion über unterschiedliche Bindungen bereit:
import math print(math.sqrt(16))from math import sqrt print(sqrt(16))Beide geben
4.0aus. Der erste erzeugtmath, daher verwendet der Aufruf einen Punkt. Der zweite erzeugtsqrt, daher braucht der Aufruf keinen vorangestellten Modulnamen. Eine falsche Aufrufart löstNameErroraus, wenn der betreffende Name nicht gebunden wurde. - 3BeispielDie Hypotenuse berechnen
Mit
import mathkann ein rechtwinkliges Dreieck mit den Seitenlängen 3 und 4 so berechnet werden:length = math.sqrt(3**2 + 4**2)Die Quadrate sind 9 und 16. Ihre Summe ist 25 und
math.sqrt(25)gibt5.0zurück. Python wertet das Argument aus, bevor es die Funktion aufruft.Der vorangestellte Modulname zeigt Lesenden auch, woher
sqrtstammt. Ein Wildcard-Import verbirgt diese Herkunft und kann vorhandene Namen ersetzen, etwa eine Variable namense. - 4HerausforderungDie Aliasbindung nachvollziehen
Ein neues Skript führt diese Zeilen aus. Was geschieht in der letzten Zeile und wie lässt sich der Fehler beheben?
import statistics as st print(statistics.mean([4, 8, 12]))---reveal---
Es löst
NameErroraus, weil der Importstbindet, nichtstatistics. Verwendest.mean([4, 8, 12]); der Aufruf gibt8zurück. Ein Alias wählt die Bindung in diesem Programm aus; er erzeugt nicht zusätzlich den ursprünglichen Modulnamen. - 5BeispielFür genügend ganze Behälter runden
Eine Wand von 23 Quadratmetern soll gestrichen werden. Jede Dose reicht für 10 Quadratmeter.
import math cans = math.ceil(23 / 10)Die Division ergibt
2.3;ceilergibt3, also genügend ganze Dosen.round(2.3)ergibt nur2.Bei negativen Eingaben bedeutet abwärts weiterhin in Richtung minus unendlich:
math.floor(-2.5)ist-3, währendint(-2.5)in Richtung null abschneidet und-2ergibt. - 6KernaussageDen Vergleich an die Zahl anpassen
Berechnete Gleitkommazahlen können geringfügig von den dezimal dargestellten Zielwerten abweichen:
0.1 + 0.2 == 0.3 # False math.isclose(0.1 + 0.2, 0.3) # TrueVerwende für diesen näherungsweisen Vergleich eine Prüfung auf annähernde Gleichheit. Unabhängig davon sind die zulässigen Eingaben einer Funktion weiterhin wichtig:
math.sqrt(-1)löstValueErroraus.Das eingebaute
roundbraucht keinen Import. Bei einem Wert genau in der Mitte wählt es die benachbarte gerade Zahl:round(2.5)ist2undround(3.5)ist4. - 7ErklärungDas benötigte Zufallsergebnis auswählen
Wähle nach
import randomeinen Aufruf nach seiner Aufgabe:randint(1, 6)wählt eine ganze Zahl einschließlich 6.randrange(1, 6)wählt eine ganze Zahl unter Ausschluss von 6.random()liefert eine Gleitkommazahl ab 0,0 bis ausschließlich 1,0.choice(items)wählt ein Element.sample(items, k)wähltkPositionen ohne Zurücklegen.
Zwei ausgewählte Positionen können denselben Wert enthalten. Wenn bei
[1, 1]beide Positionen gezogen werden, erscheint daher die 1 mehrfach. - 8HerausforderungDie gemischte Liste behalten
Welchen Wert enthält
namesam Ende und wie sollte die letzte Zeile geändert werden, damit die Liste erhalten bleibt?names = ["Mina", "Luis", "Jo"] names = random.shuffle(names)---reveal---
Die Variable enthält
None.shuffleverändert die vorhandene Liste und gibt dannNonezurück; die Zuweisung ersetzt die Bindung an die Liste durch diesen Rückgabewert. Verwenderandom.shuffle(names)in einer eigenen Zeile. Die neue Reihenfolge der Liste ist zufällig, aber das Rückgabeverhalten ist festgelegt. - 9VerbindungEine Simulation wiederholen und weitere Werkzeuge finden
Rufe vor dem Erzeugen von Werten
random.seed(42)auf, um bei derselben Python-Version dieselbe Folge zu wiederholen. Das erleichtert es, einen zufällig auftretenden Fehler zu reproduzieren. Verwendesecretsfür Sicherheitstoken.Andere Standardmodule lösen alltägliche Aufgaben:
statistics.mean([2, 4, 9])ergibt5undmedianergibt4.datetime.date.today()liefert das heutige Datum.sys.argvlistet den Namen des ausgeführten Skripts gefolgt von seinen Befehlszeilenargumenten auf. - 10BeispielEine wiederverwendbare Funktion in ein Modul setzen
Speichere dies als
geometry.py:import math def circle_area(radius): """Return the circle's area.""" return math.pi * radius ** 2Ein Skript im selben Ordner kann
import geometryund danachprint(geometry.circle_area(2))ausführen. Es gibt12.566370614359172aus.Importiere die Datei ohne
.py. Die erste Zeichenkette der Funktion ist ihr Docstring, denhelp(geometry.circle_area)anzeigen kann. - 11ErklärungEin Import führt Anweisungen auf Modulebene aus
Angenommen,
geometry.pyendet außerhalb einer Funktion mitprint(circle_area(2)). Sein erster Import gibt den Flächeninhalt als Nebeneffekt aus.Python führt Anweisungen auf Modulebene der Reihe nach aus. Die Definition von
circle_areaerstellt die Funktion, ohne ihren Körper auszuführen; ihr Aufruf innerhalb der Ausgabe auf Modulebene führt die Berechnung aus. Ein zweiter Import im selben Prozess verwendet das geladene Modul wieder. Auch eine interaktive Shell behält nach einer Änderung der Datei diese geladene Version, bis du sie neu startest. - 12BeispielNur bei direkter Ausführung starten
Belasse die wiederverwendbare Funktion oberhalb dieses Blocks:
def main(): print(circle_area(2)) if __name__ == "__main__": main()Eine direkte Ausführung setzt
__name__auf"__main__", sodassmain()ausgeführt wird. Ein Import setzt es auf"geometry", sodass der Start übersprungen wird.Die Definitionen bleiben in beiden Fällen verfügbar. Dadurch kann ein Test
circle_areaimportieren, ohne die gewöhnliche Ausgabe des Programms zu erzeugen. - 13HerausforderungDie Funktion für importierenden Code verfügbar halten
Eine Datei definiert
convertinnerhalb ihresif __name__ == "__main__":-Blocks. Ein neuer Testprozess importiert die Datei und kannconvertnicht finden. Welche Anordnung ermöglicht sowohl die Wiederverwendung als auch den Start bei direkter Ausführung?---reveal---
Verschiebe
def convert(...)außerhalb der Schutzbedingung und belasse nur den Programmstart innerhalb. Beim Import ist die Schutzbedingung falsch, sodass eine Definition darin übersprungen wird. Durch das Verschieben der Definition wird sie verfügbar, ohne die Anwendung zu starten. - 14TippDie Datei prüfen, die ein Import findet
Python prüft eingebaute Module und durchsucht dann
sys.pathder Reihe nach. Der Ordner des Skripts steht normalerweise vor Bibliotheksordnern. Eine lokale Dateirandom.pykann daher dasrandomverdecken, das du importieren wolltest.Benenne diese Datei zum Beispiel in
dice.pyum. Verwende für eigene Hilfsmodule gültige Namen wiemy_tools.pyund bewahre sie neben dem Anfängerskript auf, das sie importiert. Eine Datei außerhalb des Suchpfads wird durch einen gewöhnlichen Import nicht gefunden. - 15BeispielIm Terminal installieren, in Python importieren
Führe im Terminal aus:
python -m pip install cowsayDanach kann Python-Code Folgendes verwenden:
import cowsay cowsay.cow("Hello, Ada")Das Paket gibt eine Kuh aus Textzeichen und eine Sprechblase aus.
pipinstalliert Code, währendimportdie Bindung erzeugt, die dein Programm verwendet. Der Installationsbefehl ist keine Python-Anweisung; an einer>>>-Eingabeaufforderung führt er zu ungültiger Syntax. - 16ErklärungDas pip des ausführenden Python verwenden
Läuft ein Skript mit
python3, installiere über denselben Befehl:python3 -m pip install cowsay python3 game.py-m pipwählt das pip, das zu diesem Interpreter gehört. Unter Windows kann dein Befehl stattdessenpysein.Kann der Editor ein erfolgreich installiertes Paket nicht importieren, vergleiche den dort ausgewählten Interpreter mit dem im Terminal verwendeten. Eine erfolgreiche Installation in einem Python installiert das Paket nicht in jedem anderen Python.
- 17HerausforderungInstallationsnamen und Importnamen unterscheiden
Die Dokumentation eines Pakets empfiehlt,
Pillowzu installieren undPILzu importieren. Die Installation für dein ausführendes Python gelingt, aberimport Pillowschlägt fehl. Was sollte sich ändern und warum?---reveal---
Verwende
import PIL. Der von pip verwendete Distributionsname kann sich von dem bereitgestellten Python-Importnamen unterscheiden. Da bereits feststeht, dass der Interpreter passt, würde die Wiederholung des unveränderten Installationsbefehls diesen konkreten Namensfehler bestehen lassen. - 18TippEin Paket untersuchen, bevor du es änderst
python -m pip listzeigt installierte Versionen.show requestsliefert Version und Speicherort eines Pakets;install --upgrade requestsaktualisiert es unduninstall requestsentfernt es. Jeder kurze Befehl folgt aufpython -m pip.Prüfe vor der Installation den genauen PyPI-Namen und die Projektdokumentation. Verwende bei einem Fehler
externally-managed-environmenteine virtuelle Umgebung für das Projekt; erzwinge keine Installation mitsudo pip install. - 19HerausforderungEinen Import und einen späteren Aufruf vorhersagen
Ein neues Modul
sizes.pyenthält diesen Code. Ein anderes Skript führtfrom sizes import batchesund dannprint(batches(17))aus. Was wird ausgegeben?import math def batches(n): return math.ceil(n / 5) if __name__ == "__main__": print(batches(12))---reveal---
Nur
4wird ausgegeben. Der Import erstellt die Funktion, überspringt aber die geschützte Ausgabe. Der spätere Aufruf dividiert 17 durch 5 und rundet dann 3,4 auf 4 auf. Die Bindungmathinnerhalb vonsizessteht dessen Funktion zur Verfügung. - 20ZusammenfassungCode von seiner Quelle bis zum Aufruf verfolgen
Wähle einen Import, der zu den Namen passt, die du verwenden möchtest. Prüfe, was ein Bibliotheksaufruf zurückgibt, einschließlich eines möglichen
Nonebei einer Operation, die eine Liste verändert.Halte wiederverwendbare Definitionen beim Import verfügbar und setze den Anwendungsstart hinter den Hauptprogrammschutz. Stimme bei installiertem Code pip auf den ausführenden Interpreter ab und prüfe den dokumentierten Importnamen. Schlägt ein Aufruf fehl, helfen diese Einzelheiten, eine fehlende Bindung, ein fehlendes Attribut und eine fehlende Installation zu unterscheiden.
2Einen Fehler bis zu seiner Ursache zurückverfolgen
11 Schritte
Speichere diese Einheit, um jede Interaktive Lektion Schritt für Schritt durchzugehen.
- 1EinführungDen Bericht als Beleg nutzen
- 2BeispielEine leere Liste erreicht eine Division
- 3ErklärungDen neuesten Aufrufrahmen finden
- 4BeispielDas Argument über die Aufrufer zurückverfolgen
- 5HerausforderungEin ungeeignetes Argument finden
- 6ErklärungGrammatik, Datentypen und Werte unterscheiden
- 7ErklärungUnterschiedliche Zugriffe führen zu unterschiedlichen Fehlern
- 8BeispielEin Programm kann mit einer falschen Summe normal enden
- 9HerausforderungEine fehlgeschlagene Addition erklären
- 10HerausforderungEinen Bibliotheksrahmen nutzen, ohne der Bibliothek die Schuld zu geben
- 11ZusammenfassungDie nächste Stelle zur Untersuchung auswählen
3Einen Programmfehler vom Beleg bis zur Korrektur untersuchen
14 Schritte
Speichere diese Einheit, um jede Interaktive Lektion Schritt für Schritt durchzugehen.
- 1EinführungDas falsche Ergebnis wiederholt erzeugen
- 2BeispielErwartetes und tatsächliches Ergebnis nennen
- 3ErklärungDie Vermutung überprüfbar machen
- 4BeispielVor der Aktualisierung ausgeben
- 5BeispielDie Additionen von Hand nachvollziehen
- 6HerausforderungEine Ausgabe nach der Aktualisierung deuten
- 7BeispielDas Durchlaufen selbst korrigieren
- 8VerbindungUnterschiedlich aufgebaute Eingaben erneut testen
- 9TippEinen größeren Fehlerfall verkleinern
- 10HerausforderungEinen Wert prüfen, der wie eine Zahl aussieht
- 11ErklärungBei dem Aufruf pausieren, den du untersuchen möchtest
- 12HerausforderungDen Schrittbefehl auswählen
- 13HerausforderungEine korrekte Ausgabe und eine falsche Rückgabe untersuchen
- 14ZusammenfassungDie Korrektur an Belege knüpfen
4Aus einem Grenzwertfehler einen dauerhaften Test machen
16 Schritte
Speichere diese Einheit, um jede Interaktive Lektion Schritt für Schritt durchzugehen.
- 1EinführungErwartetes Verhalten wiederholbar prüfen
- 2ErklärungEine Zusicherung hält eine Erwartung fest
- 3HerausforderungEine Prüfung finden, die nicht fehlschlagen kann
- 4KernaussagePrüfungen externer Eingaben aktiv halten
- 5BeispielEingaben um den Notenwechsel auswählen
- 6BeispielEine Testfunktion schreiben, die den Code importiert
- 7TippDie definierten Prüfungen ausführen
- 8HerausforderungEinen Schwellenwert korrigieren und die Prüfung behalten
- 9VerbindungDas zurückgegebene Ergebnis oder eine sinnvolle Eigenschaft testen
- 10BeispielDie unittest-Klassenvorlage verwenden
- 11ErklärungDie Zusicherung passend zum Ergebnis wählen
- 12BeispielEine fehlgeschlagene Notenprüfung lesen
- 13HerausforderungEinen fehlgeschlagenen Test von einem Testfehler unterscheiden
- 14TippPrüfen, was die automatische Testerkennung ausgeführt hat
- 15HerausforderungMit einem Bericht die Korrektur planen
- 16ZusammenfassungEinen Test für das korrigierte Verhalten behalten
Python-Module, Fehlersuche und Tests: Übungsaufgaben
Set 1: Importe, Fehlerhinweise und erste Prüfungen10 Übungsfragen
Frage 1 von 10
import math. Wie sollte es die Quadratwurzelfunktion für 81 aufrufen?Antwort zeigenAntwort ausblenden
math, sodass die Funktion über math.sqrt(81) erreicht wird und 9.0 zurückgibt. Der alleinstehende Aufruf sqrt(81) würde NameError auslösen, weil dieser Import keine separate Bindung für sqrt erzeugt hat.Frage 2 von 10
math.ceil(35 / 8) zurück und wofür steht das Ergebnis?Antwort zeigenAntwort ausblenden
4.375, sodass bei vier Shuttles drei Personen keinen Platz haben. ceil rundet auf die nächste ganze Zahl auf und ergibt fünf Shuttles. Das Runden auf die nächste ganze Zahl würde hier vier ergeben, benötigt werden aber genügend ganze Shuttles für alle.Frage 3 von 10
text_tools.py definiert shorten. Wie kann eine andere Datei im selben Ordner die Funktion importieren und aufrufen?Antwort zeigenAntwort ausblenden
.py weg, daher erzeugt import text_tools die Modulbindung. Über Punktnotation wird dann auf die Funktion shorten zugegriffen. Steht .py im Import, wird die Endung als Teil eines Importpfads statt als Dateiendung behandelt.Frage 4 von 10
>>> und gibst pip install cowsay ein. Warum meldet Python ungültige Syntax?Antwort zeigenAntwort ausblenden
>>> akzeptiert Python-Anweisungen, während die Installationssyntax von pip ein Terminalbefehl ist. Verlasse die Python-Shell und führe python -m pip install cowsay im Terminal aus. Ein vorheriger Import kann ein noch nicht installiertes Paket nicht verfügbar machen.Frage 5 von 10
ZeroDivisionError. Seine Aufrufrahmen zeigen von oben nach unten show([]), mean(data) und sum(values) / len(values). Welche Kette erklärt den Fehler am besten?Antwort zeigenAntwort ausblenden
len(values), und die früheren Aufrufe führen diesen Wert auf die leere Liste zurück. Ihre Länge ist null und verursacht die Ausnahme. Ein Zähler von null ist erlaubt, wenn der Divisor ungleich null ist, daher erklärt die Summe allein diesen Absturz nicht.Frage 6 von 10
int("7.25") aus?Antwort zeigenAntwort ausblenden
int, aber "7.25" ist kein Text in der Form einer ganzen Zahl, die diese Umwandlung verlangt. Deshalb entsteht ein ValueError. Ein TypeError würde auf einen ungeeigneten Eingabetyp oder eine ungeeignete Operation hinweisen, nicht auf diesen ungeeigneten Zeichenkettenwert.Frage 7 von 10
[9, 2, 5] zu 16 summieren, gibt aber 7 zurück. Du vermutest, dass sie das erste Element überspringt. Welche Beobachtung würde diese Idee am unmittelbarsten prüfen?Antwort zeigenAntwort ausblenden
Frage 8 von 10
Welche zwei Zeilen werden ausgegeben?
total = 0
for n in [3, 8]:
print(f"{n=} {total=}")
total += n
Antwort zeigenAntwort ausblenden
Frage 9 von 10
Was geschieht bei normaler Ausführung dieses Codes?
count = 6
assert count < 5, "too many"
print("saved")
Antwort zeigenAntwort ausblenden
6 < 5 ist falsch, sodass die Zusicherung AssertionError mit der angegebenen Meldung auslöst. Die Ausführung stoppt vor der letzten Ausgabe, weil die Ausnahme unbehandelt ist. Die Meldung ist Teil der Ausnahme; sie ist keine gewöhnliche Ausgabe, nach der die Ausführung weiterläuft.Frage 10 von 10
Die Regel lautet: bis einschließlich Gewicht 12 kostenlos, bei 13 bis einschließlich 18 eine Gebühr von 4 und über 18 eine Gebühr von 7. Tests für 5 und 20 bestehen. Welche Ergebnisse und welche Änderung diagnostizieren diesen Code richtig?
def fee(weight):
if weight < 12:
return 0
if weight <= 18:
return 4
return 7
Antwort zeigenAntwort ausblenden
Set 2: Module, Entscheidungen bei der Fehlersuche und Testergebnisse10 Übungsfragen
Speichere diese Einheit, um jede Antwort zu sehen und jedes Set zu üben.
Frage 1 von 10
import statistics as st lautet die nächste Zeile statistics.mean([3, 7]). Weitere Importe oder Zuweisungen gibt es nicht. Was erklärt den Fehler?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 2 von 10
Welchen Wert hat tickets nach diesem Code?
import random
tickets = [11, 22, 33]
tickets = random.shuffle(tickets)
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 3 von 10
double und enthält danach die ungeschützte Zeile print(double(6)). Ein anderes Skript importiert es während eines Laufs zweimal. Wie oft erzeugt diese Zeile eine Ausgabe?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 4 von 10
python -m pip show cowsay meldet einen Speicherort in Python B, und der Editor kann cowsay nicht importieren. Was solltest du zuerst tun?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 5 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 6 von 10
Diese Funktion soll jede Zahl addieren. Welche Art von Problem erklärt das Ergebnis von total([2, 5, 8])?
def total(values):
result = 0
for i in range(1, len(values)):
result += values[i]
return result
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 7 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 8 von 10
assert (score >= 0, "negative score"). Sie besteht, wenn score gleich -3 ist. Welche Änderung korrigiert die Prüfung und hält die Validierung zugleich unter -O aktiv?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 9 von 10
test_discount mit zwei Zusicherungen, ruft die Funktion aber nie auf. Du führst die Datei mit python test_discount.py aus; Code zur Testausführung ist nicht vorhanden. Warum werden keine Zusicherungen geprüft?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 10 von 10
self.assertEqual(lookup("x"), 7) auf. Der Aufruf von lookup löst vor der Rückgabe unerwartet KeyError aus. Welches Kurzzeichen markiert diesen Test?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Set 3: Ursachen über Module und Tests hinweg verfolgen10 Übungsfragen
Speichere diese Einheit, um jede Antwort zu sehen und jedes Set zu üben.
Frage 1 von 10
Was gibt dieser Code aus?
from math import sqrt as root
sqrt = 4
result = root(sqrt ** 2)
print(result, sqrt)
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 2 von 10
random.sample(["blue", "blue", "red"], 2). Warum kann das Ergebnis zwei Werte "blue" enthalten?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 3 von 10
Ein Modul enthält diesen Code. Ein neuer Prozess importiert es und ruft dann helper.answer() auf. Was geschieht?
# helper.py
if __name__ == "__main__":
def answer():
return 42
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 4 von 10
run_report(), zeigt dann deinen Aufruf parse(raw) und endet mit ValueError innerhalb einer Umwandlung in einer Bibliothek. Du untersuchst raw bei deinem parse-Aufruf und findest "9x", obwohl die Regel Text in Form einer ganzen Zahl verlangt. Wo sollte die Untersuchung weitergehen?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 5 von 10
result + 2 aus, wobei result der Rückgabewert des Aufrufs ist. Warum tritt ein TypeError auf?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 6 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 7 von 10
answer = calculate(7) angehalten. Du möchtest die erste Zeile innerhalb von calculate untersuchen. Welchen Befehl solltest du verwenden?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 8 von 10
charge(6) gibt 3 zurück, aber der zugehörige Test erwartet 3 und besteht. Welche Änderung erzeugt einen nützlichen Regressionstest und korrigiert die Regel?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 9 von 10
test_alpha, der 4 erwartet, aber 5 erhält, einen bestandenen test_beta und test_gamma, der unerwartet TypeError auslöst. Welche kompakte Fortschrittszeichenkette ergibt die standardmäßige alphabetische Ausführung?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 10 von 10
unittest.TestCase hat check_empty mit einer fehlschlagenden Gleichheitsprüfung und test_full mit einer bestandenen Prüfung. Das Testwerkzeug meldet einen Test und OK. Wie bringst du die fehlende Verhaltensprüfung zur Ausführung?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Set 4: Entscheidungen zu Import, Fehlersuche und Testen kombinieren10 Übungsfragen
Speichere diese Einheit, um jede Antwort zu sehen und jedes Set zu üben.
Frage 1 von 10
random.randint(2, 5) auf. Ein neuer Test legt bei derselben Python-Version denselben Startwert fest und wiederholt dieselben Aufrufe. Was sollten seine Prüfungen erwarten?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 2 von 10
beautifulsoup4 als Distribution und bs4 als deren Modul. Ein Skript importiert bs4 erfolgreich. Ein Kollege möchte python -m pip install beautifulsoup4 ändern und bs4 installieren, damit die Namen übereinstimmen. Ist die Übereinstimmung der Namen ein guter Grund für diese Änderung?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 3 von 10
display.py meldet von oben nach unten diese Aufrufe: start(" "), read_count(text) und dann int(text); er endet mit ValueError. Welche Folge von Schritten prüft vor einer Korrektur die wahrscheinliche Ursache am besten?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 4 von 10
[6, 3, 2]. Welche erwarteten und tatsächlichen Ergebnisse für zwei neue Eingaben decken das verbleibende Problem auf?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 5 von 10
Diese Funktion gibt ein korrektes Produkt aus, aber der aufrufende Code erhält bei area(4, 5) + 1 einen TypeError. Wo solltest du untersuchen und welches Ergebnis erklärt den Fehler?
def area(w, h):
product = w * h
print(product)
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 6 von 10
Was geschieht bei einer gewöhnlichen, nicht optimierten Ausführung?
import math
assert math.isclose(0.1 + 0.2, 0.3)
assert 0.1 + 0.2 == 0.3
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 7 von 10
pick() gibt eine zufällige ganze Zahl von 3 bis einschließlich 7 zurück. pair_total() addiert die Ergebnisse zweier Aufrufe von pick(). Ein Test, der für jede Summe ohne festgelegten Startwert den Wert 10 verlangt, schlägt manchmal fehl, obwohl beide Funktionen richtig arbeiten. Welche Prüfung jedes Ergebnisses entspricht dem geforderten Summenbereich?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 8 von 10
reserve(n) muss ganzzahlige Anzahlen von 0 bis einschließlich 8 akzeptieren und außerhalb dieses Bereichs ValueError auslösen. Tests mit assertRaises(ValueError) melden für -1 ein F, weil keine Ausnahme auftritt, und für 9 ein E, weil ein TypeError auftritt. Ein Test mit 2 besteht. Welches Verhalten sollte die Korrektur herstellen?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 9 von 10
random.py und importiert random. Danach schlägt es fehl, weil random.randint fehlt. Welche erste Änderung behebt den Namenskonflikt?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 10 von 10
assert total == 10 für eine interne Berechnung und assert age >= 0 für eine Benutzereingabe. Es läuft mit -O, wobei total gleich 9 und age gleich -1 ist. Was geschieht mit diesen Prüfungen und welche Änderung erhält die erforderliche Eingabevalidierung?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Hängst du bei deiner eigenen Aufgabe fest? Hausaufgabenhilfe
Schlüsselbegriffe
A
Ablauftabelle
Abrundungsfunktion
math.floor rundet bei Bedarf in Richtung minus unendlich.math.floor(-3.2) von der Umwandlung mit int(-3.2) unterscheidet.Alias
as gewählte Name für ein importiertes Modul oder Objekt; der Import bindet diesen Namen im importierenden Programm.stats in import statistics as stats ermöglicht dem Skript den Aufruf stats.mean([1, 3, 8]).AssertionError
"wrong total" lenkt die Aufmerksamkeit auf den Test, der die berechnete Summe verglichen hat.Aufrufkette
report zu einer Funktion average zeigt, wie eine Liste die Funktion erreichte, die durch ihre Länge dividierte.Aufrufrahmen
Aufrundungsfunktion
math.ceil führt diese Rundung zur größeren ganzen Zahl aus.Ausnahme
average stoppen, bevor der aufrufende Code das berechnete arithmetische Mittel ausgibt.automatische Testerkennung
test*.py für Dateien.python -m unittest kann passende Dateien ausführen, ohne jede einzeln im Befehl aufzuführen.C
Code auf Modulebene
print aufruft, kann beim Import eine Ausgabe erzeugen, selbst wenn der importierende Code keine Funktion aufruft.D
Debugger
calculate einzusteigen und dessen Argumente zu untersuchen, wenn der aufrufende Code eine falsche Antwort erhält.direkte Veränderung eines Objekts
random.shuffle(cards) ändert cards und gibt None zurück, sodass keine Zuweisung nötig ist.Distribution
beautifulsoup4 wird unter diesem Namen installiert, aber ihre Dokumentation weist Python-Nutzer an, bs4 zu importieren.Docstring
help() angezeigt werden kann.circle_area kann das Radiusargument und den an den aufrufenden Code zurückgegebenen Flächeninhalt erklären.E
Eingabevalidierung
if score < 0 bleibt aktiv, wenn Python ohne Zusicherungen ausgeführt wird.Einzelschritt
result = convert(text) einen Einzelschritt, wenn der Rückgabewert falsch ist und der Eingabetext korrekt aussieht.F
Fehlersuche mit Ausgaben
print(f"{i=} {total=}") den Schleifenindex und die Summe zum untersuchten Zeitpunkt.Fehlersuche und Fehlerbehebung
fehlgeschlagener Test
F markierter fehlgeschlagener Test kann zeigen, dass die Funktion "B" zurückgegeben hat, wo die geprüfte Regel "A" verlangte.G
Grenzfall
H
Haltepunkt
Hauptprogrammschutz
if __name__ == "__main__":-Block, der seinen Körper bei direkter Ausführung ausführt und bei einem gewöhnlichen Import überspringt.main() ermöglicht Tests, die Funktionen des Moduls zu importieren, ohne dessen Anwendung zu starten.Hypothese
I
ImportError
from einen Namen anfordert, der in einem Modul nicht verfügbar ist, obwohl Python das Modul selbst gefunden hat.from math import square_root weist auf den angeforderten Namen hin, nicht auf eine fehlende Installation.IndexError
names[4] legt nahe, die Länge der Liste und die Schleifenschranken zu prüfen, die 4 erzeugt haben.Interpreter
K
KeyError
"price" kann zeigen, dass das aktuelle Wörterbuch den erwarteten Preiseintrag nie erhalten hat.L
Laufzeitfehler
Logikfehler
M
minimales reproduzierbares Beispiel
Modul
.py-Datei, deren Funktionen und Variablen zur Verwendung in einem anderen Programm importiert werden können.math stellt sqrt bereit, sodass ein Programm Quadratwurzeln berechnen kann, ohne diese Funktion zu definieren.Modulsuchpfad
sys.path, die Python bei der Suche nach Importen nach seinen eingebauten Modulen prüft.ModuleNotFoundError verursachen, wenn das Skript sie importiert.Modulverdeckung
random.py kann dazu führen, dass einem Skript die Bibliotheksfunktion randint fehlt.N
NameError
sqrt nach alleinigem import math bedeutet, dass die Funktion dort das Präfix math. benötigt.O
Optimierungsoption
-O verändert die Python-Ausführung, indem sie assert-Anweisungen entfernt. Erforderliche Validierungen können sich daher nicht auf diese Anweisungen verlassen.P
Paket
pip
python3 -m pip show cowsay weist das zu python3 gehörende pip an, die Installationsdetails dieses Pakets anzuzeigen.Programmfehler
Prozedurschritt
Pseudozufallszahl
random.randint(1, 6) kann in einer Brettspielsimulation ein Würfelergebnis darstellen.Punktnotation
math.sqrt(25) findet die Funktion sqrt innerhalb des bereits importierten Moduls math.PyPI
R
Randfall
Regression
S
Spezifikation
Standardbibliothek
math für Berechnungen und random für Simulationen wie Würfelwürfe.Startwert des Zufallszahlengenerators
Syntaxfehler
if ready lässt sich beheben, indem du den vor dem eingerückten Block erforderlichen Doppelpunkt ergänzt.T
Teile und herrsche
TestCase
unittest.TestCase erbt, kann innerhalb ihrer benannten Testmethoden self.assertEqual aufrufen.Testfehler
E markierter Testfehler kann entstehen, wenn eine Funktion TypeError auslöst, bevor die Gleichheitsprüfung einen Wert erhält.Testmethode
test beginnt, sodass unittest ihre Prüfungen finden und ausführen kann.test_limit kann einen Grenzfall getrennt vom Test einer gewöhnlichen Eingabe prüfen.Traceback
TypeError
"Score: " + 8 weist auf eine Zeichenkette und eine ganze Zahl hin, die mit diesem Ausdruck nicht verknüpft werden können.Typosquatting
U
Unit-Test
V
ValueError
int("6.2") bedeutet, dass die Zeichenkette keine ganze Zahl in der geforderten Form beschreibt.W
Wildcard-Import
*, um die öffentlichen Namen eines Moduls in das aktuelle Programm zu übernehmen. Das kann ihre Herkunft verschleiern und vorhandene Bindungen ersetzen.from math import * kann eine Variable namens e durch die mathematische Konstante ersetzen.Z
Ziehen ohne Zurücklegen
["A", "A", "B"] können beide Positionen gewählt werden, die "A" enthalten.Zusicherung
AssertionError ausgelöst.assert count == 3 prüft ein internes Ergebnis gegen die unabhängig erwartete Anzahl.Über diese Einheit
Erstelle deine eigenen Lernmaterialien
Mach aus deinen Notizen, Folien und Dokumenten Lernleitfäden, Übungsfragen, Karteikarten und 6 weitere Tools.