Handling Exceptions
When something goes wrong at runtime — a missing file, bad input, a network failure — Python raises an exception. If nothing handles it, the program stops with a traceback. try/except lets you catch exceptions and respond sensibly instead.
try and except
def parse_age(text): try: age = int(text) except ValueError: print(f"{text!r} is not a number") return None return age print(parse_age("42")) print(parse_age("forty-two"))
42 'forty-two' is not a number None
Code in the try block runs normally. If it raises a ValueError, Python jumps to the matching except block instead of crashing.
Catching specific exceptions
def safe_divide(a, b): try: return a / b except ZeroDivisionError: return float("inf") except TypeError as err: return f"bad input: {err}" print(safe_divide(10, 4)) print(safe_divide(1, 0)) print(safe_divide("10", 2))
2.5 inf bad input: unsupported operand type(s) for /: 'str' and 'int'
as err gives you the exception object, whose message you can show or log. Several exceptions can share one handler: except (ValueError, TypeError):.
except: or except Exception: also swallows bugs you didn't anticipate — typos, wrong variable names — and makes them very hard to find. Catch the specific exceptions you expect and can actually handle.else and finally
def read_number(text): try: value = float(text) except ValueError: print(" invalid") else: print(f" parsed {value}") return value finally: print(" (cleanup always runs)") print("first:") read_number("3.5") print("second:") read_number("abc")
first: parsed 3.5 (cleanup always runs) second: invalid (cleanup always runs)
elseruns only if thetryblock raised nothing. Putting follow-up code here keeps thetryblock small, so you don't accidentally catch errors from it.finallyruns no matter what — success, exception, or even areturn. Use it for cleanup like closing connections.
Common built-in exceptions
| Exception | Typical cause |
|---|---|
ValueError | Right type, wrong value: int("abc") |
TypeError | Wrong type: "a" + 1, calling a non-function |
KeyError | Missing dictionary key |
IndexError | List index out of range |
AttributeError | Object has no such attribute or method |
NameError | Undefined variable |
ZeroDivisionError | Dividing by zero |
FileNotFoundError, PermissionError | File system problems (subclasses of OSError) |
ImportError / ModuleNotFoundError | Missing module or package |
The exception hierarchy
Exceptions are classes arranged in a hierarchy, and an except clause catches the named class and all its subclasses:
print(issubclass(FileNotFoundError, OSError)) print(issubclass(KeyError, LookupError), issubclass(IndexError, LookupError)) data = {"a": [1, 2]} for key, index in [("a", 5), ("b", 0)]: try: print(data[key][index]) except LookupError as err: print(f"lookup failed: {type(err).__name__} {err}")
True True True lookup failed: IndexError list index out of range lookup failed: KeyError 'b'
Because the first matching clause wins, put more specific exceptions before more general ones.
EAFP vs LBYL
Two styles exist for handling things that might fail. "Look before you leap" (LBYL) checks first; "easier to ask forgiveness than permission" (EAFP) just tries and handles the exception. EAFP is common in Python:
config = {"port": "8080"}
# LBYL
if "port" in config and config["port"].isdigit():
port = int(config["port"])
# EAFP
try:
port = int(config["port"])
except (KeyError, ValueError):
port = 80
print(port)
8080
EAFP avoids duplicating checks and handles race conditions — a file could be deleted between checking it exists and opening it.
Exception groups
When several independent operations fail together — for example, concurrent tasks — Python can raise an ExceptionGroup, handled with except*:
def validate(form): errors = [] if not form.get("name"): errors.append(ValueError("name is required")) if "@" not in form.get("email", ""): errors.append(ValueError("email is invalid")) if errors: raise ExceptionGroup("form invalid", errors) try: validate({"name": "", "email": "nope"}) except* ValueError as group: for err in group.exceptions: print("-", err)
- name is required - email is invalid