Python ValueError: Ursachen, Behandlung, Auslösen, Logging und Best Practices

Python löst einen ValueError aus, wenn eine Funktion ein Argument mit dem erwarteten Datentyp erhält, dessen Wert jedoch für die jeweilige Operation ungeeignet ist. Diese Exception kann in zahlreichen Situationen auftreten, beispielsweise bei Typumwandlungen, mathematischen Berechnungen, beim Entpacken von Werten oder bei einer eigenen Eingabevalidierung. Deshalb gehört sie zu den häufigsten Exceptions im täglichen Umgang mit Python.

In diesem Tutorial erfährst du, wodurch ein ValueError ausgelöst wird, an welcher Stelle er sich innerhalb der Python-Exception-Hierarchie befindet und wie du ihn korrekt behandelst, selbst auslöst und protokollierst. Anhand praktischer Beispiele werden fehlgeschlagene Konvertierungen, Fehler beim Unpacking, die Validierung von Eingaben sowie ein für Produktivumgebungen geeignetes Logging behandelt. Abschließend folgen Best Practices, die sich allgemein auf Python-Projekte übertragen lassen.

Die wichtigsten Erkenntnisse

  • Ein ValueError entsteht, wenn eine Funktion zwar den richtigen Datentyp erhält, der konkrete Wert jedoch nicht akzeptiert werden kann, beispielsweise wenn "hello" an int() übergeben wird.
  • ValueError erbt direkt von Exception. Die einzige integrierte Unterklasse ist UnicodeError.
  • Verwende nach Möglichkeit except ValueError: anstelle von except Exception: oder einem alleinstehenden except:, damit andere Fehler nicht unbeabsichtigt verborgen werden.
  • Wenn du in eigenen Funktionen einen ValueError auslöst, sollte die Fehlermeldung aussagekräftig sein und den tatsächlich problematischen Wert enthalten.
  • Mit raise ValueError(...) from e kannst du Exceptions miteinander verketten und den ursprünglichen Traceback für die Fehlersuche erhalten.
  • In Produktivumgebungen eignet sich logging.exception() besser als print(), wenn sowohl die Fehlermeldung als auch der vollständige Stack Trace aufgezeichnet werden sollen.
  • Python 3.11 führte ExceptionGroup und die Syntax except* ein, um mehrere gleichzeitig auftretende Exceptions beispielsweise in asynchronen Abläufen zu behandeln. Die normale Behandlung eines ValueError hat sich dadurch nicht verändert.
  • Python-Anwendungen können aus einem Git-Repository über eine verwaltete Anwendungsplattform bereitgestellt werden, sodass die jeweilige Hosting-Plattform Aufgaben wie die Skalierung übernehmen kann.

Voraussetzungen

Um diesem Tutorial folgen zu können, benötigst du:

  • Python 3.7 oder eine neuere Version auf einem lokalen Computer oder Server.
  • Grundkenntnisse der Python-Syntax, einschließlich Funktionen, Imports und dem Ausführen von Skripten über die Kommandozeile.

Was ist ein Python ValueError?

Ein ValueError wird ausgelöst, wenn eine Funktion einen Wert mit dem richtigen Datentyp erhält, dieser konkrete Wert jedoch für die gewünschte Operation nicht zulässig ist. Ein typisches Beispiel ist der Versuch, eine nicht numerische Zeichenkette mit int() in eine Ganzzahl umzuwandeln.


try:
number = int(„hello“)
except ValueError as e:
print(f“ValueError abgefangen: {e}“)

Die Ausgabe lautet:


ValueError abgefangen: invalid literal for int() with base 10: ‚hello‘

Der Wert "hello" besitzt mit str einen für int() grundsätzlich passenden Datentyp. Der enthaltene Text stellt jedoch keine gültige Ganzzahl dar. Genau diese Unterscheidung zwischen einem falschen Datentyp und einem ungeeigneten Wert trennt ValueError von TypeError.

Wo ValueError in der Python-Exception-Hierarchie eingeordnet ist

