Python & AI Tutorials Logo
Python Programmierung

24. Fehler und Tracebacks verstehen

Fehler sind ein unvermeidlicher Teil des Programmierens. Jede Programmiererin und jeder Programmierer, von Anfänger bis Expertin, begegnet ihnen regelmäßig. Der Unterschied zwischen dem Kampf mit Fehlern und dem Lernen aus ihnen liegt darin, zu verstehen, was Python Ihnen sagen will, wenn etwas schiefgeht.

Wenn Python in Ihrem Code auf ein Problem stößt, hält es nicht einfach still an – es liefert detaillierte Informationen darüber, was schiefgelaufen ist, wo es passiert ist und oft Hinweise darauf, warum. Zu lernen, diese Fehlermeldungen zu lesen und zu interpretieren, ist eine der wertvollsten Fähigkeiten, die Sie als Programmierer entwickeln können.

In diesem Kapitel werden wir die zwei Hauptkategorien von Fehlern untersuchen, denen Sie begegnen werden: Syntaxfehler (syntax errors) (Probleme damit, wie Sie den Code geschrieben haben) und Laufzeit-Exceptions (runtime exceptions) (Probleme, die auftreten, während der Code läuft). Wir lernen, Tracebacks (tracebacks) zu lesen – Pythons detaillierte Fehlerberichte – und verstehen, wie Exceptions den normalen Ablauf Ihres Programms verändern. Am wichtigsten ist, dass wir ein Debugging-Mindset entwickeln, das Fehler nicht als Scheitern, sondern als wertvolle Information betrachtet, die Ihnen hilft, besseren Code zu schreiben.

24.1) Syntaxfehler vs. Laufzeit-Exceptions

Python unterscheidet zwischen zwei grundsätzlich verschiedenen Arten von Problemen in Ihrem Code: Syntaxfehler und Laufzeit-Exceptions. Dieses Verständnis hilft Ihnen, Probleme schneller zu diagnostizieren und zu wissen, wo Sie nach Lösungen suchen müssen.

24.1.1) Was Syntaxfehler sind

Ein Syntaxfehler (syntax error) tritt auf, wenn Python Ihren Code nicht verstehen kann, weil er gegen die grammatikalischen Regeln der Sprache verstößt. So wie „The cat sat on the“ ein unvollständiger englischer Satz ist, ist Code mit Syntaxfehlern unvollständiges oder fehlerhaftes Python, das der Interpreter nicht parsen kann.

Syntaxfehler werden erkannt, bevor Ihr Programm läuft. Python liest zuerst Ihr gesamtes Skript durch und prüft, ob es den Regeln der Sprache folgt. Findet es einen Syntaxfehler, weigert es sich, irgendeinen Teil des Codes auszuführen – nicht einmal die Teile, die korrekt sind.

Hier ist ein einfaches Beispiel:

python
# WARNUNG: Syntaxfehler – nur zur Demonstration
# FEHLER: Doppelpunkt nach if-Anweisung fehlt
age = 25
if age >= 18
    print("You are an adult")

Wenn Sie versuchen, diesen Code auszuführen, meldet Python sofort:

  File "example.py", line 3
    if age >= 18
                ^
SyntaxError: expected ':'

Beachten Sie mehrere wichtige Merkmale dieser Fehlermeldung:

  1. Datei und Zeilennummer: Python sagt Ihnen genau, wo es das Problem gefunden hat (line 3)
  2. Visueller Indikator: Das Caret-Zeichen (^) zeigt auf die Stelle, an der Python verwirrt war
  3. Fehlertyp: SyntaxError identifiziert dies klar als Grammatikproblem
  4. Hilfreicher Hinweis: expected ':' sagt Ihnen, was fehlt

Der Code läuft nie, weil Python gar nicht erst beginnen kann, ihn auszuführen – die Syntax ist ungültig.

Schauen wir uns einen weiteren häufigen Syntaxfehler an:

