Why are values from a dict not being returned even if keys are present when passing a function as default?

Question:

I’m a bit puzzled by the following behaviour. I am retrieving values from a dict such that if a key does not exist I call a function which creates the value and inserts it into the dict. I do this through the default parameter of the dictionary.get method. The problem is that the default function keeps being called EVEN IF the value is already present in the dictionary.
Really mindboggling. Any ideas of why is this happening?

dictionary = {}
def load_default(key):
    print("Inside function")
    value = "world"
    dictionary[key] = value
    return value

print(dictionary) 
'{}' #empty dict, everything ok
value = dictionary.get("hello", load_default("hello"))
'Inside function' # we ask for "hello", which does not exist so we call load_default
print(dictionary) 
"{'hello': 'world'}" # the dict now contains de key "hello"
value = dictionary.get("hello", load_default("hello"))
'Inside function' # we ask for "hello" again, but load_default is called instead ("Inside function" is printed) I would expect the dict to return only the value and not call `load_default`

In the given example, I would expect the dictionary to return the value world WITHOUT calling the load_default function

Answers:

In the given example, I would expect the dictionary to return the value world WITHOUT calling the load_default function.

When executing dictionary.get("hello", load_default("hello")) Python evaluates the expressions passed to .get() method as parameter first and then calls the method with values obtained from the evaluation of the provided parameter.

The first passed expression is a value "hello" so it can be passed to .get() as it is. The second as parameter passed expression is load_default("hello") which needs to be evaluated to a value as it is a call to a function. To obtain a value the function must be executed and the resulting return value of the function will be then passed as second parameter to .get().

At that stage .get() isn’t executed yet so the assumption that the load_default() function will be run only if the key isn’t in the dictionary is wrong leading to confusion.

In other words .get() will be executed AFTER it gets its parameter passed as values and because a function call is not a value it needs to be evaluated to a value so load_default() will be run at each call of .get().

P.S. Python in its new versions is actually continuously optimized for speed. So theoretically it could happen that what is explained above may become not always the case due to some optimizations which won’t run same function or method call again and again if nothing was changed in relation to previous calls with known return value and known side-effects which could be saved in memory by the optimization algorithm and then reused at next function call. Generally the observed resulting behavior as such should stay the same and the use of optimizations perceivable only by suddenly shorter time required to execute the same function or method with same parameter (what could lead to confusion while benchmarking a function/method for speed as suddenly an otherwise time expensive function call generates almost immediately its results).

P.S. P.S. With deep going time optimizations it could happen that in some future Python versions the to a function/method passed parameter will be evaluated only ‘on demand’ and the expectation that a function call as parameter won’t be executed if the value of this parameter isn’t used eventually the right one.

The conclusion from both the P.S. and P.S. P.S. is that it is a good idea not to rely writing own code on the currently probably still true fact that parameter passed to a function/method are evaluated before the function/method is executed.

Answered By: Claudio
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.