How to Catch Multiple Exceptions in One Line in Python
When handling errors in Python, you often encounter situations where different exceptions should be handled in the same way like displaying the same error message, logging the same warning, or executing the same fallback logic. Instead of writing separate except blocks for each exception type, Python lets you catch multiple exceptions in a single line using a tuple.
This approach keeps your code cleaner, shorter, and follows the DRY (Don't Repeat Yourself) principle.
Basic Syntax
Catch multiple exceptions by grouping them in a tuple within a single except clause:
try:
# Code that might raise different exceptions
pass
except (ExceptionType1, ExceptionType2, ExceptionType3) as e:
# Handle all of them the same way
print(f"An error occurred: {e}")
The Problem: Repetitive Exception Handling
Without grouping, handling multiple exceptions with the same logic requires duplicate code:
# Repetitive: violates DRY principle
try:
result = int("hello")
except ValueError as e:
print(f"Error: {e}")
except TypeError as e:
print(f"Error: {e}")
except ArithmeticError as e:
print(f"Error: {e}")
Each except block does the exact same thing. This is repetitive and harder to maintain.
The Solution: Catch Multiple Exceptions in One Line
Group the exceptions into a tuple to handle them all with a single block:
try:
result = int("hello")
except (ValueError, TypeError, ArithmeticError) as e:
print(f"Error: {e}")
Output:
Error: invalid literal for int() with base 10: 'hello'
The same handler runs regardless of which exception in the tuple is raised.
Practical Examples
Example 1: Handling User Input Errors
def safe_divide(a, b):
try:
result = a / b
return result
except (ZeroDivisionError, TypeError) as e:
print(f"Cannot divide: {e}")
return None
# Division by zero
print(safe_divide(10, 0))
# Wrong type
print(safe_divide(10, "hello"))
Output:
Cannot divide: division by zero
None
Cannot divide: unsupported operand type(s) for /: 'int' and 'str'
None
Both ZeroDivisionError and TypeError are handled by the same block.
Example 2: File and Data Processing
import json
def load_config(filepath):
try:
with open(filepath, 'r') as f:
config = json.load(f)
return config
except (FileNotFoundError, PermissionError, json.JSONDecodeError) as e:
print(f"Failed to load config: {e}")
return {}
# Test with a non-existent file
config = load_config("missing_config.json")
print(f"Config: {config}")
Output:
Failed to load config: [Errno 2] No such file or directory: 'missing_config.json'
Config: {}
Three different exceptions ae file not found, permission denied, and invalid JSON and they are all handled the same way.
Example 3: Data Conversion
def convert_to_number(value):
try:
return float(value)
except (ValueError, TypeError) as e:
print(f"Cannot convert '{value}': {e}")
return 0.0
# Various invalid inputs
print(convert_to_number("3.14")) # Works
print(convert_to_number("hello")) # ValueError
print(convert_to_number(None)) # TypeError
print(convert_to_number([1, 2])) # TypeError
Output:
3.14
Cannot convert 'hello': could not convert string to float: 'hello'
0.0
Cannot convert 'None': float() argument must be a string or a real number, not 'NoneType'
0.0
Cannot convert '[1, 2]': float() argument must be a string or a real number, not 'list'
0.0
Combining Grouped and Separate Exception Handlers
You can mix grouped and individual except blocks when some exceptions need different handling:
def process_data(data):
try:
result = 100 / int(data)
return result
except (ValueError, TypeError) as e:
# Handle input conversion errors the same way
print(f"Invalid input: {e}")
return None
except ZeroDivisionError:
# Handle division by zero differently
print("Error: Cannot divide by zero. Using default value.")
return float('inf')
print(process_data("hello")) # ValueError
print(process_data(None)) # TypeError
print(process_data("0")) # ZeroDivisionError
print(process_data("5")) # Success
Output:
Invalid input: invalid literal for int() with base 10: 'hello'
None
Invalid input: int() argument must be a string, a bytes-like object or a real number, not 'NoneType'
None
Error: Cannot divide by zero. Using default value.
inf
20.0
Identifying Which Exception Was Raised
When catching multiple exceptions in one block, you can check which specific exception occurred using type() or isinstance():
def parse_value(value):
try:
return int(value) / (int(value) - 5)
except (ValueError, TypeError, ZeroDivisionError) as e:
error_type = type(e).__name__
print(f"[{error_type}] {e}")
if isinstance(e, ZeroDivisionError):
print(" → Hint: The value results in division by zero.")
elif isinstance(e, ValueError):
print(" → Hint: Provide a valid integer string.")
parse_value("hello")
parse_value(None)
parse_value("5")
Output:
[ValueError] invalid literal for int() with base 10: 'hello'
→ Hint: Provide a valid integer string.
[TypeError] int() argument must be a string, a bytes-like object or a real number, not 'NoneType'
[ZeroDivisionError] division by zero
→ Hint: The value results in division by zero.
Using Exception Hierarchies
Python exceptions follow an inheritance hierarchy. Catching a parent exception also catches all its children:
# ArithmeticError is the parent of:
# - ZeroDivisionError
# - OverflowError
# - FloatingPointError
try:
result = 1 / 0
except ArithmeticError as e:
print(f"Math error: {e}")
print(f"Specific type: {type(e).__name__}")
Output:
Math error: division by zero
Specific type: ZeroDivisionError
Instead of listing many related exceptions individually:
except (ZeroDivisionError, OverflowError, FloatingPointError):
You can catch the parent:
except ArithmeticError:
Common parent exceptions:
OSError: coversFileNotFoundError,PermissionError,ConnectionError, etc.ArithmeticError: coversZeroDivisionError,OverflowError,FloatingPointErrorLookupError: coversIndexError,KeyError
Common Patterns
Dictionary/List Access Errors
def get_value(data, key):
try:
return data[key]
except (KeyError, IndexError, TypeError) as e:
print(f"Access error: {e}")
return None
print(get_value({"a": 1}, "b")) # KeyError
print(get_value([1, 2, 3], 10)) # IndexError
print(get_value(None, "key")) # TypeError
Output:
Access error: 'b'
None
Access error: list index out of range
None
Access error: 'NoneType' object is not subscriptable
None
Network/IO Operations
import urllib.request
def fetch_url(url):
try:
response = urllib.request.urlopen(url, timeout=5)
return response.read()
except (urllib.error.URLError, urllib.error.HTTPError,
TimeoutError, ConnectionError) as e:
print(f"Network error: {e}")
return None
Summary
| Approach | Syntax | Use Case |
|---|---|---|
| Single exception | except ValueError: | One specific exception |
| Multiple (tuple) | except (ValueError, TypeError): | Same handling for multiple exceptions |
| Parent exception | except ArithmeticError: | Catch all child exceptions |
| Mixed | Grouped + separate blocks | Different handling for different groups |
| With alias | except (ValueError, TypeError) as e: | Access the exception message |
Conclusion
Catching multiple exceptions in one line using a tuple is a clean, Pythonic pattern that reduces code duplication and improves readability.
- Group exceptions together when they need the same handling logic, and use separate
exceptblocks when different exceptions need different responses. - Leverage Python's exception hierarchy to catch related exceptions with a single parent class.
- The
as eclause gives you access to the specific error message regardless of which exception was raised, making your error handling both concise and informative.