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
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.
# Don't do this
try:
process(order)
except:
print("Something went wrong") # swallows EVERYTHING, including bugsCatching 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.
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.
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'
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.
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)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.
def read_first_line(path):
file = open(path)
try:
return file.readline()
finally:
file.close() # runs even if readline() raises, or the function returns abovePutting It All Together
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 intry.finally— always runs, for guaranteed cleanup.