Why does the variable in the function retain it's value?

I have this function:

def canSum(targetSum, numbers, memo={}):
  print(f"Memo start: {memo}")
  if targetSum in memo.keys(): 
    return memo[targetSum]
  if targetSum == 0:
    return True
  if targetSum < 0: return False
  for num in numbers:
    remainder = targetSum - num
    if canSum(remainder, numbers, memo) == True:
      memo[targetSum] = True
      return True
  memo[targetSum] = False
  return False

Which functions as expected if called like this:

print(canSum(7, [2,3],{}))
print(canSum(7, [2,4],{}))

Output:

Memo start: {}
...
Memo start: {1: False}
True
Memo start: {}
...
Memo start: {1: False}
Memo start: {1: False, 3: False}
Memo start: {1: False, 3: False, 5: False}
False

it functions as expected. However if I subsequently do not explicitly pass an empty object, the value of memo from a prior call seems to be retained and used, therefore failing:

print(canSum(7, [2,4]))

Output

Memo start: {1: False, 3: True, 5: True, 7: True}
True

How is this value for memo being retained between function calls?

Thanks for your time

The “why are default values shared between objects” part of the FAQ deals with this.

1 Like

This is a common gotcha. The default values for a function’s arguments are evaluated once, when the function is defined, not every time the function is called. This means that if you have a mutable default value, any mutation will be preserved from call to call. The default value itself is attached to the function object, so it never goes out of scope.

The standard idiom for dealing with this is to use the default value of None, and check for it in the body of your function. Something like:

def canSum(targetSum, numbers, memo=None):
    if memo is None:
        memo = {}

Default values are created once, when the function is created, not every time you call the function. This is called “early binding” of the default value for the parameter.

So the default value for memo gets initialised once, and then the same dict object is used each time you use the default.

See the FAQ for this issue.

If you want to use a different dict each time, you need to create a new dict each time the function is called. The easiest way to do this is:

def canSum(targetSum, numbers, memo=None):
    if memo is None:
        memo = {}
    ...

Thank you for pointing that out. Wouldn’t that mean that the objects stay in memory after the function call? That would be potentially problematic for large objects if I understand correctly, no?

Get BlueMail for Android

Thank you. I keep forgetting that functions are themselves objects.

Get BlueMail for Android

Objects stay in memory so long as they are used. When they are no longer in use, the garbage collector reclaims them.

Unless the object is truly enormous, you shouldn’t worry about it – and remember that’s enormous to you probably isn’t to the computer.