Exception Handling in Python
Mastering Exception Handling in Python: A Comprehensive Guide
Errors are unavoidable in real programs — a file may be missing, a user may type text where a number was expected, or a network call may fail. Exception handling is the mechanism Python provides to detect these errors and respond to them gracefully instead of crashing. In this guide, we’ll explore the trytry, exceptexcept, elseelse, and finallyfinally blocks, the raiseraise keyword, and how to build your own custom exceptions.
What is an Exception?
An exception is an event that occurs during the execution of a program that disrupts the normal flow of instructions. When Python encounters an error it cannot handle, it raises an exception. If the exception is not handled, the program stops and prints a traceback.
# An unhandled exception
print(10 / 0)# An unhandled exception
print(10 / 0)Output:
C:\Users\Your Name> python error.py
Traceback (most recent call last):
File "error.py", line 2, in <module>
print(10 / 0)
ZeroDivisionError: division by zeroC:\Users\Your Name> python error.py
Traceback (most recent call last):
File "error.py", line 2, in <module>
print(10 / 0)
ZeroDivisionError: division by zeroThe program never reaches any code after line 2 — the ZeroDivisionErrorZeroDivisionError terminates it. Exception handling lets us catch that error and continue.
Errors vs. Exceptions
- Syntax errors are mistakes in the structure of the code (a missing colon, an unclosed bracket). They are caught before the program runs and cannot be handled at runtime.
- Exceptions are raised while the program runs. These are the errors we handle with
trytry/exceptexcept.
# Syntax error - cannot be caught with try/except
if True
print("missing colon")# Syntax error - cannot be caught with try/except
if True
print("missing colon")The try-except Block
The trytry block contains code that might raise an exception. The exceptexcept block contains the code that runs if an exception occurs.
The syntax of the try-excepttry-except block in Python is as follows:
try:
# code that might raise an exception
except ExceptionType:
# code that runs if the exception occurstry:
# code that might raise an exception
except ExceptionType:
# code that runs if the exception occursDiagram:
graph TD
A[Start] --> B[Run try block]
B --> C{Exception raised?}
C -->|No| D[Skip except block]
C -->|Yes| E[Run except block]
D --> F[End]
E --> F[End]
The following example demonstrates how to catch a ZeroDivisionErrorZeroDivisionError:
try:
result = 10 / 0
print(result)
except ZeroDivisionError:
print("You cannot divide by zero!")try:
result = 10 / 0
print(result)
except ZeroDivisionError:
print("You cannot divide by zero!")Output:
C:\Users\Your Name> python try_except.py
You cannot divide by zero!C:\Users\Your Name> python try_except.py
You cannot divide by zero!In the above example, the division raises a ZeroDivisionErrorZeroDivisionError. Python jumps to the matching exceptexcept block, prints the message, and the program continues normally instead of crashing.
Catching the Exception Object
You can capture the exception object itself using the asas keyword. This gives you access to the error message.
try:
number = int("hello")
except ValueError as error:
print("Conversion failed:", error)try:
number = int("hello")
except ValueError as error:
print("Conversion failed:", error)Output:
C:\Users\Your Name> python as_keyword.py
Conversion failed: invalid literal for int() with base 10: 'hello'C:\Users\Your Name> python as_keyword.py
Conversion failed: invalid literal for int() with base 10: 'hello'Handling Multiple Exceptions
A single trytry block can be followed by several exceptexcept blocks. Python runs the first one that matches the raised exception.
try:
value = int(input("Enter a number: "))
result = 100 / value
print(result)
except ValueError:
print("That was not a valid number.")
except ZeroDivisionError:
print("You cannot divide by zero.")try:
value = int(input("Enter a number: "))
result = 100 / value
print(result)
except ValueError:
print("That was not a valid number.")
except ZeroDivisionError:
print("You cannot divide by zero.")You can also handle several exception types in one block by passing a tuple:
try:
risky_operation()
except (ValueError, TypeError, KeyError) as error:
print("Something went wrong:", error)try:
risky_operation()
except (ValueError, TypeError, KeyError) as error:
print("Something went wrong:", error)The else Clause
The elseelse block runs only if no exception was raised in the trytry block. It is useful for code that should run after a successful trytry, keeping the trytry block focused on the risky operation alone.
try:
number = int("42")
except ValueError:
print("Invalid number.")
else:
print("Conversion succeeded:", number)try:
number = int("42")
except ValueError:
print("Invalid number.")
else:
print("Conversion succeeded:", number)Output:
C:\Users\Your Name> python else_clause.py
Conversion succeeded: 42C:\Users\Your Name> python else_clause.py
Conversion succeeded: 42The finally Clause
The finallyfinally block runs no matter what — whether or not an exception was raised, and even if a returnreturn statement executes. It is used for cleanup tasks such as closing files or releasing resources.
try:
file = open("data.txt", "r")
data = file.read()
except FileNotFoundError:
print("File not found.")
finally:
print("Cleaning up...")try:
file = open("data.txt", "r")
data = file.read()
except FileNotFoundError:
print("File not found.")
finally:
print("Cleaning up...")Diagram:
graph TD
A[Start] --> B[Run try block]
B --> C{Exception?}
C -->|No| D[Run else block]
C -->|Yes| E[Run except block]
D --> F[Run finally block]
E --> F[Run finally block]
F --> G[End]
The raise Keyword
You can deliberately raise an exception using the raiseraise keyword. This is useful for signalling that something is wrong with the input to your function.
def set_age(age):
if age < 0:
raise ValueError("Age cannot be negative")
print("Age set to", age)
set_age(-5)def set_age(age):
if age < 0:
raise ValueError("Age cannot be negative")
print("Age set to", age)
set_age(-5)Output:
C:\Users\Your Name> python raise.py
Traceback (most recent call last):
...
ValueError: Age cannot be negativeC:\Users\Your Name> python raise.py
Traceback (most recent call last):
...
ValueError: Age cannot be negativeRe-raising an Exception
Inside an exceptexcept block, a bare raiseraise re-raises the current exception after you have done some work (such as logging).
try:
risky()
except ValueError:
print("Logging the error before re-raising...")
raisetry:
risky()
except ValueError:
print("Logging the error before re-raising...")
raiseCustom Exceptions
You can define your own exception types by subclassing the built-in ExceptionException class. Custom exceptions make your code more readable and let callers catch your specific error.
class InsufficientFundsError(Exception):
"""Raised when a withdrawal exceeds the available balance."""
pass
def withdraw(balance, amount):
if amount > balance:
raise InsufficientFundsError(
f"Cannot withdraw {amount}; balance is only {balance}"
)
return balance - amount
try:
withdraw(100, 250)
except InsufficientFundsError as error:
print("Transaction failed:", error)class InsufficientFundsError(Exception):
"""Raised when a withdrawal exceeds the available balance."""
pass
def withdraw(balance, amount):
if amount > balance:
raise InsufficientFundsError(
f"Cannot withdraw {amount}; balance is only {balance}"
)
return balance - amount
try:
withdraw(100, 250)
except InsufficientFundsError as error:
print("Transaction failed:", error)Output:
C:\Users\Your Name> python custom_exception.py
Transaction failed: Cannot withdraw 250; balance is only 100C:\Users\Your Name> python custom_exception.py
Transaction failed: Cannot withdraw 250; balance is only 100Common Built-in Exceptions
| Exception | Raised when |
|---|---|
ValueErrorValueError | A function gets an argument of the right type but wrong value (e.g. int("abc")int("abc")). |
TypeErrorTypeError | An operation is applied to an object of the wrong type. |
KeyErrorKeyError | A dictionary key is not found. |
IndexErrorIndexError | A sequence index is out of range. |
FileNotFoundErrorFileNotFoundError | A file or directory is requested but does not exist. |
ZeroDivisionErrorZeroDivisionError | The second argument of a division or modulo is zero. |
AttributeErrorAttributeError | An attribute reference or assignment fails. |
ImportErrorImportError | An importimport statement fails to find the module. |
Conclusion
Exception handling lets your programs respond to errors gracefully instead of crashing. The trytry block holds risky code, exceptexcept handles failures, elseelse runs on success, and finallyfinally always runs for cleanup. The raiseraise keyword lets you signal errors, and custom exception classes make your error handling expressive and precise. Master these tools and your Python programs will be far more robust. For more hands-on examples and in-depth tutorials, explore our resources on Python Central Hub!
Try it: Exception Handling Exercises
Exercise 1 – Catch a ZeroDivisionError
Exercise 2 – try / except / else / finally
Exercise 3 – Raise a Custom Exception
If this helped you, consider buying me a coffee ☕
Buy me a coffeeWas this page helpful?
Let us know how we did