Wenn du weißt, an welcher Stelle sich ValueError innerhalb der Exception-Hierarchie befindet, kannst du präzisere Fehlerbehandlungen schreiben. Sämtliche Python-Exceptions leiten sich von BaseException ab. Die meisten für Anwendungen relevanten Exceptions erben von Exception, das wiederum direkt von BaseException abgeleitet ist.

Die folgende vereinfachte Darstellung zeigt vor allem Exception-Typen, die häufig gemeinsam mit ValueError auftreten:


BaseException
└── Exception
├── ArithmeticError
│ └── ZeroDivisionError, OverflowError, FloatingPointError
├── LookupError
│ └── IndexError, KeyError
├── TypeError
├── ValueError
│ └── UnicodeError
│ └── UnicodeDecodeError, UnicodeEncodeError, UnicodeTranslateError
└── (viele weitere)

ValueError ist direkt von Exception abgeleitet. Die einzige integrierte Unterklasse ist UnicodeError, unter der Fehler beim Codieren und Decodieren von Unicode zusammengefasst werden. Ein Handler wie except ValueError: fängt daher sowohl ValueError selbst als auch dessen Unterklassen ab, einschließlich der UnicodeError-Familie. Andere Exceptions wie KeyError liegen dagegen in separaten Zweigen, beispielsweise unter LookupError.

Die vollständige Struktur ist in der Dokumentation zu den integrierten Python-Exceptions beschrieben.

ValueError vs. TypeError vs. AttributeError: die wichtigsten Unterschiede

ValueError, TypeError und AttributeError werden häufig miteinander verwechselt. Die folgende Tabelle zeigt, wodurch sie sich unterscheiden.

Exception Wann sie ausgelöst wird Beispiel
ValueError Der Datentyp stimmt, der konkrete Wert ist jedoch ungeeignet. int("abc"), math.sqrt(-1)
TypeError Der übergebene Datentyp ist grundsätzlich falsch. "hello" + 5, len(42)
AttributeError Ein angefordertes Attribut oder eine Methode ist auf dem Objekt nicht vorhanden. "hello".push("x")

Als praktische Faustregel gilt: Wird eine Zeichenkette an einer Stelle übergeben, an der eine Zeichenkette grundsätzlich erlaubt ist, deren Inhalt aber ungültig ist, handelt es sich häufig um einen ValueError. Wird dagegen beispielsweise eine Zeichenkette übergeben, obwohl eine Ganzzahl erwartet wird, entsteht üblicherweise ein TypeError.

Häufige Ursachen für ValueError in Python

In den folgenden Abschnitten werden typische Situationen beschrieben, in denen Python einen ValueError auslöst.

Fehler bei Typumwandlungen mit int(), float() und complex()

Eine der häufigsten Ursachen für einen ValueError ist der Versuch, eine Zeichenkette umzuwandeln, die keine gültige numerische Darstellung enthält.


try:
int(„hello“)
except ValueError as e:
print(f“int(): {e}“)

try:
float(„keine Zahl“)
except ValueError as e:
print(f“float(): {e}“)

try:
complex(„abc“)
except ValueError as e:
print(f“complex(): {e}“)

Die Ausgabe lautet:


int(): invalid literal for int() with base 10: ‚hello‘
float(): could not convert string to float: ‚keine Zahl‘
complex(): complex() arg is a malformed string

Unpacking-Fehler durch zu viele oder zu wenige Werte

Python löst ebenfalls einen ValueError aus, wenn die Anzahl der Werte innerhalb eines Iterables nicht mit der Anzahl der Variablen auf der linken Seite einer Zuweisung übereinstimmt.

try:
a, b = 1, 2, 3
except ValueError as e:
print(f“Zu viele: {e}“)

try:
x, y, z = [1, 2]
except ValueError as e:
print(f“Zu wenige: {e}“)

Die Ausgabe lautet:


Zu viele: too many values to unpack (expected 2)
Zu wenige: not enough values to unpack (expected 3, got 2)

Mathematische Bereichsfehler mit math.sqrt() und math.log()

Das Modul math erzeugt einen ValueError, wenn ein übergebener Wert außerhalb des mathematisch gültigen Definitionsbereichs einer Operation liegt.


import math

