What is the EAFP principle in Python?
Asked Answered
P

4

193

What is meant by "using the EAFP principle" in Python? Could you provide any examples?

Procrustes answered 6/7, 2012 at 10:55 Comment(0)
N
286

From the glossary:

Easier to ask for forgiveness than permission. This common Python coding style assumes the existence of valid keys or attributes and catches exceptions if the assumption proves false. This clean and fast style is characterized by the presence of many try and except statements. The technique contrasts with the LBYL style common to many other languages such as C.

An example would be an attempt to access a dictionary key.

EAFP:

try:
    x = my_dict["key"]
except KeyError:
    # handle missing key

LBYL:

if "key" in my_dict:
    x = my_dict["key"]
else:
    # handle missing key

The LBYL version has to search the key inside the dictionary twice, and might also be considered slightly less readable.

Nineveh answered 6/7, 2012 at 10:56 Comment(6)
An enhancement would be that another advantage is the avoidance of race conditions... eg, just try opening a file and if you get it, you got it. Instead of seeing if you can get it, then trying to get it afterwards and realise that in the miniscule amount of time between the check and access attemp, you can longer get it.Diastole
Python also provides for a way of avoiding both of those, if the handler is just assigning a default value to x when the key doesn't exist: x = mydict.get('key') will return None if 'key' is not in my_dict; you could also do .get('key', <something>), and then x will be assigned that something if the key isn't in the dictionary. dict.setdefault() and collections.defaultdict are nice things for avoiding excess code as well.Nadabb
I think except KeyError as well as AttributeError are simple but some of the worst examples. So many times I was stuck debugging something because except AttributeError was put in wrong place, which end up catching wrong attribute error raised deeper down in the chain. Better examples I think are: try: open() ... except: IOError. Or try: parseLine() ... except ParseErrorSubgroup
@ski That's a slightly different problem. You should always keep the try block as minimal as possible to avoid catching the wrong exception. Also note that I don't generally prefer the EAFP style. I'm just answering the question here, and state that some people prefer it. I deicde on a case-by-case basis what code looks the most readable to me.Nineveh
I thought it'd be worth mentioning that Grace Hopper is likely the source for this phrase, with her quote: "Dare and Do. It's easier to ask forgiveness than it is to get permission" (not restricted to programming).Vivacity
I'd add that it's considered bad practice to catch exceptions that are too broad. It's enticing to simply include except Exception to catch any problem that might occur, but it's much better to catch errors explicitly. (Note that this example includes an explicit exception statement.)Parliamentarian
P
30

I'll try to explain it with another example.

Here we're trying to access the file and print the contents in console.

LBYL - Look Before You Leap :

We might want to check if we can access the file and if we can, we'll open it and print the contents. If we can't access the file we'll hit the else part. The reason that this is a race condition is because we first make an access-check. By the time we reach with open(my_file) as f: maybe we can't access it anymore due to some permission issues (for example another process gains an exclusive file lock). This code will likely throw an error and we won't be able to catch that error because we thought that we could access the file.

import os

my_file = "/path/to/my/file.txt"

# Race condition
if os.access(my_file, os.R_OK):
    with open(my_file) as f:
        print(f.read())
else:
    print("File can't be accessed")

EAFP - Easier to Ask for Forgiveness than Permission :

In this example, we're just trying to open the file and if we can't open it, it'll throw an IOError. If we can, we'll open the file and print the contents. So instead of asking something we're trying to do it. If it works, great! If it doesn't we catch the error and handle it.

# # No race condition
try:
    f = open(my_file)
except IOError as e:
    print("File can't be accessed")
else:
    with f:
        print(f.read())
Paraffin answered 8/5, 2019 at 8:50 Comment(6)
I'm not sure it's correct to describe this as a race condition. Either the file is accessible or not.Felder
@Felder It is the race condition if file accessibility changes between lines 6 and 7, that is between checking if the file is accessible and opening it.Zeiler
@MarkusvonBroady agreed, edited the answer to provide an example of the other participant in the race condition.Felder
I've always assumed LBYL is the preferred way to do things instead of try, except blocks, correct?Waldron
@Waldron maybe in other languages but not in Python. Python expects you to use exceptions, so it has been optimized to be efficient when an exception is not thrown. Exceptions improve readability because the error handling code is grouped together after the working code, and it reduces the amount of indenting when every possible error needs to be handled in-line.Colorblind
@Waldron P.S. the example given for exceptions in this answer isn't optimal, because the else block is completely unnecessary - that code should have been part of the try block instead.Colorblind
A
11

I call it "optimistic programming". The idea is that most times people will do the right thing, and errors should be few. So code first for the "right thing" to happen, and then catch the errors if they don't.

My feeling is that if a user is going to be making mistakes, they should be the one to suffer the time consequences. People who use the tool the right way are sped through.

Augustus answered 29/5, 2018 at 5:28 Comment(2)
Or "try-catch" rather than "if-else"Urbano
I meant to look for possible known errors, like a zip code in a phone number field. Errors should be handled properly, not by catch routines.Augustus
W
-1

To add @sven-marnach to answer,

You could use:

dict[“key”] = dict.get(“key”, None)

This is better than the search twice issue he mentions for doing LBYL.

Winola answered 30/8, 2023 at 22:42 Comment(1)
This does not provide an answer to the question. Once you have sufficient reputation you will be able to comment on any post; instead, provide answers that don't require clarification from the asker. - From ReviewAzaleeazan

© 2022 - 2024 — McMap. All rights reserved.