Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is checking if key is in dictionary and getting it's value in the same "if" safe?

I think this is safe:

if key in test_dict:
    if test_dict[key] == 'spam':
        print('Spam detected!')

but is this safe?

if key in test_dict and test_dict[key] == 'spam':
    print('Spam detected!')

It should do the same thing because condition checking is lazy in python. It won't try to get the value (and raise an exception because there's no such key in the dict) because the first condition is already not satisfied. But can I rely on the lazyness and use the second example in my programs?

like image 357
Ilya Peterov Avatar asked Sep 20 '15 12:09

Ilya Peterov


2 Answers

Yes, it is safe, Python would short circuit if first expression result in False, that is it would not evaluate the second expression in the if condition.

But a better way to do your condition would be to use .get() , which returns None if key is not present in dictionary . Example -

if test_dict.get(key) == 'spam':
    print('Spam detected!')
like image 156
Anand S Kumar Avatar answered Sep 23 '22 07:09

Anand S Kumar


and test_dict[key] == 'spam': will only be evaluated if if key in test_dict is True, it will behave exactly the same as your first code where you have the if nested.

When you use and both sides of the expression must be True so if the key in test_dict returns False then the code will short circuit.

Using your and approach is actually the most efficient especially when the left hand side of the expression is False:

In [13]: d = {k:k for k in range(10000)}
In [14]: timeit 99999 in d and d[100] == "foo"
10000000 loops, best of 3: 48.2 ns per loop

In [15]: timeit d.get(9000) == "foo" 
10000000 loops, best of 3: 155 ns per loop
In [16]: timeit 100  in d and d[100] == "foo
10000000 loops, best of 3: 119 ns per loo    
In [17]: timeit d.get(100) == "foo"
10000000 loops, best of 3: 141 ns per loop
like image 24
Padraic Cunningham Avatar answered Sep 24 '22 07:09

Padraic Cunningham