Skip to content
Themen
Python
Python Try Except: How to Handle Exceptions the Right Way

Python Try Except: Ausnahmen richtig behandeln

Updated on

Ihr Python-Skript liest eine Datei, parst die Daten und sendet sie an eine API. Auf Ihrem Rechner funktioniert alles einwandfrei. Dann läuft es auf einem Server, wo der Dateipfad falsch ist, das JSON fehlerhaft ist oder das Netzwerk ausgefallen ist. Das Programm stürzt mit einem Traceback ab, und die gesamte Pipeline stoppt. Genau dieses Problem löst try/except. Anstatt Fehler Ihr Programm zum Absturz bringen zu lassen, fangen Sie sie ab, behandeln sie und lassen das Programm weiterlaufen.

📚

Was sind Ausnahmen in Python?

Eine Ausnahme ist ein Ereignis, das den normalen Programmablauf unterbricht. Wenn Python auf eine Operation stößt, die es nicht ausführen kann -- Division durch Null, Zugriff auf einen fehlenden Dictionary-Schlüssel, Öffnen einer nicht existierenden Datei -- erstellt es ein Ausnahmeobjekt und stoppt die Ausführung. Wenn nichts diese Ausnahme abfängt, wird das Programm beendet und ein Traceback ausgegeben.

# Dies bringt das Programm zum Absturz
result = 10 / 0

Ausgabe:

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

Ausnahmen unterscheiden sich von Syntaxfehlern. Ein Syntaxfehler bedeutet, dass Python Ihren Code überhaupt nicht parsen kann. Eine Ausnahme tritt während der Ausführung auf, nachdem der Code erfolgreich geparst wurde.

Grundlegende Try/Except-Syntax

Der try/except-Block ermöglicht es Ihnen, Code auszuprobieren, der fehlschlagen könnte, und zu definieren, was passieren soll, wenn er fehlschlägt.

try:
    result = 10 / 0
except ZeroDivisionError:
    print("Cannot divide by zero.")

Ausgabe:

Cannot divide by zero.

Das Programm stürzt nicht ab. Python führt den Code innerhalb von try aus. Wenn ZeroDivisionError auftritt, springt die Ausführung zum except-Block. Alles nach dem try/except wird normal fortgesetzt.

Sie können auch das Ausnahmeobjekt erfassen, um die Fehlermeldung zu inspizieren:

try:
    result = 10 / 0
except ZeroDivisionError as e:
    print(f"Error: {e}")

Ausgabe:

Error: division by zero

Bestimmte Ausnahmen abfangen

Python hat Dutzende von integrierten Ausnahmetypen. Das Abfangen des richtigen Typs macht Ihren Code präzise. Hier sind die häufigsten Ausnahmen, die Sie behandeln werden.

ValueError

Wird ausgelöst, wenn eine Funktion ein Argument mit dem richtigen Typ, aber einem ungeeigneten Wert erhält.

try:
    number = int("not_a_number")
except ValueError as e:
    print(f"Invalid value: {e}")

Ausgabe:

Invalid value: invalid literal for int() with base 10: 'not_a_number'

TypeError

Wird ausgelöst, wenn eine Operation auf ein Objekt des falschen Typs angewendet wird.

try:
    result = "hello" + 42
except TypeError as e:
    print(f"Type error: {e}")

Ausgabe:

Type error: can only concatenate str (not "int") to str

FileNotFoundError

Wird ausgelöst, wenn Sie versuchen, eine nicht existierende Datei zu öffnen.

try:
    with open("nonexistent_file.txt", "r") as f:
        content = f.read()
except FileNotFoundError:
    print("File not found. Check the file path.")

KeyError

Wird ausgelöst, wenn Sie auf einen Dictionary-Schlüssel zugreifen, der nicht existiert.

data = {"name": "Alice", "age": 30}
 
try:
    email = data["email"]
except KeyError as e:
    print(f"Missing key: {e}")

Ausgabe:

Missing key: 'email'

IndexError

Wird ausgelöst, wenn Sie auf einen Listenindex zugreifen, der außerhalb des Bereichs liegt.

items = [10, 20, 30]
 
try:
    value = items[5]
except IndexError:
    print("Index out of range.")

Mehrere Except-Blöcke