python
# WARNUNG: Syntaxfehler – nur zur Demonstration
# FEHLER: Nicht zusammenpassende Klammern
numbers = [1, 2, 3, 4, 5]
total = sum(numbers
print(f"Total: {total}")

Python meldet:

  File "example.py", line 2
    total = sum(numbers
               ^
SyntaxError: '(' was never closed

Hier hat Python erkannt, dass wir in Zeile 2 eine Klammer geöffnet, sie aber nie geschlossen haben. Der Fehler wird in Zeile 2 gemeldet (wo die nicht geschlossene Klammer ist), und das Caret-Zeichen zeigt auf die Stelle, an der Python die schließende Klammer erwartet hat.

Wichtige Eigenschaften von Syntaxfehlern:

  • Werden erkannt, bevor irgendein Code läuft
  • Verhindern die Ausführung des gesamten Programms
  • Weisen meist auf Tippfehler, fehlende Satzzeichen oder falsche Einrückung hin
  • Die Fehlerstelle kann leicht nach dem eigentlichen Fehler liegen

24.1.2) Was Laufzeit-Exceptions sind

Eine Laufzeit-Exception (runtime exception) (oder einfach „Exception“) tritt auf, wenn syntaktisch korrekter Code während der Ausführung auf ein Problem stößt. Der Code ist grammatikalisch gültiges Python, aber etwas geht schief, wenn das Programm tatsächlich läuft.

Anders als Syntaxfehler passieren Exceptions während Ihr Programm läuft. Python hat Ihren Code erfolgreich geparst und mit der Ausführung begonnen, ist dann aber auf eine Situation gestoßen, mit der es nicht umgehen konnte.

Hier ist ein einfaches Beispiel:

python
# Dieser Code hat gültige Syntax, wird aber eine Exception auslösen
numbers = [10, 20, 30]
print(numbers[0])  # Output: 10
print(numbers[5])  # Diese Zeile wird einen IndexError auslösen
print("This line never executes")

Output:

10
Traceback (most recent call last):
  File "example.py", line 3, in <module>
    print(numbers[5])
          ~~~~~~~^^^
IndexError: list index out of range

Beachten Sie, was passiert ist:

  1. Das erste print-Statement wurde erfolgreich ausgeführt (wir sahen 10)
  2. Das zweite print versuchte, auf Index 5 zuzugreifen, der nicht existiert
  3. Python löste eine IndexError-Exception aus
  4. Das Programm stoppte, und das dritte print wurde nie ausgeführt

Der Code war syntaktisch korrekt – Python hatte kein Problem zu verstehen, was wir tun wollten. Das Problem entstand während der Ausführung, als wir versucht haben, auf ein Listenelement zuzugreifen, das nicht existiert.

Hier ist ein weiteres Beispiel, das eine andere Art von Laufzeit-Exception zeigt:

python
# Gültige Syntax, aber Division durch Null zur Laufzeit
def calculate_average(total, count):
    return total / count
 
# Diese funktionieren einwandfrei
print(calculate_average(100, 4))  # Output: 25.0
print(calculate_average(75, 3))   # Output: 25.0
 
# Dies löst eine Exception aus
print(calculate_average(50, 0))   # ZeroDivisionError

Output:

25.0
25.0
Traceback (most recent call last):
  File "example.py", line 8, in <module>
    print(calculate_average(50, 0))
          ^^^^^^^^^^^^^^^^^^^^^^^^
  File "example.py", line 2, in calculate_average
    return total / count
           ~~~~~~^~~~~~~
ZeroDivisionError: division by zero

Die Funktion hat zweimal perfekt funktioniert, aber beim dritten Aufruf haben wir 0 als count übergeben, was eine Division durch Null verursacht hat. Python konnte dieses Problem nicht erkennen, bevor der Code tatsächlich mit diesen konkreten Werten lief.

Wichtige Eigenschaften von Laufzeit-Exceptions:

  • Treten während der Programmausführung auf
  • Der Code ist syntaktisch gültig
  • Hängen oft von spezifischen Daten oder Bedingungen ab
  • Das Programm läuft bis zu dem Punkt, an dem die Exception auftritt
  • Unterschiedliche Eingaben können unterschiedliche Exceptions verursachen (oder gar keine)

24.1.3) Syntaxfehler und Laufzeit-Exceptions vergleichen

Sehen wir uns beide Fehlertypen nebeneinander an, um ihre Unterschiede zu verstehen:

python
# Beispiel 1: Syntaxfehler
# FEHLER: Fehlendes schließendes Anführungszeichen
print("Program started!")
message = "Hello, world
print(message)

Dies erzeugt sofort einen Syntaxfehler:

  File "example.py", line 4
    message = "Hello, world
              ^
SyntaxError: unterminated string literal (detected at line 4)

Wichtig: Beachten Sie, dass Sie „Program started!“ nicht in der Ausgabe sehen. Python hat den Syntaxfehler erkannt, bevor irgendein Code überhaupt ausgeführt wurde.

Vergleichen Sie das nun mit einer Laufzeit-Exception:

python
# Beispiel 2: Laufzeit-Exception
# Gültige Syntax, aber die Variable existiert nicht
print("Program started!")
message = "Hello, world"
print(mesage)  # Tippfehler: 'mesage' statt 'message'

Output:

Program started!
Traceback (most recent call last):
  File "example.py", line 5, in <module>
    print(mesage)
          ^^^^^^
NameError: name 'mesage' is not defined

Wichtig: Dieses Mal sehen Sie „Program started!“ in der Ausgabe. Python hat die ersten beiden print- und Zuweisungs-Statements (Zeilen 3–4) erfolgreich ausgeführt, ist dann aber in Zeile 5 auf ein Problem gestoßen, als es versuchte, mesage zu finden.

Der entscheidende Unterschied: Im ersten Beispiel hat Python nie versucht, den Code auszuführen – es fand den Syntaxfehler beim Parsen. Im zweiten Beispiel hat Python erfolgreich mit der Ausführung des Programms begonnen und mehrere Zeilen ausgeführt, bevor es auf den Laufzeitfehler stieß.

Nein

Ja

Nein

Ja

Python liest Ihren Code

Gültige Syntax?

Syntaxfehler
Programm läuft nie

Programm beginnt mit der Ausführung

Problem während der
Ausführung?

Programm wird
erfolgreich beendet

Laufzeit-Exception
Programm stoppt

24.2) Häufige integrierte Exception-Typen

Python hat viele integrierte Exception-Typen, die jeweils eine bestimmte Art von Problem darstellen. Wenn Sie lernen, diese häufigen Exceptions zu erkennen, verstehen Sie schneller, was schiefgelaufen ist und wie Sie es beheben können. Jeder Exception-Typ hat einen beschreibenden Namen, der auf das Problem hinweist.

24.2.1) NameError: Undefinierte Namen verwenden

Ein NameError tritt auf, wenn Sie versuchen, eine Variable, Funktion oder einen anderen Namen zu verwenden, den Python nicht erkennt. Das bedeutet meist, dass Sie etwas zu definieren vergessen haben, einen Namen falsch geschrieben haben oder versuchen, etwas zu verwenden, bevor es erstellt wurde.