try:
math.sqrt(-1)
except ValueError as e:
print(f“sqrt: {e}“)

try:
math.log(-1)
except ValueError as e:
print(f“log: {e}“)

Die Ausgabe lautet:


sqrt: math domain error
log: math domain error

math.sqrt(-1) führt zu einem ValueError, anstatt eine komplexe Zahl zurückzugeben, da das Modul math ausschließlich mit reellen Zahlen arbeitet. Werden Berechnungen mit komplexen Zahlen benötigt, kann stattdessen das Modul cmath verwendet werden.

Ungültige Literale bei int() mit einer Zahlenbasis

Die Funktion int() unterstützt ein optionales zweites Argument, mit dem die verwendete Zahlenbasis festgelegt wird. Passt eine Zeichenkette nicht zu dieser Basis, löst Python einen ValueError aus.


try:
result = int(„FF“, 16)
print(f“int(‚FF‘, 16) = {result}“)

int(„FF“, 10)
except ValueError as e:
print(f“int(‚FF‘, 10): {e}“)

try:
int(„2″, 2)
except ValueError as e:
print(f“int(‚2‘, 2): {e}“)

Die Ausgabe lautet:


int(‚FF‘, 16) = 255
int(‚FF‘, 10): invalid literal for int() with base 10: ‚FF‘
int(‚2‘, 2): invalid literal for int() with base 2: ‚2‘

'FF' ist ein gültiger hexadezimaler Wert zur Basis 16, jedoch kein gültiges Literal zur Basis 10. Entsprechend ist '2' keine zulässige Binärziffer, da bei der Basis 2 ausschließlich 0 und 1 verwendet werden können.

ValueError mit try und except behandeln

Nachdem die häufigsten Ursachen eines ValueError betrachtet wurden, geht es im nächsten Schritt darum, diese Exceptions korrekt zu behandeln.

Grundlegendes try/except-Muster für ValueError

Das übliche Vorgehen besteht darin, die möglicherweise fehlschlagende Operation innerhalb eines try-Blocks auszuführen und den Fehler anschließend in einem except ValueError:-Block zu behandeln.

import math

try:
x = int(input(„Bitte gib eine positive Zahl ein: „))
result = math.sqrt(x)
print(f“Die Quadratwurzel von {x} ist {result}“)
except ValueError:
print(„Ungültige Eingabe. Bitte gib eine positive Ganzzahl ein.“)

Wenn du auf die ursprüngliche Fehlermeldung zugreifen möchtest, kannst du die Exception mithilfe von as einer Variablen zuweisen.


try:
number = int(input(„Gib eine Zahl ein: „))
except ValueError as e:
print(f“Die Eingabe konnte nicht in eine Ganzzahl umgewandelt werden: {e}“)

Mehrere Exceptions in einem Block abfangen

Wenn verschiedene Exception-Typen auftreten können und alle auf dieselbe Weise behandelt werden sollen, lassen sich die entsprechenden Klassen als Tupel in einer einzelnen except-Klausel angeben.


import math

try:
x = int(input(„Gib eine positive Zahl ein: „))
result = math.sqrt(x)
print(f“Die Quadratwurzel von {x} ist {result}“)
except (ValueError, TypeError) as e:
print(f“Ungültige Eingabe: {e}“)

Wenn die Fehlerbehandlung für beide Exception-Typen identisch ist, ist dieses Vorgehen übersichtlicher als mehrere separate except-Blöcke.

else und finally zusammen mit try/except verwenden

Ein else-Block wird nur ausgeführt, wenn der zugehörige try-Block ohne Exception abgeschlossen wurde. Ein finally-Block wird dagegen immer ausgeführt, unabhängig davon, ob zuvor ein Fehler aufgetreten ist. Beide Bereiche sind optional.


try:
number = int(input(„Gib eine Zahl ein: „))
except ValueError:
print(„Das ist keine gültige Ganzzahl.“)
else:
print(f“Erfolgreich verarbeitet: {number}“)
finally:
print(„Die Verarbeitung der Eingabe ist abgeschlossen.“)