Sie können verschiedene Ausnahmetypen mit separaten except-Blöcken behandeln. Python prüft sie der Reihe nach und führt den ersten Treffer aus.

def parse_config(raw_value):
    try:
        parts = raw_value.split(":")
        key = parts[0]
        value = int(parts[1])
        return {key: value}
    except IndexError:
        print("Config format error: missing colon separator.")
    except ValueError:
        print("Config format error: value is not a number.")
    except AttributeError:
        print("Config format error: input is not a string.")
 
parse_config("timeout:30")    # Returns {'timeout': 30}
parse_config("timeout")       # Config format error: missing colon separator.
parse_config("timeout:abc")   # Config format error: value is not a number.
parse_config(12345)            # Config format error: input is not a string.

Sie können auch mehrere Ausnahmen in einem einzigen except-Block mit einem Tupel abfangen:

try:
    value = int(input("Enter a number: "))
    result = 100 / value
except (ValueError, ZeroDivisionError) as e:
    print(f"Invalid input: {e}")

Der Else-Block

Der else-Block wird nur ausgeführt, wenn keine Ausnahme im try-Block auftritt. Er trennt den "Erfolgsweg"-Code vom Fehlerbehandlungscode.

try:
    number = int("42")
except ValueError:
    print("That is not a valid number.")
else:
    print(f"Successfully parsed: {number}")

Ausgabe:

Successfully parsed: 42

Der else-Block ist nützlich, weil jede Ausnahme, die darin ausgelöst wird, nicht von den vorhergehenden except-Blöcken abgefangen wird. Dies verhindert das versehentliche Unterdrücken von Fehlern in Ihrem Erfolgspfad-Code.

filename = "data.txt"
 
try:
    f = open(filename, "r")
except FileNotFoundError:
    print(f"File '{filename}' does not exist.")
else:
    content = f.read()
    f.close()
    print(f"Read {len(content)} characters.")

Wenn open() fehlschlägt, behandelt der except-Block dies. Wenn open() erfolgreich ist, liest der else-Block die Datei. Jeder Fehler während f.read() wird hier nicht abgefangen -- er wird weitergegeben, was das korrekte Verhalten ist.

Der Finally-Block

Der finally-Block wird immer ausgeführt, unabhängig davon, ob eine Ausnahme aufgetreten ist oder nicht. Er ist der richtige Ort für Aufräumcode: Dateien schließen, Sperren freigeben, Datenbankverbindungen trennen.

f = None
try:
    f = open("data.txt", "r")
    content = f.read()
except FileNotFoundError:
    print("File not found.")
finally:
    if f is not None:
        f.close()
        print("File handle closed.")

Der finally-Block wird auch ausgeführt, wenn der try-Block einen Wert zurückgibt oder eine nicht behandelte Ausnahme auslöst.

def divide(a, b):
    try:
        return a / b
    except ZeroDivisionError:
        return None
    finally:
        print("Division attempted.")
 
result = divide(10, 3)
# Output: Division attempted.
# result = 3.3333333333333335
 
result = divide(10, 0)
# Output: Division attempted.
# result = None

Das vollständige Try/Except/Else/Finally-Muster

So funktionieren alle vier Blöcke zusammen:

import json
 
def load_config(filepath):
    """Load and parse a JSON config file."""
    f = None
    try:
        f = open(filepath, "r")
        data = json.load(f)
    except FileNotFoundError:
        print(f"Config file '{filepath}' not found.")
        return {}
    except json.JSONDecodeError as e:
        print(f"Invalid JSON in '{filepath}': {e}")
        return {}
    else:
        print(f"Config loaded successfully with {len(data)} keys.")
        return data
    finally:
        if f is not None:
            f.close()
            print("File handle released.")
 
config = load_config("settings.json")
BlockWann er ausgeführt wirdZweck
tryImmer (zuerst)Enthält den Code, der eine Ausnahme auslösen könnte
exceptNur wenn eine passende Ausnahme auftrittBehandelt den Fehler
elseNur wenn keine Ausnahme im try auftrittFührt die Erfolgslogik aus
finallyImmer (zuletzt)Aufräumcode, der in jedem Fall ausgeführt werden muss

Referenz häufiger integrierter Ausnahmen

Python enthält eine Hierarchie von integrierten Ausnahmen. Hier sind die, die Sie am häufigsten antreffen werden.

