dict.get(key, default) evaluates default even if key exists

Roel Schroeven roel at roelschroeven.net
Sat Dec 19 16:29:34 EST 2020


Ethan Furman schreef op 16/12/2020 om 17:59:
> Or, if the computationally massively expensive call uses potentially
> different arguments for each invocation:
> 
>       some_var = d.get('a') or cme(arg1, arg2, ...)

I don't like that because it is very fragile: the expression will 
evaluate to the second part not only when 'a' is not in the dictionary 
but also whenever the value corresponding to key 'a' is false-y.

Example:

     def slow_function(a, b):
         # Pretend this is slow, or there is any other reason for this
         # function to be called only when strictly necessary.
         print('slow_function called')
         return a + b

     d = {
         'spam': 0,
         'ham': 1,
         'parrot': 2,
     }

     some_var = d.get('spam') or slow_function(31, 11)
     print(some_var)

Result: slow_function is called, and some_var equals 42 instead of 0.

You could make it work reliable by using a unique sentinel value and the 
walrus operator:

     SENTINEL = object()
     some_var = (
         r
         if (r := d.get('spam', SENTINEL)) is not SENTINEL
         else slow_function(31, 11)
         )

But I don't like that either because it's way too complicated.

What could be useful in some use cases, I think, is a wrapper function 
that evaluates the function lazily:

     def dict_get_lazily(d, key, fnc, *args, **kwargs):
         try:
             return d[key]
         except KeyError:
             return fnc(*args, **kwargs)

     some_var = dict_get_lazily(d, 'spam', some_function, 31, 11)



-- 
"Honest criticism is hard to take, particularly from a relative, a
friend, an acquaintance, or a stranger."
         -- Franklin P. Jones

Roel Schroeven



More information about the Python-list mailing list