-1

I always assumed that the default value in the myDict.get('key', default_value) expression was never called if the key exists in the dictionary. However, I have recently found out it somehow is. For example, consider the code

def doSomething(): 
    print('Done')
    return 'Another value'

myDict = {'key': 'value'}
print(myDict.get('key', doSomething()))

One would expect that since key exists in myDict, then the only thing myDict.get('key', doSomething()) would do is to return value and hence, the function doSomething would never be called. This is not the case however, and running this code in python currently outputs:

Done
value

My question is: why is this the case? Why isn't the default_value ignored completely when the key exists?

And what can be done to change this, that is, to not call the default_value when the key exists?

Lucas Baldo
  • 130
  • 7
  • 6
    `get` is a function. Like all python functions, the arguments will be evaluated before passing them to the function. It's not really up to `get` -- it just receives the value passed in after it's been evaluated. – Mark Jun 10 '21 at 23:30
  • The `get` method isn't some kind of macro that conditionally evaluates its arguments. It's a method, which is a type of function. When called, all of its arguments are evaluated and passed to the function. That's basic to Python. – Tom Karzes Jun 10 '21 at 23:32
  • 1
    If you don't want the function to be evaluated, then you could pass the function object `myDict.get('key', doSomething)`. However, not sure the use case of that unless your valid values are also callable functions – OneCricketeer Jun 10 '21 at 23:40

2 Answers2

2

The way you've written it, doSomething() is executed before myDict.get() is called, and its return value (which is implicitly None, because there's no return statement) is passed in as the default.

print(myDict.get('key', doSomething()))
# evaluates to
print(myDict.get('key', None))
# evaluates to
print('value')

As a rule of thumb, passing an expression as a function argument will always have the expression evaluated first, before its result is passed as that argument.

get() does not have the ability to run a separate function upon a nonexistent key. If you pass a lambda in as the default, you'd get the same lambda back out as the default, uncalled.

Green Cloak Guy
  • 23,793
  • 4
  • 33
  • 53
  • The question had nothing to do with the return value of doSomething(), as that would only be relevant in the case the key is not in the dictionary. But thank you for the clarification, "passing an expression as a function argument will always have the expression evaluated first" was what I needed to realize. – Lucas Baldo Jun 11 '21 at 00:05
2

That's because the get method of a dictionary does not accept a callback as Java's computeIfAbsent, but a value. If you want it to not be called, you can do it using the ternary expression:

print(doSomething() if myDict.get('key') is None else myDict.get('key'))
enzo
  • 9,861
  • 3
  • 15
  • 38
  • Doesn't this still call `doSomething`? – OneCricketeer Jun 10 '21 at 23:35
  • @Mark Okay, but as-written, it'll still print Done and None when the value does exist – OneCricketeer Jun 10 '21 at 23:43
  • @OneCricketeer Python has an if...else expression, e.g. `v1 if cond else v2`. In this example, the expression `v1` is only evaluated if `cond` is true, and `v2` is only evaluated if `cond` is false. You can learn about them [here](https://docs.python.org/3/reference/expressions.html#conditional-expressions). – Tom Karzes Jun 10 '21 at 23:44
  • @OneCricketeer, yes you're right, it will print the return value of `doSomething`, which is none, but that seem like the OP's code would too if it worked the way they hoped. – Mark Jun 10 '21 at 23:44
  • 1
    @Mark It's not syntactic sugar. It's an expression, as opposed to what you showed which is a statement that cannot be used in an expression context. – Tom Karzes Jun 10 '21 at 23:47
  • Thanks, @enzo, this is preciselly what I wanted. As for this discuss, this expression does not call the doSomething() neither does it print its return value if the key is in myDict, as I just tested it. – Lucas Baldo Jun 11 '21 at 00:00