Solved

A function keeps remembering the list from the previous call

Asked by Inès Berger · 643 views · 2 replies

Inès Berger5 messages · 1 solution

This is driving me slightly mad:

def collect(item, into=[]):
    into.append(item)
    return into

print(collect("a"))  # ['a']
print(collect("b"))  # ['a', 'b']  <- why?

I expected a fresh empty list each time. Instead the second call sees the first call's data. There is no global anywhere.

2 replies

Priya NairSolution

The default is evaluated once, when the def statement runs — not on each call. So there is exactly one list, created at import time, and every call that omits the argument appends to that same object.

You can see it directly:

print(collect.__defaults__)   # (['a', 'b'],)

The fix is the standard one — use None as the sentinel and build the list inside the call:

def collect(item, into=None):
    if into is None:
        into = []
    into.append(item)
    return into

This applies to any mutable default: lists, dicts, sets, and also anything whose value is computed at definition time, which is why def f(t=datetime.now()) gives you the time the module was imported rather than the time it was called.

Link
Inès Berger

__defaults__ showing the accumulated list made it click immediately. I had read the explanation before and never really believed it until I saw the object.

Link