Catching Specific vs. Generic Exceptions
When handling exceptions, it’s usually best to catch only the exceptions you expect and know how to handle. Catching exceptions specifically means naming the exception type in the except clause, like except ValueError: or except ZeroDivisionError:. This way, you handle the error appropriately for that particular situation. For example, if you expect a ValueError from converting input to int, you catch that and perhaps prompt the user again, but you wouldn’t want to catch a KeyboardInterrupt or a MemoryError the same way.
You can have multiple except blocks after a single try to handle different exceptions separately:
try:
num = int(input("Enter a number: "))
result = 100 / num
except ValueError:
print("That's not a valid integer.")
except ZeroDivisionError:
print("You entered 0, division is not possible!")
In this snippet:
-
If the
int()conversion fails, it raisesValueErrorand the firstexceptruns. -
If the conversion succeeds but the division fails (
numwas 0), the secondexceptruns.
Order matters: Python will check each except in turn and execute the first one that matches the raised exception. Therefore, list more specific exceptions first, and more general catches later. If we had an except Exception: at the top, it would catch everything, even the ValueError and ZeroDivisionError, and those specific handlers below would never run.
Generic exception handlers:
Using except Exception: or the even broader except: (which catches all exceptions including system-exiting ones like KeyboardInterrupt) is generally not recommended unless you have a very good reason. A generic handler might be useful to log an unexpected error or to ensure some cleanup, but it can also catch errors you didn’t anticipate, potentially masking bugs. If you do use a generic catch-all, it’s good practice to at least log or display the error so it’s not swallowed silently, and possibly re-raise it after handling if you can’t fully resolve it.
For example:
try:
# some code
except Exception as err:
print(f"Unexpected error: {err}") # Log the error details
raise # re-raise the exception to not suppress it
This will catch any exception, print a message with the error, then re-raise it so it can propagate or be caught elsewhere. The re-raise is done by just calling raise with no argument inside an except block, which throws the same exception again.
When to use generic handlers:
Sometimes in a top-level program loop you might catch Exception so that your program can continue running (or to fail gracefully) instead of crashing. In those cases, handle what you can (like logging or cleaning up) and consider exiting or re-raising after logging, so the error isn’t hidden. Avoid patterns like:
except Exception:
pass
which completely ignore errors – this makes bugs very hard to find because the program will fail silently.
Catching multiple exception types in one block:
You can have a single except handle multiple exception types by using a tuple, e.g.:
try:
# ...
except (TypeError, ValueError) as e:
print("Invalid input:", e)
This except will catch either a TypeError or a ValueError and handle them in the same way.
Summary:
Prefer specific exceptions in except clauses – it’s a best practice to only catch what you can actually handle. Use broad except Exception as a fallback or for logging, and avoid the bare except: (which even catches things like KeyboardInterrupt or SystemExit that you usually shouldn’t interfere with). Being specific prevents hiding unexpected errors and makes your code’s error handling intentions clear.