AusnahmeWann sie auftrittBeispiel
ExceptionBasisklasse für die meisten AusnahmenElternklasse aller nicht-systembeendenden Ausnahmen
ValueErrorFalscher Wert für den korrekten Typint("abc")
TypeErrorFalscher Typ für eine Operation"text" + 5
KeyErrorFehlender Dictionary-Schlüsseld["missing"]
IndexErrorListenindex außerhalb des Bereichs[1,2,3][10]
FileNotFoundErrorDatei existiert nichtopen("no.txt")
ZeroDivisionErrorDivision oder Modulo durch Null1 / 0
AttributeErrorObjekt hat das Attribut nichtNone.append(1)
ImportErrorModulimport schlägt fehlimport nonexistent
OSErrorBetriebssystem-Operation schlägt fehlFestplatte voll, Zugriff verweigert
StopIterationIterator hat keine weiteren Elementenext() bei erschöpftem Iterator
RuntimeErrorAllgemeiner LaufzeitfehlerSammelbecken für verschiedene Fehler

Alle diese erben von BaseException. In der Praxis sollten Sie Exception oder dessen Unterklassen abfangen, niemals direkt BaseException (welches SystemExit und KeyboardInterrupt einschließt).

Ausnahmen mit raise auslösen

Sie können Ausnahmen explizit auslösen, wenn Ihr Code ungültige Bedingungen erkennt. So erzwingen Sie Vorbedingungen und signalisieren Fehler an den Aufrufer.

def set_age(age):
    if not isinstance(age, int):
        raise TypeError(f"Age must be an integer, got {type(age).__name__}")
    if age < 0 or age > 150:
        raise ValueError(f"Age must be between 0 and 150, got {age}")
    return age
 
# Valid usage
print(set_age(25))  # 25
 
# Invalid usage
try:
    set_age(-5)
except ValueError as e:
    print(e)  # Age must be between 0 and 150, got -5
 
try:
    set_age("thirty")
except TypeError as e:
    print(e)  # Age must be an integer, got str

Sie können auch die aktuelle Ausnahme innerhalb eines except-Blocks mit raise ohne Argumente erneut auslösen. Dies ist nützlich, wenn Sie einen Fehler protokollieren und ihn dann weitergeben möchten.

import logging
 
try:
    result = 10 / 0
except ZeroDivisionError:
    logging.error("Division by zero encountered")
    raise  # re-raises the original ZeroDivisionError

Benutzerdefinierte Ausnahmeklassen

Für größere Projekte definieren Sie Ihre eigenen Ausnahmeklassen. Benutzerdefinierte Ausnahmen machen die Fehlerbehandlung lesbarer und ermöglichen es Aufrufern, bestimmte Fehlermodi abzufangen.

class ValidationError(Exception):
    """Raised when input data fails validation."""
    def __init__(self, field, message):
        self.field = field
        self.message = message
        super().__init__(f"Validation failed on '{field}': {message}")
 
class DatabaseConnectionError(Exception):
    """Raised when the database connection fails."""
    pass
 
# Usage
def validate_email(email):
    if "@" not in email:
        raise ValidationError("email", "Must contain @ symbol")
    if "." not in email.split("@")[1]:
        raise ValidationError("email", "Domain must contain a dot")
    return email
 
try:
    validate_email("userexample.com")
except ValidationError as e:
    print(e)          # Validation failed on 'email': Must contain @ symbol
    print(e.field)    # email
    print(e.message)  # Must contain @ symbol

Benutzerdefinierte Ausnahmen sollten von Exception erben, nicht von BaseException. Gruppieren Sie verwandte Ausnahmen unter einer gemeinsamen Basisklasse, damit Aufrufer breite oder enge Kategorien abfangen können:

class AppError(Exception):
    """Base exception for this application."""
    pass
 
class ConfigError(AppError):
    pass
 
class NetworkError(AppError):
    pass
 
# Caller can catch all app errors or specific ones
try:
    raise NetworkError("Connection timed out")
except AppError as e:
    print(f"Application error: {e}")

Best Practices für die Python-Ausnahmebehandlung

1. Fangen Sie niemals nackte Ausnahmen ab

