Proper error handling can make your Python code simpler, more readable, and idiomatic. Here are 7 tips to help you.
Exceptions in Python have the following clauses:
try: ... except ...: # handle exception except ...: # handle another exception ...
And optionally after the except clauses:
... else: # code if no exception was hit finally: # cleanup code, always executes
except is all it takes, but with freedom comes responsibility. A bare
try except: might cause you headaches debugging. So name your exceptions specificly.
Note that order matters here. Address more specific exceptions before more general ones. If you put
ZeroDivisionError for example, it will become a catch-all.
Raising an exception (using
raise) is a great way to avoid unnecessary indenting in your code.
Take one of our Bites. Right off the bat we raise an exception if the input doesn't make sense:
def pretty_date(date): """Receives a datetime object and converts/returns a readable string""" if not isinstance(date, datetime) or date > NOW: raise ValueError('expecting past date')
This is literally all it takes:
class NoInternetConnection(Exception): """Exception raised when there is no internet"""
(We have always used
pass here, but Python in a Nutshell, actually suggests using a docstring instead.)
The advantage: it's like a contract with the caller of your function (this is how we gonna play the game).
It can increase the readability of your code as your module has more specific (granular) expressions as compared to the standard library ones.
You could even build up exception class hierarchies and namespace them by using a dedicated module like the
requests library does.
When to do this? Here is a good answer:
Generally, the only times you should be defining your own exceptions are when you have a specific case where raising a custom exception would be more appropriate than raising a existing Python exception.
Because handling too many exceptions is a red flag.
Reducing the amount of code in your
try block limits this temptation, which reduces the amount of error handling code you'll need.
Instead of excessive condition checking (aka look before you leap), it's easier to ask for forgiveness than permission (EAFP).
As we showed in our previous exception handling article instead of checking:
if os.path.exists("file.txt"): ...
It's more idiomatic (pythonic) to just try things and catch the corresponding exception(s):
try: # assume the file is there os.unlink("file.txt") except OSError: # if not, handle the exception
Here is another good example and a bit more about the advantage of EAFP: Idiomatic Python: EAFP versus LBYL
We can have perfect exception handling, but sometimes something slips through the cracks. You can use a tool like Sentry to send you an email when a user hits an unexpected error and doesn't tell you about it. Not only will you know right away, you will also get the full stacktrace which greatly helps fixing the issue.
I hope this helps. What have you learned dealing with exceptions in Python? Share you insights in the comments below.
Keep Calm and Code in Python!