Skip to main content

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

python
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

python
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:

python
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:

python
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:

python
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:

python
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:

python
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:

python
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

  1. Be Specific First: Always catch specific exceptions before more general ones.

  2. Don't Catch Too Much: Avoid catching Exception or BaseException unless you have a good reason - it can hide bugs.

  3. Keep Handlers Simple: Exception handlers should be simple. For complex recovery, call separate functions.

  4. Use Finally for Cleanup: Use the finally clause for code that must execute regardless of whether an exception was raised:

python
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")
  1. Consider the else Clause: Code in the else clause runs if no exceptions were raised in the try block:
python
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 and finally clauses

You can create more resilient applications that provide better user experiences even when things go wrong.

Additional Resources and Exercises

Exercises

  1. 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.

  2. API Explorer: Create a program that queries a public API of your choice and handles different possible exceptions (network issues, JSON parsing errors, etc.).

  3. 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

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! :)