When programming, it is common to perform an operation that is expected to fail at times, but should not throw an error. In Python, try/except
clauses are frequent. In human terms, try/except
means, "try this operation, and if it fails for this reason, then do this other thing." For instance, you can try the following out in your console.
friends = {"Nguyen": "tacos", "Hannah": "borscht"}
def get_food(friend_name):
try:
food = friends[friend_name]
except KeyError:
food = "baklava"
friends[friend_name] = food
return food
get_food("Nguyen")
# "tacos"
get_food("Ahmed")
# "baklava"
friends
# {'Nguyen': 'tacos', 'Hannah': 'borscht', 'Ahmed': 'baklava'}
The above in simple language: if the key isn't in the dictionary, don't fret, just add it with a default value.
What if you don't have an alternate course of action? What if you just want to silence the error? Let's say you want to make a directory, and if the directory is already there, don't whine about it, just ignore. Some developers use try/finally
for this case as well:
import os
os.mkdir("frivolous_directory")
try:
os.mkdir("frivolous_directory")
except FileExistsError:
pass
So, the above works, it is common practice, and one doesn't have to do any special imports. Awesome. No shame if you have good reason to use this pattern.
However, in human terms, the above try/except
block reads like this: make the directory, but if that fails because it already exists, then perform the following action: um, nothing.
So, from a utility standpoint, it works fine. From a semantic standpoint, it is not particularly meaningful.
A meaningful alternative: contextlib.suppress
If the desire is to simply suppress an error, rather than perform some sort of branching logic, the Python standard library has a paradigm for that: contextlib.suppress()
. It works like this:
import contextlib
import os
os.mkdir("frivolous_directory")
with contextlib.suppress(FileExistsError):
os.mkdir("frivolous_directory")
The FileExistsError
in the above was silenced.
This was two less lines of code than the previous try/except
example (OK, we had to add import contextlib
as well, so one less line in this example).
More importantly, it is very clear what is happening in the code. We don't mind if the operation fails for the specific reason given. There is no pass
with unclear intent. We are not try
ing something nor are we seeking to intercept the error for further logic or processing.
Conclusion
This isn't so much about working code as it is about readable and reasonable code. Using contextlib.suppress
is a worthwhile practice. Reading a bit about the treasures in contextlib
may be worth your time as well!
Top comments (3)
I haven't heard that one. I am curious if you have any links to references saying this?
If it is slower, I can't imagine it would be much slower. Typically, exception handling isn't the primary source of slowness in a Python program.
Agreed. I use black, too. I don't actually use the wemake python styleguide much, but I did use it on a project once and learned a lot.
I do use a variety of Flake8 plugins, though, along with Black and Mypy (and sometimes PyRight).
I am so glad you find it interesting, too! I first encountered this while using the wemake python styleguide