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 / 0Ausgabe:
Traceback (most recent call last):
File "example.py", line 2, in <module>
result = 10 / 0
ZeroDivisionError: division by zeroAusnahmen 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 zeroBestimmte 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 strFileNotFoundError
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: 42Der 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 = NoneDas 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")| Block | Wann er ausgeführt wird | Zweck |
|---|---|---|
try | Immer (zuerst) | Enthält den Code, der eine Ausnahme auslösen könnte |
except | Nur wenn eine passende Ausnahme auftritt | Behandelt den Fehler |
else | Nur wenn keine Ausnahme im try auftritt | Führt die Erfolgslogik aus |
finally | Immer (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.
| Ausnahme | Wann sie auftritt | Beispiel |
|---|---|---|
Exception | Basisklasse für die meisten Ausnahmen | Elternklasse aller nicht-systembeendenden Ausnahmen |
ValueError | Falscher Wert für den korrekten Typ | int("abc") |
TypeError | Falscher Typ für eine Operation | "text" + 5 |
KeyError | Fehlender Dictionary-Schlüssel | d["missing"] |
IndexError | Listenindex außerhalb des Bereichs | [1,2,3][10] |
FileNotFoundError | Datei existiert nicht | open("no.txt") |
ZeroDivisionError | Division oder Modulo durch Null | 1 / 0 |
AttributeError | Objekt hat das Attribut nicht | None.append(1) |
ImportError | Modulimport schlägt fehl | import nonexistent |
OSError | Betriebssystem-Operation schlägt fehl | Festplatte voll, Zugriff verweigert |
StopIteration | Iterator hat keine weiteren Elemente | next() bei erschöpftem Iterator |
RuntimeError | Allgemeiner Laufzeitfehler | Sammelbecken 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 strSie 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 ZeroDivisionErrorBenutzerdefinierte 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 @ symbolBenutzerdefinierte 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.
| Ansatz | Fängt ab | Risikostufe |
|---|---|---|
except: | Alles einschließlich SystemExit | Sehr hoch |
except Exception: | Alle Standard-Ausnahmen | Hoch |
except ValueError: | Nur ValueError | Niedrig |
except (ValueError, TypeError): | Zwei spezifische Typen | Niedrig |
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 occursPraxisbeispiele
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 resultsTry/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"| Kriterium | if/else (LBYL) | try/except (EAFP) |
|---|---|---|
| Am besten wenn | Die Prüfung günstig und der Fehler häufig ist | Der 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) | Gleich | Langsamer (Ausnahmeerstellung hat Overhead) |
| Race Conditions | Möglich (Zustand kann sich zwischen Prüfung und Verwendung ändern) | Keine (atomare Operation) |
| Lesbarkeit | Klar bei einfachen Bedingungen | Besser bei Operationen, die auf mehrere Arten fehlschlagen können |
| Dateioperationen | if os.path.exists(path) -- Datei könnte zwischen Prüfung und Öffnen gelöscht werden | try: open(path) -- behandelt den tatsächlichen Fehler |
| Dictionary-Zugriff | if key in dict -- einfach und schnell | try: 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/excepthinzugefü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.