python
# Beispiel 1: Vergessen, eine Variable zu definieren
print(greeting)  # NameError: name 'greeting' is not defined

Output:

Traceback (most recent call last):
  File "example.py", line 2, in <module>
    print(greeting)
          ^^^^^^^^
NameError: name 'greeting' is not defined

Python sagt Ihnen, dass es nicht weiß, was greeting ist. Sie müssen es zuerst erstellen:

python
# Korrekte Version
greeting = "Hello, Python!"
print(greeting)  # Output: Hello, Python!

Hier ist ein subtileres Beispiel mit einem Tippfehler:

python
# Beispiel 2: Tippfehler im Variablennamen
user_name = "Alice"
age = 30
 
print(f"{username} is {age} years old")  # NameError: name 'username' is not defined

Wir haben user_name (mit Unterstrich) definiert, aber versucht, username (ohne Unterstrich) zu verwenden. Python betrachtet diese als völlig unterschiedliche Namen.

24.2.2) TypeError: Falscher Typ für eine Operation

Ein TypeError tritt auf, wenn Sie versuchen, eine Operation mit einem Wert des falschen Typs auszuführen. Zum Beispiel können Sie keinen String zu einer Ganzzahl addieren oder etwas aufrufen, das keine Funktion ist.

python
# Beispiel 1: Inkompatible Typen mischen
age = 25
message = "You are " + age + " years old"  # TypeError

Output:

Traceback (most recent call last):
  File "example.py", line 2, in <module>
    message = "You are " + age + " years old"
              ~~~~~~~~~~~~^~~~~
TypeError: can only concatenate str (not "int") to str

Python sagt Ihnen, dass der Operator + Strings mit Strings verketten kann, aber keine Strings mit Ganzzahlen. Sie müssen die Ganzzahl in einen String umwandeln:

python
# Korrekte Version
age = 25
message = "You are " + str(age) + " years old"
print(message)  # Output: You are 25 years old

TypeErrors treten auch auf, wenn Sie einer Funktion die falsche Anzahl an Argumenten übergeben:

python
# Beispiel 3: Falsche Anzahl an Argumenten
def calculate_area(length, width):
    return length * width
 
area = calculate_area(5)  # TypeError: missing 1 required positional argument

Output:

Traceback (most recent call last):
  File "example.py", line 4, in <module>
    area = calculate_area(5)
TypeError: calculate_area() missing 1 required positional argument: 'width'

Die Funktion erwartet zwei Argumente, aber wir haben nur eines angegeben.

24.2.3) ValueError: Richtiger Typ, falscher Wert

Ein ValueError tritt auf, wenn Sie einen Wert des richtigen Typs übergeben, der Wert selbst aber für die Operation ungeeignet ist. Der Typ passt, aber der konkrete Wert ergibt in diesem Kontext keinen Sinn.

python
# Beispiel 1: Ungültigen String in eine Ganzzahl umwandeln
user_input = "twenty-five"
age = int(user_input)  # ValueError: invalid literal for int()

Output:

Traceback (most recent call last):
  File "example.py", line 2, in <module>
    age = int(user_input)
ValueError: invalid literal for int() with base 10: 'twenty-five'

Die Funktion int() erwartet einen String, und wir haben ihr einen String gegeben – der Typ ist also korrekt. Aber der String „twenty-five“ kann nicht in eine Ganzzahl umgewandelt werden, weil er Buchstaben enthält. Der String „25“ würde einwandfrei funktionieren:

python
# Korrekte Version
user_input = "25"
age = int(user_input)
print(age)  # Output: 25

ValueErrors treten auch bei Listenmethoden auf:

python
# Beispiel 3: Nicht vorhandenes Element entfernen
fruits = ["apple", "banana", "orange"]
fruits.remove("grape")  # ValueError: 'grape' is not in list

Output:

Traceback (most recent call last):
  File "example.py", line 2, in <module>
    fruits.remove("grape")
    ~~~~~~~~~~~~~^^^^^^^^^
ValueError: list.remove(x): x not in list

Die Methode remove() erwartet einen Wert, der in der Liste vorhanden ist. Wir sollten zuerst prüfen:

python
# Korrekte Version
fruits = ["apple", "banana", "orange"]
if "grape" in fruits:
    fruits.remove("grape")
else:
    print("Grape not found in list")  # Output: Grape not found in list

24.2.4) IndexError: Ungültiger Sequenzindex

Ein IndexError tritt auf, wenn Sie versuchen, auf eine Sequenz (Liste, Tuple, String) mit einem Index zuzugreifen, der nicht existiert. Denken Sie daran, dass Python eine nullbasierte Indizierung verwendet und gültige Indizes von 0 bis len(sequence) - 1 reichen.

python
# Beispiel 1: Index zu groß
colors = ["red", "green", "blue"]
print(colors[0])  # Output: red
print(colors[3])  # IndexError: list index out of range

Output:

red
Traceback (most recent call last):
  File "example.py", line 3, in <module>
    print(colors[3])
          ~~~~~~^^^
IndexError: list index out of range

Die Liste hat drei Elemente an den Indizes 0, 1 und 2. Index 3 existiert nicht. Das ist ein sehr häufiger Fehler, wenn man vergisst, dass die Indizierung bei 0 beginnt:

python
# Korrekte Version
colors = ["red", "green", "blue"]
print(colors[2])  # Output: blue (das dritte Element)

