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:
pass
except (ExceptionType1, ExceptionType2, ExceptionType3) as e:
print(f"An error occurred: {e}")The Problem: Repetitive Exception Handling
Without grouping, handling multiple exceptions with the same logic requires duplicate code:
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
print(safe_divide(10, 0))
print(safe_divide(10, "hello"))Output:
Cannot divide: division by zero
None
Cannot divide: unsupported operand type(s) for /: 'int' and 'str'
NoneBoth 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 {}
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
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])) # TypeErrorOutput:
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.0Combining 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:
print(f"Invalid input: {e}")
return None
except ZeroDivisionError:
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")) # SuccessOutput:
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.0Identifying 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:
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: ZeroDivisionErrorCommon 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")) # TypeErrorOutput:
Access error: 'b'
None
Access error: list index out of range
None
Access error: 'NoneType' object is not subscriptable
NoneNetwork/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 NoneSummary
| 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.