0

It's intriguing to me why default function parameters are mutable in Python. First learned about this when memoising a dynamic programming algorithm and results were different than for non-memoised solution. When I showed this to one of my colleagues at the office, he opened my eyes to the mutable __defaults__ parameter in python. Another colleague made an observation why is this even a feature? Are there use cases when you want this? I'm not a Python engineer, so I'd like to hear from those that are what are your thoughts on this and experiences dealing with this feature.

The aforementioned code:

def can_sum(target, numbers, memo={}):
    if target in memo: eturn memo[target]
    if target < 0: return False
    if target == 0: return True

    for number in numbers:
        if can_sum(target - number, numbers, memo) == True:
            memo[target] = True
            return True

    memo[target] = False
    return False

print(can_sum(7, [2, 4, 6])) # False, actual result False
print(can_sum(7, [1, 5, 2])) # True, actual result False
print(can_sum(112, [1, 2, 3])) # True, actual result True
print(can_sum(17, [2, 4, 6])) # False, actual result True
print(can_sum(1874, [3, 7, 17])) # True, actual result True

Here's a link to a Python sandbox when you can run it and see this in action: https://www.online-python.com/Z2k4u5z8HA

kamil_k
  • 154
  • 7
  • 1
    It's a design mistake. Defaults should have been reevaluated on each call, like how other languages handle this. I don't know of any other languages that made this mistake. – user2357112 Jan 12 '22 at 07:27
  • What would be a good mental model to work around this? Never use mutable values for default parameters or don't use default parameters at all in production code? – kamil_k Jan 12 '22 at 07:38
  • 1
    The general rule is to use `None` for default parameters if you want the default to be a mutable value, and explicitly check for that (use `is`, not `==` to be idiomatic) and substitute the new default value. If you do want an explicit cache of some sort, your options are a) just assign an attribute to the function; b) if it's specifically to memoize call results, check out `functools.cache`/`functools.lru_cache`. – Karl Knechtel Jan 12 '22 at 07:42
  • But also, reconsider your design. Do you *need* a mutable value? The main reason to pass specifically a mutable value as an argument is so that you can communicate information back by mutating it. If the value is default, there's nothing to communicate back. – Karl Knechtel Jan 12 '22 at 07:43
  • 2
    @kamil_k the mental model is that the value of default arguments is evaluated *once* when the function is defined – juanpa.arrivillaga Jan 12 '22 at 07:45

0 Answers0