Ein nacktes except: fängt alles ab, einschließlich KeyboardInterrupt und SystemExit. Dies verbirgt Fehler und macht das Debugging zum Albtraum.

# BAD - catches everything, hides real bugs
try:
    do_something()
except:
    pass
 
# GOOD - catches specific exceptions
try:
    do_something()
except ValueError as e:
    logging.warning(f"Invalid value: {e}")

Wenn Sie einen breiten Bereich abfangen müssen, verwenden Sie stattdessen except Exception. Dies lässt KeyboardInterrupt und SystemExit trotzdem weitergeben.

2. Halten Sie Try-Blöcke klein

Setzen Sie nur den Code in den try-Block, der die Ausnahme auslösen könnte. Große try-Blöcke machen es unklar, welche Zeile den Fehler verursacht hat.

# BAD - too much code in try
try:
    data = load_data()
    cleaned = clean_data(data)
    result = analyze(cleaned)
    save_results(result)
except Exception as e:
    print(f"Something failed: {e}")
 
# GOOD - narrow try blocks
data = load_data()
cleaned = clean_data(data)
 
try:
    result = analyze(cleaned)
except ValueError as e:
    print(f"Analysis failed: {e}")
    result = default_result()
 
save_results(result)

3. Protokollieren Sie Ausnahmen, unterdrücken Sie sie nicht

Ausnahmen mit pass zu verschlucken erzeugt unsichtbare Fehler. Protokollieren oder melden Sie den Fehler immer.

import logging
 
try:
    process_record(record)
except ValueError as e:
    logging.error(f"Failed to process record {record['id']}: {e}")

4. Verwenden Sie spezifische Ausnahmetypen

Fangen Sie den spezifischsten Ausnahmetyp ab. Dies verhindert das versehentliche Behandeln von Fehlern, die Sie nicht erwartet haben.

AnsatzFängt abRisikostufe
except:Alles einschließlich SystemExitSehr hoch
except Exception:Alle Standard-AusnahmenHoch
except ValueError:Nur ValueErrorNiedrig
except (ValueError, TypeError):Zwei spezifische TypenNiedrig

5. Räumen Sie Ressourcen in Finally auf oder verwenden Sie Kontextmanager

Für Datei-Handles, Datenbankverbindungen und Sperren verwenden Sie immer finally oder (noch besser) eine with-Anweisung.

# Prefer context managers for resource cleanup
with open("data.txt", "r") as f:
    content = f.read()
# File is automatically closed, even if an exception occurs

Praxisbeispiele

Lesen und Parsen einer JSON-Datei

import json
 
def read_json_config(filepath):
    """Read a JSON configuration file with proper error handling."""
    try:
        with open(filepath, "r") as f:
            config = json.load(f)
    except FileNotFoundError:
        print(f"Config file not found: {filepath}")
        return None
    except PermissionError:
        print(f"No permission to read: {filepath}")
        return None
    except json.JSONDecodeError as e:
        print(f"Invalid JSON at line {e.lineno}, column {e.colno}: {e.msg}")
        return None
    else:
        print(f"Loaded config with keys: {list(config.keys())}")
        return config
 
config = read_json_config("app_config.json")
if config:
    db_host = config.get("database_host", "localhost")

HTTP-API-Aufrufe durchführen

import urllib.request
import urllib.error
import json
 
def fetch_user(user_id):
    """Fetch user data from an API with retry logic."""
    url = f"https://jsonplaceholder.typicode.com/users/{user_id}"
    max_retries = 3
 
    for attempt in range(1, max_retries + 1):
        try:
            with urllib.request.urlopen(url, timeout=5) as response:
                data = json.loads(response.read().decode())
            return data
        except urllib.error.HTTPError as e:
            if e.code == 404:
                print(f"User {user_id} not found.")
                return None
            print(f"HTTP error {e.code} on attempt {attempt}")
        except urllib.error.URLError as e:
            print(f"Network error on attempt {attempt}: {e.reason}")
        except json.JSONDecodeError:
            print("API returned invalid JSON.")
            return None
 
    print(f"All {max_retries} attempts failed.")
    return None
 
user = fetch_user(1)
if user:
    print(f"Found user: {user['name']}")

CSV-Daten verarbeiten

import csv
 