else eignet sich für Code, der ausschließlich nach einer erfolgreichen Ausführung laufen soll. finally ist dagegen für Aufräumarbeiten gedacht, beispielsweise zum Schließen von Dateien oder Datenbankverbindungen.

except ValueError, except Exception und bare except im Vergleich

Die Wahl des richtigen Exception-Bereichs ist wichtig. Zu allgemein formulierte Handler können Fehler verdecken, die mit dem eigentlich erwarteten Problem nichts zu tun haben.

Muster Was abgefangen wird Wann es sinnvoll ist
except ValueError: Nur ValueError und dessen Unterklassen Wenn genau bekannt ist, welcher Fehler auftreten kann
except Exception: Alle Exceptions, die nicht zum Beenden des Systems dienen Allgemeine Fehlergrenzen auf oberster Ebene oder zentrale Logging-Handler
Alleinstehendes except: Alles, einschließlich SystemExit und KeyboardInterrupt Fast nie

Ein alleinstehendes except: kann auch Signale wie KeyboardInterrupt abfangen. Dadurch kann beispielsweise verhindert werden, dass ein Benutzer das Programm mit Strg+C beendet. Bevorzuge deshalb except ValueError: oder verwende höchstens dann except Exception:, wenn tatsächlich eine breitere Fehlergrenze erforderlich ist.

ValueError im eigenen Python-Code auslösen

Eigene Funktionen müssen manchmal signalisieren, dass ein Argument oder eine Eingabe ungültig ist, obwohl der Datentyp grundsätzlich stimmt. In solchen Fällen macht ein selbst ausgelöster ValueError deutlich, dass der konkrete Wert die Ursache des Problems ist.

ValueError mit einer eigenen Fehlermeldung auslösen

Mit der Anweisung raise kannst du einen ValueError selbst erzeugen. Die dazugehörige Meldung sollte möglichst genau erklären, warum der Wert ungültig ist und welcher konkrete Wert den Fehler verursacht hat.

def set_discount(percent):
if percent < 0 or percent > 100:
raise ValueError(f“Der Rabatt muss zwischen 0 und 100 liegen, erhalten: {percent}“)
return percent

try:
set_discount(150)
except ValueError as e:
print(e)

print(set_discount(20))

Die Ausgabe lautet:


Der Rabatt muss zwischen 0 und 100 liegen, erhalten: 150
20

ValueError zur Eingabevalidierung innerhalb einer Funktion verwenden

Ein zuverlässiges Muster besteht darin, Argumente direkt zu Beginn einer Funktion zu prüfen, bevor die eigentliche Verarbeitung startet. Dadurch bleibt der reguläre Erfolgsfall übersichtlich, während ungültige Eingaben schnell erkannt werden können.


import math

def calculate_square_root(number):
if not isinstance(number, (int, float)):
raise TypeError(f“Eine Zahl wurde erwartet, erhalten: {type(number).__name__}“)
if number < 0:
raise ValueError(f“Die Quadratwurzel einer negativen Zahl kann nicht berechnet werden: {number}“)
return math.sqrt(number)

print(calculate_square_root(25))

try:
calculate_square_root(-4)
except ValueError as e:
print(e)

try:
calculate_square_root(„25“)
except TypeError as e:
print(e)

Die Ausgabe lautet:


5.0
Die Quadratwurzel einer negativen Zahl kann nicht berechnet werden: -4
Eine Zahl wurde erwartet, erhalten: str

Die Funktion löst einen TypeError aus, wenn der Datentyp falsch ist. Ein ValueError wird dagegen verwendet, wenn der Typ stimmt, der Wert aber außerhalb des zulässigen mathematischen Bereichs liegt. Durch diese Trennung können aufrufende Funktionen beide Fehlerarten bei Bedarf unterschiedlich behandeln.

Exception Chaining mit raise … from

Wenn eine Exception auf niedriger Ebene abgefangen und anschließend durch eine verständlichere Exception ersetzt wird, kann raise ... from verwendet werden, um die ursprüngliche Ursache als Kontext zu erhalten. Python zeigt anschließend beide Exceptions im Traceback an und kennzeichnet die ursprüngliche Exception als direkte Ursache. Dadurch wird die Fehlersuche deutlich erleichtert.

