MTV DNA
MTV DNA

Reputation: 187

Python nested try except vs if, elif, else

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?

Upvotes: 0

Views: 2227

Answers (2)

Guoliang
Guoliang

Reputation: 895

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

Upvotes: 1

BrenBarn
BrenBarn

Reputation: 251383

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()

Upvotes: 6

Related Questions