def process_sales_data(filepath):
    """Process a CSV file with robust error handling."""
    results = []
 
    try:
        with open(filepath, "r", newline="") as f:
            reader = csv.DictReader(f)
            for row_num, row in enumerate(reader, start=2):
                try:
                    amount = float(row["amount"])
                    quantity = int(row["quantity"])
                    results.append({
                        "product": row["product"],
                        "total": amount * quantity,
                    })
                except KeyError as e:
                    print(f"Row {row_num}: Missing column {e}")
                except ValueError as e:
                    print(f"Row {row_num}: Invalid number - {e}")
    except FileNotFoundError:
        print(f"File not found: {filepath}")
    except PermissionError:
        print(f"Cannot read file: {filepath}")
 
    return results

Try/Except vs If/Else: Wann was verwenden

Python folgt dem EAFP-Prinzip: "Easier to Ask Forgiveness than Permission" (Es ist einfacher, um Vergebung zu bitten als um Erlaubnis). Dies steht im Gegensatz zum LBYL-Ansatz: "Look Before You Leap" (Schauen, bevor man springt).

# LBYL (Look Before You Leap) - using if/else
if "email" in user_data:
    email = user_data["email"]
else:
    email = "unknown"
 
# EAFP (Easier to Ask Forgiveness) - using try/except
try:
    email = user_data["email"]
except KeyError:
    email = "unknown"
Kriteriumif/else (LBYL)try/except (EAFP)
Am besten wennDie Prüfung günstig und der Fehler häufig istDer Fehler selten oder die Prüfung teuer ist
Leistung (kein Fehler)Etwas langsamer (zusätzliche Prüfung jedes Mal)Etwas schneller (kein Prüfungs-Overhead)
Leistung (Fehler tritt auf)GleichLangsamer (Ausnahmeerstellung hat Overhead)
Race ConditionsMöglich (Zustand kann sich zwischen Prüfung und Verwendung ändern)Keine (atomare Operation)
LesbarkeitKlar bei einfachen BedingungenBesser bei Operationen, die auf mehrere Arten fehlschlagen können
Dateioperationenif os.path.exists(path) -- Datei könnte zwischen Prüfung und Öffnen gelöscht werdentry: open(path) -- behandelt den tatsächlichen Fehler
Dictionary-Zugriffif key in dict -- einfach und schnelltry: dict[key] -- oder verwenden Sie einfach dict.get(key, default)

Verwenden Sie try/except wenn:

  • Der Fehlerfall selten ist (Ausnahmen sind für den "kein Fehler"-Pfad optimiert).
  • Die Prüfung selbst so teuer ist wie die Operation (z.B. prüfen, ob eine Datei existiert, dann öffnen).
  • Mehrere Dinge in einer Operationssequenz schiefgehen können.
  • Race Conditions ein Problem sind (besonders bei Dateien und Netzwerkressourcen).

Verwenden Sie if/else wenn:

  • Die Bedingung günstig zu prüfen ist und Fehler häufig sind.
  • Sie Benutzereingaben vor der Verarbeitung validieren.
  • Die Logik als Bedingung klarer lesbar ist.

Ausnahmen schneller debuggen mit RunCell

Wenn Sie in Jupyter-Notebooks arbeiten, können Ausnahmen Ihren Analyse-Workflow unterbrechen. Sie stoßen auf einen KeyError in Zeile 50.000 eines DataFrames, oder ein TypeError taucht drei Zellen tief in einer Pipeline auf. Die Ursache zu finden bedeutet, durch Tracebacks zu scrollen und Variablen manuell zu inspizieren.

RunCell (opens in a new tab) ist ein KI-Agent, der direkt in Jupyter läuft. Er liest den vollständigen Traceback, inspiziert die Variablen in Ihrem aktuellen Scope und schlägt eine Lösung im Kontext vor. So hilft es bei der Ausnahmebehandlung:

  • Traceback-Analyse. RunCell parst die Ausnahmekette und identifiziert, welche Variable oder Operation den Fehler verursacht hat, auch bei verschachtelten Funktionsaufrufen.
  • Lösungsvorschläge. Anstatt Stack Overflow zu durchsuchen, generiert RunCell eine korrigierte Code-Zelle, die Sie sofort ausführen können. Es weiß, ob ein try/except hinzugefügt, eine Typkonvertierung korrigiert oder ein fehlender Schlüssel behandelt werden muss.
  • Präventive Prüfungen. RunCell kann Ihren Code scannen und Operationen markieren, die wahrscheinlich Ausnahmen auslösen -- wie der Zugriff auf Dictionary-Schlüssel ohne .get() oder Division ohne Nullprüfung -- bevor Sie die Zelle ausführen.