24.2.5) KeyError: Fehlender Dictionary-Schlüssel

Ein KeyError tritt auf, wenn Sie versuchen, auf einen Dictionary-Schlüssel zuzugreifen, der nicht existiert. Anders als bei Listen, bei denen Sie die Länge prüfen können, können Dictionaries beliebige Schlüssel haben, daher müssen Sie überprüfen, ob ein Schlüssel existiert, bevor Sie darauf zugreifen.

python
# Beispiel 1: Zugriff auf nicht existierenden Schlüssel
student = {
    "name": "Alice",
    "age": 20,
    "major": "Computer Science"
}
 
print(student["name"])   # Output: Alice
print(student["grade"])  # KeyError: 'grade'

Output:

Alice
Traceback (most recent call last):
  File "example.py", line 7, in <module>
    print(student["grade"])
          ~~~~~~~^^^^^^^^^
KeyError: 'grade'

Das Dictionary hat keinen Schlüssel "grade". Sie können zuerst prüfen, ob ein Schlüssel existiert:

python
# Korrekte Version mit 'in'
student = {
    "name": "Alice",
    "age": 20,
    "major": "Computer Science"
}
 
if "grade" in student:
    print(student["grade"])
else:
    print("Grade not available")  # Output: Grade not available

Oder verwenden Sie die Methode get(), die None (oder einen Standardwert) zurückgibt, statt einen Fehler auszulösen:

python
# Alternative mit get()
grade = student.get("grade")
if grade is not None:
    print(f"Grade: {grade}")
else:
    print("Grade not available")  # Output: Grade not available

KeyErrors treten häufig auf, wenn Sie Daten mit inkonsistenter Struktur verarbeiten:

python
# Beispiel 2: Mehrere Datensätze verarbeiten
students = [
    {"name": "Alice", "age": 20, "grade": "A"},
    {"name": "Bob", "age": 21},  # Fehlender Schlüssel 'grade'
    {"name": "Carol", "age": 19, "grade": "B"}
]
 
for student in students:
    print(f"{student['name']}: {student['grade']}")  # KeyError bei Bob

Output:

Alice: A
Traceback (most recent call last):
  File "example.py", line 7, in <module>
    print(f"{student['name']}: {student['grade']}")
                                ~~~~~~~^^^^^^^^^
KeyError: 'grade'

Verwenden Sie get() mit einem Standardwert, um fehlende Schlüssel elegant zu behandeln:

python
# Korrekte Version
students = [
    {"name": "Alice", "age": 20, "grade": "A"},
    {"name": "Bob", "age": 21},
    {"name": "Carol", "age": 19, "grade": "B"}
]
 
for student in students:
    grade = student.get("grade", "Not assigned")
    print(f"{student['name']}: {grade}")

Output:

Alice: A
Bob: Not assigned
Carol: B

24.2.6) AttributeError: Ungültiger Attributzugriff

Ein AttributeError tritt auf, wenn Sie versuchen, auf ein Attribut oder eine Methode zuzugreifen, die für ein Objekt nicht existiert. Das passiert oft, wenn man Methoden zwischen verschiedenen Typen verwechselt oder Attributnamen falsch schreibt.

python
# Beispiel 1: Falsche Methode für den Typ
numbers = [1, 2, 3, 4, 5]
numbers.append(6)  # Das funktioniert – Listen haben append()
print(numbers)     # Output: [1, 2, 3, 4, 5, 6]
 
text = "Hello"
text.append("!")   # AttributeError: 'str' object has no attribute 'append'

Output:

[1, 2, 3, 4, 5, 6]
Traceback (most recent call last):
  File "example.py", line 6, in <module>
    text.append("!")
    ^^^^^^^^^^^
AttributeError: 'str' object has no attribute 'append'

Strings haben keine Methode append(), weil sie unveränderlich sind. Sie müssen Verkettung oder andere String-Methoden verwenden:

python
# Korrekte Version
text = "Hello"
text = text + "!"  # Verkettung
print(text)        # Output: Hello!

AttributeErrors treten auch bei Tippfehlern auf:

python
# Beispiel 2: Falsch geschriebener Methodenname
message = "Python Programming"
result = message.uppper()  # AttributeError: 'str' object has no attribute 'uppper'

Output:

Traceback (most recent call last):
  File "example.py", line 2, in <module>
    result = message.uppper()
             ^^^^^^^^^^^^^^
AttributeError: 'str' object has no attribute 'uppper'. Did you mean: 'upper'?

Beachten Sie, dass Python 3.10+ oft die korrekte Schreibweise vorschlägt! Die korrekte Methode ist upper():

python
# Korrekte Version
message = "Python Programming"
result = message.upper()
print(result)  # Output: PYTHON PROGRAMMING

24.2.7) ZeroDivisionError: Division durch Null

Ein ZeroDivisionError tritt auf, wenn Sie versuchen, eine Zahl durch Null zu teilen, was mathematisch nicht definiert ist. Das passiert häufig bei Benutzereingaben oder berechneten Werten, die Sie nicht als Null erwartet haben.

python
# Beispiel 1: Direkte Division durch Null
result = 10 / 0  # ZeroDivisionError: division by zero

Output:

Traceback (most recent call last):
  File "example.py", line 1, in <module>
    result = 10 / 0
             ~~~^~~
ZeroDivisionError: division by zero

Das gilt auch für Ganzzahldivision und Modulo:

python
# Beispiel 2: Andere Divisionsoperationen
a = 10 // 0  # ZeroDivisionError
b = 10 % 0   # ZeroDivisionError

Ein realistischeres Beispiel betrifft Berechnungen:

python
# Beispiel 3: Durchschnitt berechnen
def calculate_average(numbers):
    total = sum(numbers)
    count = len(numbers)
    return total / count
 
scores = [85, 90, 78, 92]
print(calculate_average(scores))  # Output: 86.25
 
empty_scores = []
print(calculate_average(empty_scores))  # ZeroDivisionError

Output:

86.25
Traceback (most recent call last):
  File "example.py", line 9, in <module>
    print(calculate_average(empty_scores))
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "example.py", line 4, in calculate_average
    return total / count
           ~~~~~~^~~~~~~
ZeroDivisionError: division by zero

Eine leere Liste hat die Länge 0, was eine Division durch Null verursacht. Prüfen Sie diesen Fall immer:

python
# Korrekte Version
def calculate_average(numbers):
    if len(numbers) == 0:
        return 0  # Oder return None, oder eine aussagekräftigere Fehlermeldung auslösen
    
    total = sum(numbers)
    count = len(numbers)
    return total / count
 
scores = [85, 90, 78, 92]
print(calculate_average(scores))  # Output: 86.25
 
empty_scores = []
print(calculate_average(empty_scores))  # Output: 0

Häufige Exception-Typen

NameError
Undefinierte Variable/Funktion

TypeError
Falscher Typ für Operation

ValueError
Richtiger Typ, falscher Wert

IndexError
Ungültiger Sequenzindex

KeyError
Fehlender Dictionary-Schlüssel

AttributeError
Ungültiges Attribut/Methode

ZeroDivisionError
Division durch Null

Diese häufigen Exception-Typen zu verstehen, hilft Ihnen dabei, Probleme schnell zu diagnostizieren. Wenn Sie eine Exception sehen, sagt Ihnen der Typname sofort, welche Problemkategorie aufgetreten ist, und die Fehlermeldung liefert konkrete Details dazu, was schiefgelaufen ist.

24.3) Tracebacks im Detail lesen und interpretieren

Wenn eine Laufzeit-Exception auftritt, sagt Python nicht nur, was schiefgelaufen ist – es liefert einen detaillierten Traceback (traceback), der genau zeigt, wie Ihr Programm zu diesem Punkt gekommen ist. Tracebacks lesen zu lernen, ist essenziell für effektives Debugging. Ein Traceback ist wie eine Brotkrümelspur, die den Pfad zeigt, den Ihr Programm genommen hat, bevor es auf den Fehler gestoßen ist.

24.3.1) Anatomie eines Tracebacks

Beginnen wir mit einem einfachen Beispiel und untersuchen jeden Teil des Tracebacks:

python
# Einfaches Programm mit einem Fehler
def calculate_discount(price, discount_percent):
    discount_amount = price * (discount_percent / 100)
    final_price = price - discount_amount
    return final_price
 
def process_order(item_price, discount):
    discounted_price = calculate_discount(item_price, discount)
    tax = discounted_price * 0.08
    total = discounted_price + tax
    return total
 
# Hauptprogramm
original_price = "50"  # Hoppla! Das sollte eine Zahl sein
discount_rate = 10
final_cost = process_order(original_price, discount_rate)
print(f"Final cost: ${final_cost:.2f}")

Output:

Traceback (most recent call last):
  File "example.py", line 16, in <module>
    final_cost = process_order(original_price, discount_rate)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "example.py", line 8, in process_order
    discounted_price = calculate_discount(item_price, discount)
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "example.py", line 2, in calculate_discount
    discount_amount = price * (discount_percent / 100)
                      ~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~
TypeError: can't multiply sequence by non-int of type 'float'

Zerlegen wir jede Komponente dieses Tracebacks:

1. Der Header: "Traceback (most recent call last):"

Diese Zeile sagt Ihnen, dass das Folgende ein Traceback ist – ein Protokoll von Funktionsaufrufen. Die Formulierung „most recent call last“ bedeutet, dass der Traceback in chronologischer Reihenfolge angezeigt wird: Der zuerst aufgerufene Funktionsaufruf steht zuerst, und die Stelle, an der der Fehler tatsächlich auftrat, steht zuletzt.

2. Der Call Stack (von oben nach unten lesen):

  File "example.py", line 16, in <module>
    final_cost = process_order(original_price, discount_rate)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Das ist der erste Funktionsaufruf in der Kette. Er zeigt:

  • Dateiname: "example.py" – wo sich der Code befindet
  • Zeilennummer: line 16 – die genaue Zeile, die diesen Aufruf ausgelöst hat
  • Kontext: in <module> – dieser Code ist auf oberster Ebene (nicht innerhalb einer Funktion)
  • Code: Die tatsächlich ausgeführte Zeile
  • Hervorhebung: Die ^-Zeichen zeigen auf den konkreten Teil der Zeile, der beteiligt ist

Der Kontext <module> bedeutet, dass dies Code ist, der auf Modulebene läuft (der Hauptteil Ihres Skripts), nicht innerhalb einer Funktion.

  File "example.py", line 8, in process_order
    discounted_price = calculate_discount(item_price, discount)
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Das ist der zweite Funktionsaufruf. Die Funktion process_order wurde aus Zeile 16 aufgerufen, und jetzt sind wir innerhalb dieser Funktion in Zeile 8, wo sie calculate_discount aufruft.

  File "example.py", line 2, in calculate_discount
    discount_amount = price * (discount_percent / 100)
                      ~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~

