0

I'm trying to get familiar with the best practices of Python. According to the Zen of Python it's easier to ask forgiveness than to ask for permission, however, it also says that flat is better than nested and readability counts. How would you deal with this:

I have 3 dictionaries. I have a key, and I want to test if the key is in a dictionary. The key will only be in one of them. Depending on which dictionary it's in, I want to do different stuff.

Using try/except, I come to the following solution:

try:
    val = dict1[key]
except KeyError:
    try:
        val = dict2[key]
    except KeyError:
        try:
            val = dict3[key]
        except KeyError:
            do_stuff_key_not_found()
        else:
            do_stuff_dict3()
    else:
        do_stuff_dict2()
else:
    do_stuff_dict1()

According to Python's EAFP principle this would be the way to go, but it looks cluttered, and is not very readable.

A simpler solution would be:

if key in dict1:
    val = dict1[key]
    do_stuff_dict1()
elif key in dict2:
    val = dict2[key]
    do_stuff_dict2()
elif key in dict3:
    val = dict3[key]
    do_stuff_dict3()
else:
    do_stuff_key_not_found()

What is the more Pythonic way of handling a case like this? Should I stick to the EAFP principle, or is flat and readability more important?

2
  • 2
    Your second solution is nicer to read, but ... why are you putting values in three different dictionaries and then not knowing where you put them? Why are you calling three different functions with basically the same name instead of one function with a parameter? What's the code doing? Commented May 5, 2016 at 2:13
  • I know exactly what is in which dictionary, but the key is loaded from an input file. I could put all stuff together in one dictionary, but this seemed the most elegant way to determine which stuff to do. Also, the functions are just examples, in my script they're just a couple (very different) lines of code. Commented May 5, 2016 at 2:35

2 Answers 2

6

EAFP is a reasonable maxim in many situations, but it's not a dictum to be followed slavishly. In your example I would say there's nothing so horribly wrong with the if/elif version.

Both versions involve code repetition, and can thus become unwieldy if you have a large number of cases to handle. One way to deal with that is to pull out the dict/function pairs into a list, and then iterate over the list:

handlers = [ (dict1, do_stuff_dict1), (dict2, do_stuff_dict2), (dict3, do_stuff_dict3) ]
for whichDict, whichFunc in handlers:
    try:
        val = whichDict[key]
    except KeyError:
        continue
    else:
        whichFunc()
        break
else:
    do_stuff_not_found()
Sign up to request clarification or add additional context in comments.

Comments

1

I prefer to use dict.get(key, default_value) to avoid exception handling, e.g.:

handlers = [(d1, func_1), (d2, func_2)]
handlers_found = [(d, func) for d, func in handlers if d.get(key)]
if handlers_found:
    handlers_found[0][1]()
else:
    do_stuff_not_found()

get(key[, default]) Return the value for key if key is in the dictionary, else default. If default is not given, it defaults to None, so that this method never raises a KeyError. https://docs.python.org/2/library/stdtypes.html

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.