Da RunCell in Ihrer bestehenden Jupyter-Umgebung arbeitet, hat es Zugriff auf Ihre tatsächlichen Daten und Variablen. Die Vorschläge sind spezifisch für Ihre Situation, keine generischen Ratschläge.

FAQ

Was ist der Unterschied zwischen try/except und try/catch in Python?

Python verwendet try/except, nicht try/catch. Die try/catch-Syntax gehört zu Sprachen wie Java, JavaScript und C++. In Python lautet das Schlüsselwort except. Die Funktionalität ist dieselbe: Sie versuchen Code, der fehlschlagen könnte, und definieren einen Handler für den Fehlerfall.

Kann ich mehrere except-Blöcke in Python verwenden?

Ja. Sie können so viele except-Blöcke verketten, wie Sie benötigen, wobei jeder einen anderen Ausnahmetyp abfängt. Python wertet sie der Reihe nach aus und führt den ersten passenden Block aus. Sie können auch mehrere Ausnahmen in einem Block mit einem Tupel abfangen: except (ValueError, TypeError) as e:.

Wann sollte ich else mit try/except verwenden?

Verwenden Sie den else-Block, wenn Sie Code haben, der nur ausgeführt werden soll, wenn der try-Block erfolgreich war. Der Hauptvorteil ist, dass Ausnahmen, die im else-Block ausgelöst werden, nicht von den vorhergehenden except-Blöcken abgefangen werden, was verhindert, dass Sie versehentlich nicht zusammenhängende Fehler unterdrücken.

Wird finally in Python immer ausgeführt?

Ja. Der finally-Block wird ausgeführt, unabhängig davon, ob der try-Block normal abgeschlossen wurde, eine behandelte Ausnahme ausgelöst hat oder eine unbehandelte Ausnahme ausgelöst hat. Er wird sogar ausgeführt, wenn der try- oder except-Block eine return-Anweisung enthält. Die einzigen Ausnahmen sind, wenn der Python-Prozess extern beendet wird oder os._exit() aufgerufen wird.

Wie erstelle ich benutzerdefinierte Ausnahmen in Python?

Erstellen Sie eine neue Klasse, die von Exception erbt. Sie können benutzerdefinierte Attribute hinzufügen und die __init__-Methode überschreiben. Zum Beispiel: class MyError(Exception): pass. Für komplexere Fälle fügen Sie Felder wie Fehlercodes oder Kontextdaten hinzu. Erben Sie immer von Exception, nicht von BaseException.

Fazit

Pythons try/except ist der Standardmechanismus zur Behandlung von Laufzeitfehlern. Es ermöglicht Ihnen, bestimmte Ausnahmen abzufangen, Aufräumcode auszuführen und Ihre Programme stabil zu halten, wenn etwas schiefgeht. Das vollständige Muster -- try/except/else/finally -- deckt jedes Szenario ab: die Operation versuchen, Fehler behandeln, Erfolgslogik ausführen und Ressourcen aufräumen.

Die Schlüsselprinzipien sind einfach. Fangen Sie spezifische Ausnahmen ab, nicht breite. Halten Sie Ihre try-Blöcke klein. Protokollieren oder melden Sie Fehler immer, anstatt sie zu unterdrücken. Verwenden Sie finally oder Kontextmanager zum Aufräumen. Lösen Sie in Ihrem eigenen Code aussagekräftige Ausnahmen mit klaren Fehlermeldungen aus.

Ob Sie Dateien lesen, API-Aufrufe machen oder Benutzereingaben verarbeiten -- eine korrekte Ausnahmebehandlung ist der Unterschied zwischen einem Skript, das um 2 Uhr morgens abstürzt, und einem, das den Fehler protokolliert und weiterläuft. Beginnen Sie mit den Grundlagen -- einem einfachen try/except um Code, der fehlschlagen könnte -- und bauen Sie bis zu benutzerdefinierten Ausnahmehierarchien auf, wenn Ihre Projekte wachsen.

📚