def parse_user_id(raw_input):
try:
user_id = int(raw_input)
except ValueError as e:
raise ValueError(
f“Ungültige Benutzer-ID ‚{raw_input}‘: Es muss eine Ganzzahl angegeben werden“
) from e
if user_id <= 0:
raise ValueError(f“Die Benutzer-ID muss eine positive Ganzzahl sein, erhalten: {user_id}“)
return user_id

try:
parse_user_id(„abc“)
except ValueError as e:
print(f“Fehler: {e}“)
print(f“Verursacht durch: {e.__cause__}“)

print(parse_user_id(„42“))

Die Ausgabe lautet:


Fehler: Ungültige Benutzer-ID ‚abc‘: Es muss eine Ganzzahl angegeben werden
Verursacht durch: invalid literal for int() with base 10: ‚abc‘
42

Python ValueError in praktischen Anwendungsszenarien

In realen Anwendungen tritt ValueError regelmäßig bei der Validierung von Benutzereingaben oder bei der Verarbeitung von Daten aus Dateien, APIs und anderen externen Quellen auf. Die folgenden Beispiele zeigen robuste Muster für solche alltäglichen Situationen.

Validierung von Benutzereingaben

Eine robuste Eingabeschleife beendet das Programm nicht sofort nach einer fehlerhaften Eingabe, sondern fragt so lange nach einem Wert, bis eine gültige Eingabe vorliegt.

def get_positive_integer(prompt):
while True:
try:
value = int(input(prompt))
if value <= 0:
raise ValueError(f“Eine positive Ganzzahl wurde erwartet, erhalten: {value}“)
return value
except ValueError as e:
print(f“Ungültige Eingabe: {e}. Bitte erneut versuchen.“)

quantity = get_positive_integer(„Menge eingeben: „)
print(f“Du hast {quantity} Artikel bestellt.“)

Die Schleife behandelt beide möglichen Formen eines ValueError: zum einen die von int() ausgelöste Exception bei nicht numerischen Eingaben und zum anderen den ausdrücklich erzeugten Fehler bei null oder negativen Zahlen. In beiden Fällen wird erneut nach einer Eingabe gefragt.

Daten aus Dateien oder APIs einlesen und konvertieren

Daten aus externen Quellen werden häufig als Zeichenketten geliefert und müssen anschließend in passende Datentypen umgewandelt werden. Fehlende oder ungültig formatierte Daten sollten dabei kontrolliert behandelt werden.

def extract_price(api_response):
try:
price = float(api_response[„price“])
except (ValueError, KeyError) as e:
raise ValueError(f“Der Preis konnte nicht aus der Antwort gelesen werden: {e}“) from e
if price < 0:
raise ValueError(f“Der Preis darf nicht negativ sein, erhalten: {price}“)
return price

print(extract_price({„price“: „19.99“}))

try:
extract_price({„price“: „N/A“})
except ValueError as e:
print(e)

Die Ausgabe lautet:


19.99
Der Preis konnte nicht aus der Antwort gelesen werden: could not convert string to float: ‚N/A‘

Wenn api_response["price"] fehlt oder eine Zeichenkette enthält, die nicht als Zahl interpretiert werden kann, erzeugt die Funktion einen aussagekräftigen ValueError. Gleichzeitig bleibt die ursprüngliche Exception auf niedriger Ebene als Ursache erhalten.

ValueError für die Validierung von Klassen und Methoden einsetzen

Eine Prüfung der Werte direkt in __init__ verhindert, dass überhaupt ein Objekt in einem ungültigen Zustand erzeugt wird. Dadurch lassen sich spätere Laufzeitfehler vermeiden, die ansonsten deutlich schwieriger zu analysieren sein könnten.

class Rectangle:
def __init__(self, width, height):
if width <= 0:
raise ValueError(f“Die Breite muss positiv sein, erhalten: {width}“)
if height <= 0:
raise ValueError(f“Die Höhe muss positiv sein, erhalten: {height}“)
self.width = width
self.height = height

def area(self):
return self.width * self.height

r = Rectangle(10, 5)
print(r.area())

