Phoenix
Phoenix

Reputation: 425

Is it recommended to use if...else condition to check for keys or use try...except to catch KeyError exceptions in python?

So, say there is a dictionary var_dict, and I want to print the value at a key a for eg. So, one way would be:

if var_dict is None:
    return
if 'a' not in var_dict.keys():
    return
print(var_dict['a'])

And other method would be:

try:
    print(var_dict['a'])
except (KeyError, TypeError):
    return

My question is which is recommended. I read here: Python: try-except vs if-else to check dict keys , that exception handling is slower but what if we specify the expected error? Is it slow in that case as well?

Upvotes: 0

Views: 515

Answers (3)

Omar
Omar

Reputation: 149

Use the second method. From the Python Glossary:

EAFP:
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.

Just to be sure, I just tried both using the %%timeit magic command in jupyter and the second option is slightly faster.

Upvotes: 0

Jordan Brière
Jordan Brière

Reputation: 1055

A try/except block to retrieve a key or a default will be much faster than all other alternatives. E.g.

from time import time

t = time()
for i in range(1000000):
    v = __builtins__['int'] if 'int' in __builtins__ else None
print(time() - t)

t = time()
for i in range(1000000):
    v = __builtins__.get('int', None)
print(time() - t)

t = time()
for i in range(1000000):
    try:
        v = __builtins__['int']
    except KeyError:
        v = None
print(time() - t)

Results:

0.12224698066711426
0.15873217582702637
0.0927286148071289

The first one is much slower, because Python basically retrieve the key twice. Once through __contains__, then __getitem__. The second is slower because Python does some internal validation while the try/catch goes straight to the point or fail.

Easier to ask for forgiveness than permission.

Upvotes: 2

Ken Kinder
Ken Kinder

Reputation: 13140

A try/except block is indeed slower. I'd do this:

def foobar(var_dict):
    return var_dict['a'] if var_dict and 'a' in var_dict else None

Eg:

In [1]: def foobar(var_dict):
   ...:      return var_dict['a'] if var_dict and 'a' in var_dict else None
   ...: 

In [2]: 

In [2]: foobar(None)

In [3]: foobar({'x': 1})

In [4]: foobar({'a': 1})
Out[4]: 1

Alternatively, if you know var_dict is a dictionary (and not None), get() has a default:

return var_dict.get('a', None)

Upvotes: 3

Related Questions