Apps Artificial Intelligence CSS DevOps Go JavaScript Laravel Linux MongoDB MySQL PHP Python Rust Svelte Vue

Python `try-except`: A Practical Error-Handling Guide

2 min read .
Python `try-except`: A Practical Error-Handling Guide

Exceptions let Python programs report failures without encoding every error as a special return value. try and except provide structured recovery when an operation has expected failure modes.

Basic try-except

try:
    number = int("not-a-number")
except ValueError:
    print("The value is not a valid integer.")

When int() raises ValueError, Python skips the remaining code in the try block and runs the matching handler.

Catch Specific Exceptions

Prefer specific exception types:

try:
    result = 10 / divisor
except ZeroDivisionError:
    print("The divisor must not be zero.")

A broad handler such as except Exception: can be useful at process boundaries for logging or cleanup, but using it everywhere can hide programming bugs.

Inspect the Exception

try:
    with open("config.json", encoding="utf-8") as file:
        content = file.read()
except OSError as exc:
    print(f"Could not read the file: {exc}")

The exception object contains details supplied by the operation that failed.

Multiple Handlers

Use separate handlers when recovery differs:

try:
    value = int(user_input)
    result = 100 / value
except ValueError:
    print("Enter an integer.")
except ZeroDivisionError:
    print("Enter a non-zero integer.")

When several exceptions should receive the same response, group them in a tuple:

except (FileNotFoundError, PermissionError) as exc:
    print(f"Cannot access the file: {exc}")

else: Run Only on Success

The optional else block runs when the try block completes without raising an exception:

try:
    value = int("42")
except ValueError:
    print("Invalid integer")
else:
    print(f"Parsed value: {value}")

Keeping success-only code in else can make the protected try block smaller, which reduces the chance of accidentally catching an unrelated error.

finally: Always Run Cleanup

resource = acquire_resource()
try:
    use_resource(resource)
finally:
    release_resource(resource)

finally runs whether the try block succeeds, raises an exception, or returns early.

For files, locks, and many other resources, a context manager is usually clearer:

with open("data.txt", encoding="utf-8") as file:
    content = file.read()

Raise Exceptions Deliberately

Your own code can signal invalid input with raise:

def withdraw(balance, amount):
    if amount < 0:
        raise ValueError("amount must be non-negative")
    if amount > balance:
        raise ValueError("insufficient balance")
    return balance - amount

Choose an existing exception type when it accurately describes the problem, or define a custom exception for domain-specific failures.

Re-Raise an Exception

Sometimes you want to add logging and then preserve the original failure:

try:
    process()
except ProcessingError:
    logger.exception("Processing failed")
    raise

A bare raise inside an exception handler re-raises the active exception with its traceback intact.

Exception Chaining

When translating one error into another, use raise ... from ...:

try:
    value = int(raw_value)
except ValueError as exc:
    raise ConfigurationError("invalid retry count") from exc

This preserves the causal relationship in the traceback.

Do Not Use Exceptions for Normal Branching

Exceptions are appropriate for exceptional or failure conditions. If a condition is normal and easily testable, a straightforward check is usually clearer:

if key in mapping:
    value = mapping[key]

That said, Python code often uses an “ask forgiveness rather than permission” style when attempting the operation directly is clearer and avoids duplicated work.

Conclusion

Catch the narrowest exceptions you can recover from, keep try blocks focused, use else for success-only work and finally for guaranteed cleanup, and preserve useful traceback information when translating or re-raising errors.

Related Posts

chevron-up