try:
r = Rectangle(-3, 5)
except ValueError as e:
print(e)

Die Ausgabe lautet:


50
Die Breite muss positiv sein, erhalten: -3

ValueError-Exceptions in Produktivumgebungen protokollieren

Während der Entwicklung kann print() ausreichen, um Fehlermeldungen sichtbar zu machen. Für Produktivumgebungen ist dieses Vorgehen jedoch meist nicht ausreichend. Das standardmäßig verfügbare Python-Modul logging bietet Kontrolle über Prioritätsstufen, Ausgabeziele und Nachrichtenformate, ohne dass die grundsätzliche Exception-Behandlung verändert werden muss. Eine allgemeine Einführung dazu findet sich in der Python-Dokumentation und in Tutorials zum Logging.

Das logging-Modul anstelle von print() verwenden

logging.error() zeichnet eine Meldung mit der Prioritätsstufe Fehler auf. Je nach Konfiguration können beispielsweise Zeitstempel und Severity-Level ausgegeben werden. Die Meldung wird an den jeweils eingerichteten Logging-Handler weitergegeben.

import logging

logging.basicConfig(
level=logging.ERROR,
format=“%(asctime)s %(levelname)s %(message)s“
)

def parse_quantity(value):
try:
return int(value)
except ValueError:
logging.error(„Die Menge konnte nicht aus dem Wert gelesen werden: %r“, value)
return None

result = parse_quantity(„abc“)
print(f“Ergebnis: {result}“)

Die Ausgabe lautet:


2024-01-15 10:23:45,123 ERROR Die Menge konnte nicht aus dem Wert gelesen werden: ‚abc‘
Ergebnis: None

Stack Traces mit logging.exception() erfassen

logging.exception() funktioniert ähnlich wie logging.error(), ergänzt die Meldung jedoch automatisch um den vollständigen Stack Trace. Die Funktion eignet sich innerhalb eines except-Blocks, wenn im Log nachvollziehbar sein soll, an welcher Stelle und aus welchem Grund ein Fehler entstanden ist.

import logging

logging.basicConfig(
level=logging.DEBUG,
format=“%(asctime)s %(levelname)s %(message)s“
)

def process_order(raw_quantity):
try:
quantity = int(raw_quantity)
print(f“Bestellung mit {quantity} Artikel(n) wird verarbeitet“)
except ValueError:
logging.exception(„Ungültige Mengenangabe erhalten: %r“, raw_quantity)

process_order(„two“)

Die Ausgabe lautet:


2024-01-15 10:23:45,123 ERROR Ungültige Mengenangabe erhalten: ‚two‘
Traceback (most recent call last):
File „example.py“, line 11, in process_order
quantity = int(raw_quantity)
ValueError: invalid literal for int() with base 10: ‚two‘

Beim Aufruf von process_order("two") wird zunächst die Fehlermeldung und anschließend der vollständige Traceback protokolliert. Dadurch lässt sich die fehlerverursachende Zeile auch innerhalb von Produktiv-Logs leichter identifizieren.

Best Practices für die Python-Exception-Behandlung

Eine sorgfältige Exception-Behandlung ist ein wichtiger Bestandteil stabiler Python-Anwendungen. Die folgenden Best Practices tragen dazu bei, Code sicherer, verständlicher und leichter zu debuggen zu gestalten, insbesondere bei Fehlern im Zusammenhang mit ungültigen Werten.

Spezifische Exceptions statt aller Exceptions abfangen

Fange möglichst immer den spezifischsten Exception-Typ ab, der zur erwarteten Fehlersituation passt. Ein allgemeines Exception oder ein alleinstehendes except: kann Fehler verdecken, die an dieser Stelle eigentlich gar nicht behandelt werden sollten.

# Empfohlen
try:
value = int(user_input)
except ValueError:
print(„Bitte gib eine gültige Ganzzahl ein.“)

# Vermeiden
try:
value = int(user_input)
except Exception:
print(„Ein Fehler ist aufgetreten.“)

Immer aussagekräftige Fehlermeldungen verwenden

