0

When I write the following code, my IDE warns about a "mutable default argument":

VALUES = [ 1, 2, 3 ]

def f(values=VALUES):
  print(values)

It proposes to write this instead:

VALUES = [ 1, 2, 3 ]

def f(values=None):
  if values is None:
    values = VALUES
  print(values)

Of course, this avoids having a (mutable) list as a default argument, and my IDE doesn't complain anymore. It appears to be an improvement.

But on the other hand, what was the original risk? The original risk was that f() could modify the list in its code, e. g. like this:

VALUES = [ 1, 2, 3 ]

def f(values=VALUES):
  values.append(4)
  print(values)

This would have changed the value of VALUES permanently (i. e. not just locally) and later calls to f() would still have the changed value as a default which is probably not intended. Furthermore, given the code above, VALUES would grow by one 4 with each call to f(). So this situation probably isn't as intended unless values in f() isn't changed which is hard to enforce.

But does the IDE-proposed "fix" solve the issue?

I think not because if the code of f() now contained a values.append(4), the same thing would happen again:

def f(values=None):
  if values is None:
    values = VALUES
  values.append(4)
  print(values)

Again, each call would make VALUES grow by one 4.

A real fix would be this:

def f(values=None):
  if values is None:
    values = VALUES.copy()  # or VALUES[:] or similar
  values.append(4)
  print(values)

But this the IDE does not propose and of course it would be costly because copies are made each time.

Other questions already deal with this problem (e. g. Why does using None fix Python's mutable default argument issue?) but in that ticket they do not use a constant like VALUES but a literal [] which creates a new value each time the code is executed, so it is similar to the .copy() approach.

Now the questions:

Why does any IDE propose this fix (in this case with a constant)? Does it maybe fix other aspects of this problem I haven't thought about?

Is there a better way to fix the original problem which doesn't have at least one of the two problems (① default value might change unexpectedly, ② costly copying of a value)?

Alfe
  • 56,346
  • 20
  • 107
  • 159
  • Aren't proposed code replacements a detail of each IDE? Have you tried different IDEs? Do they all suggest the same replacement? Alternatively, you could file a new ticket (and search if there is one similar already) in the online issue tracker of your IDE. – Ralf Feb 11 '19 at 11:52
  • @Ralf Of course it's an IDE detail what code replacement is proposed. But it's not an IDE detail whether this proposal makes sense or not. My question is about the latter: Does such a replacement make sense at all because of an aspect I haven't thought about yet. The ones I thought about make it seem useless, so I ask the community if there are other aspects to consider. – Alfe Feb 11 '19 at 13:02
  • 1
    I don't think any IDE really checks the _value_ (or its origin, for that matter). In my experience they only care if the value's type is mutable or not. IMHO this check only tries to enforce the principle of least astonishment, especially in the context of the literal `[]`, where inexperienced developers might expect every call to the function to get a fresh, empty list. I would not even consider the change unexpected in your case. You explicitly `append` to what might legally be your list named `VALUES`. – shmee Feb 11 '19 at 13:50

0 Answers0