Python Multiple Exceptions
In real-world programming, your code can encounter various types of errors or exceptions. Rather than writing separate try-except blocks for each potential exception, Python allows you to handle multiple exceptions within a single structure. This approach makes your code cleaner, more readable, and more efficient.
Introduction to Multiple Exception Handling
When writing Python applications, particularly those that interact with external resources like files, networks, or databases, various things can go wrong. Your code needs to be prepared to handle different types of errors gracefully.
Python's exception handling mechanism allows you to:
- Catch multiple exception types in a single try-except block
- Handle different exceptions in different ways
- Specify a catch-all handler for unanticipated exceptions
Basic Syntax for Handling Multiple Exceptions
There are several ways to handle multiple exceptions in Python:
Method 1: Catch Multiple Exceptions in a Single Except Statement
try:
# Code that might raise exceptions
number = int(input("Enter a number: "))
result = 10 / number
print(f"Result: {result}")
except (ValueError, ZeroDivisionError):
print("Error: Please enter a non-zero number")
In this example, both ValueError
(if the user enters text instead of a number) and ZeroDivisionError
(if the user enters zero) are handled by the same except block.
Method 2: Separate Except Blocks for Different Exception Types
try:
number = int(input("Enter a number: "))
result = 10 / number
print(f"Result: {result}")
except ValueError:
print("Error: Please enter a valid integer")
except ZeroDivisionError:
print("Error: Cannot divide by zero")
Output when entering "abc":
Error: Please enter a valid integer
Output when entering "0":
Error: Cannot divide by zero
This approach allows you to provide custom error messages and handling for each specific exception type.
Method 3: Using the Exception Hierarchy with Specific to General
When catching multiple exceptions, it's important to order them from most specific to most general:
try:
with open("config.txt") as file:
config = file.read()
settings = int(config)
except FileNotFoundError:
print("Error: Config file not found, creating default")
settings = 100
except ValueError:
print("Error: Config file contains invalid data")
settings = 100
except Exception as e:
print(f"Unexpected error occurred: {e}")
settings = 100
Always place more specific exceptions before general ones, or the specific handlers will never be executed.
The Exception Hierarchy
Understanding Python's exception hierarchy helps you design better exception handling:
BaseException
├── SystemExit
├── KeyboardInterrupt
├── GeneratorExit
└── Exception
├── StopIteration
├── ArithmeticError
│ ├── FloatingPointError
│ ├── OverflowError
│ └── ZeroDivisionError
├── AssertionError
├── AttributeError
├── BufferError
├── EOFError
├── ImportError
│ └── ModuleNotFoundError
├── LookupError
│ ├── IndexError
│ └── KeyError
├── MemoryError
...and many more
When you catch Exception
, you're catching all standard errors except for system-level exceptions like KeyboardInterrupt
.
Capturing Exception Information
Sometimes you need details about the exception to properly handle it. You can capture the exception object:
try:
data = [1, 2, 3]
value = data[5]
except IndexError as err:
print(f"Error details: {err}")
print(f"Error type: {type(err).__name__}")
Output:
Error details: list index out of range
Error type: IndexError
Practical Examples
Example 1: File Processing with Error Handling
Here's a function that reads data from a file with comprehensive error handling:
def read_user_data(filename):
try:
with open(filename, 'r') as file:
data = file.read()
user_id = int(data.split(',')[0])
return user_id
except FileNotFoundError:
print(f"Error: The file '{filename}' was not found.")
return None
except PermissionError:
print(f"Error: No permission to access '{filename}'.")
return None
except (IndexError, ValueError):
print(f"Error: The file '{filename}' has invalid content format.")
return None
except Exception as e:
print(f"An unexpected error occurred: {e}")
return None
Example 2: Network Request with Multiple Exception Handling
This example shows how to handle different exceptions when making network requests:
import requests
def fetch_data_from_api(url):
try:
response = requests.get(url, timeout=5)
response.raise_for_status() # Raises an HTTPError for bad responses
return response.json()
except requests.exceptions.HTTPError as err:
print(f"HTTP Error: {err}")
except requests.exceptions.ConnectionError:
print("Error: Unable to connect to the server.")
except requests.exceptions.Timeout:
print("Error: The request timed out.")
except requests.exceptions.JSONDecodeError:
print("Error: Could not parse server response as JSON.")
except Exception as err:
print(f"An unexpected error occurred: {err}")
return None
# Usage
data = fetch_data_from_api("https://api.example.com/data")
if data:
print("Data retrieved successfully!")
Example 3: Database Operations with Multiple Exception Types
When working with databases, various exceptions might occur:
import sqlite3
def save_user_to_database(user_data):
connection = None
try:
connection = sqlite3.connect('users.db')
cursor = connection.cursor()
# Create table if it doesn't exist
cursor.execute('''
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
email TEXT UNIQUE NOT NULL
)
''')
# Insert user data
cursor.execute('''
INSERT INTO users (name, email) VALUES (?, ?)
''', (user_data['name'], user_data['email']))
connection.commit()
print(f"User {user_data['name']} saved successfully!")
return True
except sqlite3.IntegrityError:
print(f"Error: User with email {user_data['email']} already exists.")
return False
except sqlite3.OperationalError as e:
print(f"Database operational error: {e}")
return False
except KeyError as e:
print(f"Missing required user data: {e}")
return False
except Exception as e:
print(f"Unexpected error: {e}")
return False
finally:
if connection:
connection.close()
Best Practices for Handling Multiple Exceptions
-
Be Specific First: Always catch specific exceptions before more general ones.
-
Don't Catch Too Much: Avoid catching
Exception
orBaseException
unless you have a good reason - it can hide bugs. -
Keep Handlers Simple: Exception handlers should be simple. For complex recovery, call separate functions.
-
Use Finally for Cleanup: Use the
finally
clause for code that must execute regardless of whether an exception was raised:
try:
file = open("important_data.txt", "r")
content = file.read()
value = int(content)
except FileNotFoundError:
print("File not found, creating with default value")
value = 0
except ValueError:
print("File contains invalid data")
value = 0
finally:
if 'file' in locals() and not file.closed:
file.close()
print("File closed successfully")
- Consider the
else
Clause: Code in theelse
clause runs if no exceptions were raised in thetry
block:
try:
data = [1, 2, 3, 4, 5]
index = int(input("Enter an index: "))
value = data[index]
except ValueError:
print("Please enter a valid integer")
except IndexError:
print("Index out of range")
else:
# This runs if no exceptions were raised
print(f"The value at index {index} is {value}")
# Do additional processing with value
finally:
print("Operation completed")
Summary
Handling multiple exceptions in Python allows you to write more robust code that can gracefully handle different error conditions. By using the various approaches we've covered:
- Grouping multiple exceptions in a tuple
- Using separate except blocks
- Arranging exceptions from specific to general
- Capturing exception details with
as
- Using
else
andfinally
clauses
You can create more resilient applications that provide better user experiences even when things go wrong.
Additional Resources and Exercises
Exercises
-
File Counter: Write a function that counts the number of lines, words, and characters in a text file. Handle at least three different exceptions that might occur.
-
API Explorer: Create a program that queries a public API of your choice and handles different possible exceptions (network issues, JSON parsing errors, etc.).
-
Calculator Application: Build a simple calculator that takes user input and performs basic operations. Use exception handling to manage invalid inputs, division by zero, and other potential errors.
Additional Resources
- Python Official Documentation on Errors and Exceptions
- Python Exception Hierarchy
- Real Python: Python Exceptions: An Introduction
By mastering multiple exception handling, you're one step closer to writing production-quality Python code that can handle real-world scenarios with grace and reliability.
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)