Hier ist die Stelle, an der der Fehler tatsächlich aufgetreten ist. Wir sind jetzt in der Funktion calculate_discount in Zeile 2, und dies ist die Zeile, die das Problem ausgelöst hat.

3. Die Fehlermeldung:

TypeError: can't multiply sequence by non-int of type 'float'

Das ist der eigentliche Fehler, der aufgetreten ist:

  • Exception-Typ: TypeError – sagt Ihnen die Fehlerkategorie
  • Beschreibung: Der Rest erklärt konkret, was schiefgelaufen ist

In diesem Fall sagt Python uns, dass wir versucht haben, eine Sequenz (hier: einen String) mit einer float zu multiplizieren, was nicht erlaubt ist.

24.3.2) Den Traceback von unten nach oben lesen

Während der Traceback in chronologischer Reihenfolge (oben nach unten) ausgegeben wird, lesen erfahrene Programmierer ihn oft von unten nach oben, weil der eigentliche Fehler unten steht und die Zeilen darüber zeigen, wie wir dorthin gekommen sind.

Lesen wir unseren vorherigen Traceback von unten nach oben:

Schritt 1: Beginnen Sie mit der Fehlermeldung

TypeError: can't multiply sequence by non-int of type 'float'

„Okay, wir haben versucht, eine Sequenz mit einer float zu multiplizieren. Das ist nicht erlaubt.“

Schritt 2: Schauen Sie, wo der Fehler aufgetreten ist

  File "example.py", line 2, in calculate_discount
    discount_amount = price * (discount_percent / 100)
                      ~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~

„Der Fehler ist in der Funktion calculate_discount in Zeile 2 passiert. Wir versuchen, price mit etwas zu multiplizieren.“

Schritt 3: Gehen Sie zurück und schauen Sie, wie wir dorthin gekommen sind

  File "example.py", line 8, in process_order
    discounted_price = calculate_discount(item_price, discount)

„Die Funktion calculate_discount wurde von process_order in Zeile 8 aufgerufen und hat item_price als Parameter price übergeben bekommen.“

Schritt 4: Weiter zurückverfolgen

  File "example.py", line 16, in <module>
    final_cost = process_order(original_price, discount_rate)

„Und process_order wurde vom Hauptprogramm in Zeile 16 aufgerufen und hat original_price als item_price übergeben.“

Schritt 5: Finden Sie die Ursache

Jetzt können wir das Problem zurückverfolgen: original_price ist "50" (ein String), der als item_price an process_order übergeben wird, der ihn als price an calculate_discount weitergibt, wo wir versuchen, ihn mit einer float zu multiplizieren. Die Lösung ist, original_price zu einer Zahl zu machen:

python
# Korrigierte Version
def calculate_discount(price, discount_percent):
    discount_amount = price * (discount_percent / 100)
    final_price = price - discount_amount
    return final_price
 
def process_order(item_price, discount):
    discounted_price = calculate_discount(item_price, discount)
    tax = discounted_price * 0.08
    total = discounted_price + tax
    return total
 
# Hauptprogramm – Typ korrigiert
original_price = 50  # Jetzt ist es eine Zahl, kein String
discount_rate = 10
final_cost = process_order(original_price, discount_rate)
print(f"Final cost: ${final_cost:.2f}")  # Output: Final cost: $48.60

Traceback lesen

1. Fehlermeldung lesen
unterste Zeile
2. Fehlerstelle finden
letzter Codeblock
3. Call-Stack zurückverfolgen
nach oben arbeiten
4. Hypothese bilden
Was ist schiefgelaufen?
5. Prüfen und beheben
Lösung testen

Zu verstehen, wie man Tracebacks liest, verwandelt sie von einschüchternden Textwänden in hilfreiche Debugging-Werkzeuge. Jede Zeile liefert wertvolle Informationen über den Ausführungspfad Ihres Programms, und mit Übung werden Sie Probleme schnell erkennen und beheben können, indem Sie der Spur folgen, die der Traceback vorgibt.

24.4) Wie Exceptions den normalen Ablauf eines Programms verändern

Wenn eine Exception auftritt, stoppt sie nicht nur Ihr Programm – sie verändert grundlegend, wie das Programm ausgeführt wird. Dieses Verhalten zu verstehen, ist entscheidend, um robusten Code zu schreiben und zu verstehen, was passiert, wenn Fehler auftreten.

24.4.1) Normaler Programmablauf vs. Ablauf bei Exceptions

Bei normaler Ausführung führt Python Ihren Code Zeile für Zeile aus, von oben nach unten:

python
# Normaler Programmablauf
print("Step 1: Starting calculation")
result = 10 + 5
print(f"Step 2: Result is {result}")
final = result * 2
print(f"Step 3: Final value is {final}")
print("Step 4: Program complete")

Output:

Step 1: Starting calculation
Step 2: Result is 15
Step 3: Final value is 30
Step 4: Program complete

Jede Zeile wird der Reihe nach ausgeführt. Sehen wir uns nun an, was passiert, wenn eine Exception auftritt:

python
# Programmablauf mit einer Exception
print("Step 1: Starting calculation")
result = 10 / 0  # Dies löst ZeroDivisionError aus
print(f"Step 2: Result is {result}")  # Wird nie ausgeführt
final = result * 2  # Wird nie ausgeführt
print(f"Step 3: Final value is {final}")  # Wird nie ausgeführt
print("Step 4: Program complete")  # Wird nie ausgeführt