Unabhängig davon, ob ein ValueError ausgelöst oder protokolliert wird, sollte die Meldung genügend Kontext liefern, damit die Ursache des Problems verstanden und behoben werden kann, ohne zunächst den gesamten Quellcode untersuchen zu müssen.

# Wenig hilfreich
raise ValueError(„Ungültige Eingabe“)

# Deutlich hilfreicher
raise ValueError(f“Für ‚quantity‘ wurde eine positive Ganzzahl erwartet, erhalten: {quantity!r}“)

Exceptions nicht stillschweigend unterdrücken

Eine Exception sollte nicht abgefangen und anschließend vollständig ignoriert werden. Das lautlose Unterdrücken von Fehlern mit pass gehört zu den häufigen Ursachen für schwer nachvollziehbare Probleme in Python-Anwendungen.

# Vermeiden
try:
result = int(user_input)
except ValueError:
pass

# Stattdessen
try:
result = int(user_input)
except ValueError as e:
logging.error(„Die Benutzereingabe konnte nicht verarbeitet werden: %s“, e)
result = None

Python 3.11+: ExceptionGroup und except*-Syntax

Mit Python 3.11 wurden ExceptionGroup und die Syntax except* über PEP 654 eingeführt. Diese Funktionalität richtet sich insbesondere an parallelen und asynchronen Code, bei dem mehrere voneinander unabhängige Exceptions gleichzeitig auftreten können. Die reguläre Behandlung mit try und except ValueError bleibt davon vollständig unberührt.

# Nur Python 3.11+
def validate_form(data):
errors = []
if not data.get(„name“):
errors.append(ValueError(„Ein Name ist erforderlich“))
if not isinstance(data.get(„age“), int):
errors.append(ValueError(„Das Alter muss eine Ganzzahl sein“))
if errors:
raise ExceptionGroup(„Die Formularvalidierung ist fehlgeschlagen“, errors)

try:
validate_form({„name“: „“, „age“: „keine-zahl“})
except* ValueError as eg:
for exc in eg.exceptions:
print(f“Validierungsfehler: {exc}“)

Die Ausgabe lautet:


Validierungsfehler: Ein Name ist erforderlich
Validierungsfehler: Das Alter muss eine Ganzzahl sein

Wird nur ein einzelner Exception-Typ abgefangen, sind bei except* keine Klammern notwendig, beispielsweise except* ValueError:. Klammern werden nur benötigt, wenn mehrere Exception-Typen als Tupel angegeben werden, zum Beispiel except* (ValueError, TypeError):. In Python 3.10 und älteren Versionen steht diese Syntax nicht zur Verfügung.

Häufige Fragen zu Python ValueError

1. Was ist der Unterschied zwischen except ValueError und except Exception in Python?

except ValueError: fängt ausschließlich ValueError und davon abgeleitete Unterklassen ab. except Exception: behandelt dagegen alle Exceptions, die nicht unmittelbar zum Beenden des Systems dienen. except ValueError: ist deshalb wesentlich präziser und reduziert das Risiko, unbeabsichtigt andere Programmfehler zu verdecken.

2. Wann sollte ich in eigenen Python-Funktionen raise ValueError verwenden?

Verwende raise ValueError, wenn eine Funktion ein Argument mit dem richtigen Datentyp erhält, der konkrete Wert jedoch nicht akzeptabel ist. Beispiele sind eine negative Zahl für eine Funktion, die eine positive Ganzzahl verlangt, oder eine Zeichenkette, deren Inhalt nicht dem erwarteten Format entspricht.

3. Wie kann ich ValueError abfangen und trotzdem die vollständige Fehlermeldung sehen?

Mit except ValueError as e: kannst du die Exception einer Variablen zuweisen. Anschließend lässt sich beispielsweise str(e) verwenden oder e an eine Logging-Funktion übergeben. Soll zusätzlich der vollständige Stack Trace gespeichert werden, kannst du innerhalb des except-Blocks logging.exception() verwenden.

4. Kann ich ValueError und TypeError gleichzeitig abfangen?

Ja. Gib beide Exception-Klassen als Tupel innerhalb der except-Klausel an: except (ValueError, TypeError):. Dadurch werden beide Exception-Typen im selben Block behandelt.

