Ivan Miller
Ivan Miller

Reputation: 113

perplexing python dict.get() behavior with default dict index

so i've run into a perplexing issue when utilising the dict.get() method in python in conjunction with a default of a dict[index]:

d = {1: 1}
d2 = {2: 2}
#throws KeyError
d.get(1, d2[1])

i'm a little lost on why this happens....in my mind the d2 index should never be called, given that the key 1 exists in d. I'm guessing this is an implementation detail i'm missing and can't seem to find more info on.

Is there any help/hope for me?

Upvotes: 0

Views: 83

Answers (2)

kaya3
kaya3

Reputation: 51093

d.get(1, d2[1]) is not a conditional/branching statement or expression like if is, it is just a normal method call, so all of the code is evaluated unconditionally.

Before the get method can be called, its arguments need to be evaluated. d2[1] is the second argument, so this is evaluated - raising KeyError - before the method is called at all. So this is the expected behaviour, not an implementation detail.

To do what you want, you can use a conditional expression like below, which will only raise KeyError when neither dictionary has the key. The expression d2[1] is not evaluated unless the condition is false, because the if expression branches.

d[1] if 1 in d else d2[1]

Upvotes: 1

chepner
chepner

Reputation: 531718

The expressions used for the arguments to get (or any function) must be evaluated before get is ever called. Python doesn't have lazy evaluation semantics, and this is part of the language's definition, not an implementation detail of CPython.

Compare this with something like the defaultdict type, which takes a callable that returns a default value, rather than the default value itself.

defaultdict(lambda: 3)  # not defaultdict(3)
defaultdict(int)  # not defaultdict(0); int happens to return
                  # 0 when called with no arguments.

get, however, expects a value, not a function that can be called to produce a value.

Upvotes: 4

Related Questions