Output:

Step 1: Starting calculation
Traceback (most recent call last):
  File "example.py", line 2, in <module>
    result = 10 / 0
             ~~~^~~
ZeroDivisionError: division by zero

Beachten Sie, dass nur die erste print-Anweisung ausgeführt wurde. Sobald die Exception in Zeile 2 auftrat, hat Python die Ausführung des restlichen Codes gestoppt. Die Exception hat den normalen Ablauf unterbrochen.

24.4.2) Exceptions propagieren den Call Stack nach oben

Wenn innerhalb einer Funktion eine Exception auftritt, stoppt Python nicht einfach in dieser Funktion – es propagiert (wandert nach oben) durch den Call Stack, bis sie irgendwo behandelt wird oder das Programm beendet wird.

python
# Beispiel 1: Exception propagiert durch Funktionen
def calculate_average(numbers):
    total = sum(numbers)
    count = len(numbers)
    return total / count  # Könnte ZeroDivisionError auslösen
 
def process_scores(score_list):
    print("Processing scores...")
    avg = calculate_average(score_list)
    print(f"Average calculated: {avg}")
    return avg
 
def main():
    print("Program starting")
    scores = []  # Leere Liste
    result = process_scores(scores)
    print(f"Final result: {result}")
    print("Program ending")
 
main()

Output:

Program starting
Processing scores...
Traceback (most recent call last):
  File "example.py", line 18, in <module>
    main()
  File "example.py", line 14, in main
    result = process_scores(scores)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "example.py", line 9, in process_scores
    avg = calculate_average(score_list)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "example.py", line 4, in calculate_average
    return total / count
           ~~~~~~^~~~~~~
ZeroDivisionError: division by zero

Verfolgen wir, was passiert ist:

  1. main() begann mit der Ausführung und gab „Program starting“ aus
  2. main() rief process_scores() auf
  3. process_scores() gab „Processing scores...“ aus
  4. process_scores() rief calculate_average() auf
  5. calculate_average() versuchte, durch Null zu teilen
  6. Die Exception trat auf und propagierte nach oben:
    • calculate_average() stoppte sofort (gab keinen Wert zurück)
    • Die Kontrolle ging zurück zu process_scores(), aber nicht normal – die Exception propagierte weiter
    • process_scores() stoppte sofort (das print nach calculate_average() wurde nie ausgeführt)
    • Die Kontrolle ging zurück zu main(), aber wieder propagierte die Exception weiter
    • main() stoppte sofort (die prints nach process_scores() wurden nie ausgeführt)
  7. Das Programm wurde mit dem Traceback beendet

Keiner der Codes nach der Exception wurde in irgendeiner der Funktionen ausgeführt. Die Exception „blubberte nach oben“ durch alle Funktionsaufrufe, bis sie die oberste Ebene erreichte und das Programm beendete.

24.5) Debugging-Mindset: Fehler als Information behandeln, nicht als Scheitern

Eine der wichtigsten Fähigkeiten beim Programmieren ist nicht, perfekten Code zu schreiben – sondern zu lernen, effektiv mit unperfektem Code zu arbeiten. Jede Programmiererin und jeder Programmierer, unabhängig vom Erfahrungsniveau, schreibt Code, der Fehler produziert. Der Unterschied zwischen Programmierern, die kämpfen, und effektiven Programmierern liegt nicht darin, Fehler zu vermeiden, sondern darin, wie sie darauf reagieren.

24.5.1) Fehler sind kein Scheitern

Wenn Sie Programmieren lernen, ist es natürlich, frustriert zu sein, wenn Sie auf Fehler stoßen. Vielleicht haben Sie das Gefühl, etwas falsch gemacht zu haben oder dass Sie es nicht „verstehen“. Diese Denkweise ist kontraproduktiv und – noch wichtiger – ungenau.

Fehler sind kein Scheitern – sie sind Feedback.

Stellen Sie sich Fehler wie ein GPS vor, das Ihre Route neu berechnet. Wenn Sie eine Abzweigung verpassen, sagt das GPS nicht „Du hast versagt!“. Es sagt „Route wird neu berechnet“ und gibt Ihnen neue Anweisungen. Pythons Fehlermeldungen funktionieren genauso: Sie sagen Ihnen, dass der Weg, den Sie genommen haben, nicht funktioniert hat, und liefern Informationen, die Ihnen helfen, einen Weg zu finden, der funktioniert.

Betrachten Sie dieses einfache Beispiel:

python
# Erster Versuch, den Durchschnitt zu berechnen
def calculate_average(numbers):
    total = sum(numbers)
    average = total / len(numbers)
    return average
 
scores = []
result = calculate_average(scores)
print(f"Average: {result}")

Output:

Traceback (most recent call last):
  File "example.py", line 8, in <module>
    result = calculate_average(scores)
             ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "example.py", line 4, in calculate_average
    average = total / len(numbers)
              ~~~~~~^~~~~~~~~~~~~~
ZeroDivisionError: division by zero

Dieser Fehler sagt Ihnen nicht, dass Sie eine schlechte Programmiererin oder ein schlechter Programmierer sind. Er sagt Ihnen etwas Konkretes und Nützliches: „Sie haben versucht, durch Null zu teilen, was passiert, wenn die Liste leer ist. Sie müssen diesen Fall behandeln.“

Mit dieser Information können Sie Ihren Code verbessern:

python
# Verbesserte Version basierend auf dem Fehler-Feedback
def calculate_average(numbers):
    if len(numbers) == 0:
        return 0  # Oder return None, oder eine aussagekräftigere Fehlermeldung auslösen
    
    total = sum(numbers)
    average = total / len(numbers)
    return average
 
scores = []
result = calculate_average(scores)
print(f"Average: {result}")  # Output: Average: 0

Der Fehler hat Ihnen geholfen, besseren Code zu schreiben. Ohne diesen Fehler hätten Sie vielleicht nicht bemerkt, dass Ihre Funktion keine leeren Listen verarbeiten kann.

24.5.2) Jeder Fehler lehrt Sie etwas

Jeder Fehler, dem Sie begegnen, lehrt Sie etwas über Python, über Ihren Code oder über Programmierung im Allgemeinen. Schauen wir uns mehrere Beispiele an, was unterschiedliche Fehler uns beibringen:

Beispiel 1: Lernen über Typen

python
# Versuch, inkompatible Typen zu addieren
age = 25
message = "You are " + age + " years old"

Output:

TypeError: can only concatenate str (not "int") to str

Was das lehrt: Python hat strikte Typregeln. Sie können Strings und Zahlen bei der Verkettung nicht mischen. Dieser Fehler lehrt Sie etwas über Typkompatibilität und führt Sie an das Konzept der Typumwandlung heran.

Beispiel 2: Lernen über Datenstrukturen

python
# Versuch, auf ein Dictionary wie auf eine Liste zuzugreifen
student = {"name": "Alice", "age": 20}
first_value = student[0]

Output:

KeyError: 0

Was das lehrt: Dictionaries verwenden Schlüssel, keine numerischen Indizes. Dieser Fehler lehrt Sie den Unterschied zwischen Dictionaries und Listen und wie man korrekt auf Dictionary-Werte zugreift.

Beispiel 3: Lernen über Scope

python
# Versuch, eine Variable zu verwenden, bevor sie definiert ist
def greet():
    print(f"Hello, {name}!")
 
greet()
name = "Alice"

Output:

NameError: name 'name' is not defined

Was das lehrt: Variablen müssen definiert sein, bevor sie verwendet werden, und die Ausführungsreihenfolge ist wichtig. Dieser Fehler lehrt Sie etwas über Variablenscope und die Bedeutung der Initialisierung.

Jeder dieser Fehler liefert spezifische, umsetzbare Informationen, die Ihnen helfen, Python besser zu verstehen. Statt sie als Hindernisse zu sehen, betrachten Sie sie als Lernchancen.

24.5.3) Das Debugging-Mindset annehmen

Professionelle Programmierer verbringen einen beträchtlichen Teil ihrer Zeit mit Debugging. Das ist kein Zeichen von Schwäche – es ist ein zentraler Teil des Jobs. Die besten Programmierer sind nicht diejenigen, die nie Fehler machen; es sind diejenigen, die:

  1. Fehler erwarten: Sie wissen, dass Fehler passieren werden, und sind nicht überrascht oder entmutigt
  2. Fehler aufmerksam lesen: Sie ziehen maximale Informationen aus Fehlermeldungen
  3. Systematisch debuggen: Sie folgen einem logischen Prozess, statt zufällige Änderungen zu machen
  4. Aus Fehlern lernen: Sie nutzen jeden Fehler als Gelegenheit, Python besser zu verstehen
  5. Neugierig bleiben: Sie fragen „Warum ist das passiert?“ statt nur „Wie behebe ich das?“

Nein

Ja

Nein

Ja

Fehler tritt auf

Fehlermeldung
sorgfältig lesen

Verstehen, was
schiefgelaufen ist

Hypothese bilden
zur Ursache

Hypothese testen
mit Debug-Ausgabe

Hypothese
korrekt?

Fix implementieren

Lösung testen

Funktioniert es
korrekt?

Aus der Erfahrung
lernen

Mit Zuversicht
weitermachen

Denken Sie daran: Jeder Fehler ist eine Gelegenheit, etwas Neues über Python, über Programmierung oder über Problemlösung zu lernen. Nehmen Sie Fehler als wertvolles Feedback an, gehen Sie systematisch vor und feiern Sie Ihre Debugging-Erfolge. Dieses Mindset wird Sie auf Ihrer gesamten Programmierreise begleiten.


Fehler und Tracebacks zu verstehen, ist grundlegend, um ein effektiver Python-Programmierer zu werden. In diesem Kapitel haben wir gelernt, zwischen Syntaxfehlern (Probleme mit der Code-Struktur) und Laufzeit-Exceptions (Probleme während der Ausführung) zu unterscheiden, häufige Exception-Typen und ihre Bedeutung zu erkennen, detaillierte Tracebacks zu lesen und zu interpretieren, um die Ursache von Problemen zu finden, zu verstehen, wie Exceptions den Programmablauf verändern, indem sie den Call Stack nach oben propagieren, und ein Debugging-Mindset zu entwickeln, das Fehler als wertvolle Information und nicht als Scheitern betrachtet.

Diese Fähigkeiten bilden die Grundlage für das nächste Kapitel, in dem wir lernen werden, Exceptions elegant zu behandeln – mit try- und except-Blöcken –, sodass unsere Programme sich von Fehlern erholen und weiterlaufen können. Aber bevor wir Exceptions effektiv behandeln können, müssen wir sie gründlich verstehen – und genau das haben wir hier erreicht.

© 2025. Primesoft Co., Ltd.
support@primesoft.ai