Pythontry / except / finally

try / except / finally

The try statement is how Python lets you anticipate that something might go wrong, attempt it anyway, and decide what should happen if it does. Instead of checking every possible failure condition before acting, you write the code that might fail inside a try block and put the recovery logic in one or more except blocks. This “easier to ask forgiveness than permission” style is idiomatic Python.

Basic Syntax

Python
try:
    number = int(input("Enter a number: "))
    print(100 / number)
except ValueError:
    print("That wasn't a valid number.")
except ZeroDivisionError:
    print("Can't divide by zero.")

Python runs the try block. If every line completes without raising, the except blocks are skipped entirely. The moment a line raises, execution jumps straight to the first matching except clause — any remaining lines in the try block are never reached.

Catch Specific Exceptions, Not Everything

except: with no exception type after it catches everything, including KeyboardInterrupt and typos in your own code that raise NameError or AttributeError. That makes real bugs disappear silently instead of surfacing where you can fix them.

Python
# Don't do this
try:
    process(order)
except:
    print("Something went wrong")   # swallows EVERYTHING, including bugs
Warning
A bare `except:` (or `except Exception:` used carelessly) hides the real cause of failures. A typo like calling `proccess(order)` instead of `process(order)` raises a `NameError` that a bare `except` will quietly eat, and you will spend hours looking in the wrong place. Always name the specific exception(s) you expect and know how to handle.
Catching Multiple Exception Types

When several exception types should be handled the same way, list them as a tuple in a single except clause. When they need different handling, use separate except clauses — Python checks them in order and runs the first one that matches.

Python
def safe_lookup(data, key, index):
    try:
        return data[key][index]
    except (KeyError, IndexError):
        # Same recovery for either problem
        return None

def parse_and_divide(raw_a, raw_b):
    try:
        a, b = int(raw_a), int(raw_b)
        return a / b
    except ValueError:
        print("Inputs must be integers.")
    except ZeroDivisionError:
        print("Can't divide by zero.")
Accessing the Exception Object with `as e`

Binding the exception to a name with as e gives you access to the actual message and arguments the exception carries, which is useful for logging or building a more helpful error for the caller.

Python
try:
    price = float("twelve dollars")
except ValueError as e:
    print(f"Could not parse price: {e}")
Could not parse price: could not convert string to float: 'twelve dollars'
Note
The name bound by `as e` only exists inside that `except` block — Python automatically deletes it once the block finishes, so you cannot reference `e` afterward.
The `else` Clause

A try statement can have an else clause, which runs only if the try block completed with no exception at all. It is different from putting the same code after the whole try/except statement: code in else is still covered by the surrounding logic and makes it explicit that this step depends on success.

Python
try:
    connection = open_connection()
except ConnectionError:
    print("Could not connect.")
else:
    # Only runs if open_connection() succeeded — no exception was raised
    print("Connected successfully.")
    use_connection(connection)
Tip
Use `else` to separate “the risky operation” from “what happens next on success.” It keeps the `try` block minimal, which makes it clearer exactly which line the `except` clauses are guarding against.
The `finally` Clause

Code inside finally always runs — whether the try block succeeded, raised an exception that was caught, raised one that was not caught, or even hit a return statement. This makes it the right place for cleanup that must happen no matter what: closing files, releasing locks, disconnecting from a database.

Python
def read_first_line(path):
    file = open(path)
    try:
        return file.readline()
    finally:
        file.close()   # runs even if readline() raises, or the function returns above
Note
In modern code, prefer a `with` statement (a context manager) for resource cleanup like file handles when possible — it expresses the same guarantee more concisely. Reach for `finally` when the cleanup logic doesn’t map neatly onto an existing context manager.
Putting It All Together

Python
def load_config(path):
    try:
        file = open(path)
    except FileNotFoundError:
        print(f"No config at {path}, using defaults.")
        return {}
    else:
        try:
            return parse(file.read())
        finally:
            file.close()
  • try — the code that might fail.

  • except — runs if a matching exception is raised; skipped otherwise.

  • else — runs only if no exception was raised in try.

  • finally — always runs, for guaranteed cleanup.