5. Was verursacht einen „Too Many Values to Unpack“-ValueError in Python?

Dieser Fehler entsteht, wenn die Anzahl der Variablen auf der linken Seite einer Zuweisung nicht mit der Anzahl der Elemente im Iterable übereinstimmt. Beispielsweise löst a, b = 1, 2, 3 den Fehler ValueError: too many values to unpack (expected 2) aus.

6. Wie kann ich einen ValueError protokollieren, anstatt ihn mit print() auszugeben?

Verwende innerhalb des except-Blocks logging.exception("deine Meldung"). Dadurch werden sowohl die Fehlermeldung als auch der vollständige Stack Trace an den konfigurierten Logging-Handler übermittelt, während die weitere Programmlogik entsprechend der implementierten Fehlerbehandlung fortgesetzt werden kann.

7. Was ist Exception Chaining und wann sollte es bei ValueError verwendet werden?

Beim Exception Chaining wird beispielsweise raise ValueError("Meldung") from original_exception verwendet, um die ursprünglich aufgetretene Exception als Ursache beizubehalten. Dieses Vorgehen eignet sich besonders dann, wenn nach dem Abfangen eines technischen Fehlers auf niedriger Ebene ein verständlicherer ValueError ausgelöst werden soll. Der Traceback enthält dadurch weiterhin die vollständige Ursachekette und erleichtert das Debugging.

8. Verändert Python 3.11 die Behandlung von ValueError?

Python 3.11 führte ExceptionGroup und except* ein, um mehrere gleichzeitig auftretende Exceptions behandeln zu können. Die klassische Behandlung von ValueError mit try und except ValueError wurde dadurch nicht verändert. Die neue Syntax ist nur relevant, wenn mit ExceptionGroup-Objekten gearbeitet wird, die hauptsächlich in asynchronen oder parallelen Programmabläufen vorkommen.

Fazit

Du weißt nun, wodurch ein ValueError ausgelöst wird, an welcher Stelle er sich innerhalb der Python-Exception-Hierarchie befindet und wie du ihn gezielt mit try und except behandelst. Außerdem hast du gesehen, wie sich ein ValueError mit aussagekräftigen Meldungen in eigenen Funktionen auslösen lässt, wie Exception Chaining den ursprünglichen Fehlerkontext bewahrt und wie print() durch logging.exception() ersetzt werden kann, um in Produktivumgebungen vollständige Stack Traces zu erfassen. Spezifische Exceptions abzufangen, verständliche Fehlermeldungen bereitzustellen und Fehler nicht stillschweigend zu unterdrücken, kann den Aufwand bei der Fehlersuche deutlich reduzieren, insbesondere wenn Python-Projekte umfangreicher werden.

Quelle: digitalocean.com

Jetzt 200€ Guthaben sichern

Registrieren Sie sich jetzt in unserer ccloud³ und erhalten Sie 200€ Startguthaben für Ihr Projekt.

Das könnte Sie auch interessieren:

Moderne Hosting Services mit Cloud Server, Managed Server und skalierbarem Cloud Hosting für professionelle IT-Infrastrukturen

Weaviate vs. OpenSearch vs. pgvector im Vergleich

AI/ML, Tutorial
Vijonavor 1 Stunde Weaviate, OpenSearch und PostgreSQL mit pgvector im Vergleich für Vektor- und Hybrid-Suche Wenn eine Anwendung Suchfunktionen benötigt, kommen häufig Werkzeuge infrage, die bereits Teil der vorhandenen Infrastruktur sind.…
Moderne Hosting Services mit Cloud Server, Managed Server und skalierbarem Cloud Hosting für professionelle IT-Infrastrukturen

Wildcard SSL-Zertifikate mit Certbot & Let’s Encrypt

Security, Tutorial
Vijonavor 3 Stunden Wildcard-SSL-Zertifikate mit Certbot und Let’s Encrypt erstellen und erneuern Für die Ausstellung eines Wildcard-Zertifikats für Namen wie *.example.com mit Certbot und Let’s Encrypt ist immer die ACME-DNS-01-Challenge erforderlich.…