One idiomatic practice in Python that often surprises people coming from programming languages where exceptions are considered, well, exceptional, is EAFP: “it’s easier to ask for forgiveness than permission”. Quickly, EAFP means that you should just do what you expect to work and if an exception might be thrown from the operation then catch it and deal with that fact. What people are traditionally used to is LBYL: “look before you leap”. Compared to EAFP, LBYL is when you first check whether something will succeed and only proceed if you know it will work.
If this all doesn’t make sense from the prose alone, don’t worry as code will make this obvious. Let’s take the case of receiving a dictionary that may (or may not) have a certain key. In LBYL you would check for the key first before using it:
if "key" in dict_: value += dict_["key"]
This prevents a
KeyError exception from being raised which seems logical. But what this code confers to the user is that the exceptional case is that the dictionary has the key, not that the key may be absent which may not be true. Without a comment stating what the common case you may have no idea what to expect to be true if you didn’t write this code. Or put another way, this code seems to emphasis that the key being (absent) in the dictionary is somehow special which may not really be the case or that important.
But what if the key is typically in the dictionary or shouldn’t be considered in any way exceptional? EAFP lets you write the same code in a different way that downplays the importance of the key being in the dictionary:
try: value += dict_["key"] except KeyError: pass
Reading this code, what does it tell you? It seems to suggest that the key is typically in the dictionary but sometimes it isn’t. If the key is missing then it’s no big deal, but if it does exist then it should be used in calling a function. This clear communication of what the code is meant to convey to the developer is very important to Python and so it is preferred over the LBYL style which can miscommunicate how common/rare/important the existence of the key is.
Some people are going to read this and say that the EAFP version is longer and explicit compared to the LBYL, which is obviously true. But the idea that “explicit is better than implicit” is a key guideline in the design of Python itself, and so the explicitness of the code is on purpose.
Other than the potential explicitness/verboseness, another worry people often have with EAFP is performance. If you are coming to Python from a programming language where exceptions are an expensive thing to trigger then that worry would be understandable. But since exceptions are used for control flow like in EAFP, Python implementations work hard to make exceptions a cheap operation, and so you should not worry about the cost of exceptions when writing your code.
One word of warning with EAFP, though, that holds true with any code that is dealing with catching exceptions is to not cast an overly broad net over the code in your
try block. For instance, it may be tempting to start writing code like:
try: do_something(dict_["key"]) except KeyError: pass
The problem with that code, though, is what happens if do_something() throws a
KeyError itself that you didn’t want suppressed? In that instance you should be more explicit about what you’re considering the exceptional case:
try: value = dict_["key"] except KeyError: pass else: do_something(value)
As with any block of code where you’re potentially suppressing exceptions, you want to localize as much as possible what code you are executing in the
try block. Now if you prefer LBYL in situations like this where EAFP seems overly explicit then there’s nothing wrong with using LBYL:
if "key" in dict_: do_something(dict["key"])
The key takeaway you should have for the post is that EAFP is a legitimate coding style in Python and you should feel free to use it when it makes sense.