Hone

Lessons · Python · the def f(x=[]) trap

The default argument that remembers

A default value is created once, when the function is defined. If it is a list or dict, every call shares the same one.

Hone is a place to practise programming. This is one of its lessons, written out in full and free to read without an account.

What it is for

This is a genuine production bug: a cache or history that quietly carries data from one request into the next. It passes every simple test and fails under real use.

How to think about it

Ask one question of any default you meet: can this thing be changed in place? A list, a dict and a set all can, and all three are shared by every call. Swap the default for None, then make the real one on the first line of the body. The type you build there is whatever that function actually needs -- a list, a dict, a set -- and the guard reads the same either way.

Worked example

def tally(word, seen={}):
Written the way it is usually written. The default LOOKS like it is made fresh on each call.
    seen[word] = seen.get(word, 0) + 1
Nothing is wrong on this line. The bug is on the one above it.
    return seen
print(tally('apple'))
{'apple': 1}
print(tally('pear'))
{'apple': 1, 'pear': 1}
def tally(word, seen=None):
The repair. None cannot be filled up, so there is nothing to carry over.
    if seen is None: seen = {}
A call with no dictionary now builds its own, every time.
    seen[word] = seen.get(word, 0) + 1
    return seen
print(tally('apple'))
{'apple': 1}
print(tally('pear'))
{'pear': 1}

Your turn

Fix the default so calls do not share state.

def log(msg, history=):
    if history is None:
        history = []
    history.append(msg)
    return history

The trap

def f(items=[]) looks right and works the first time. The second call finds the first call's items still there.

Next in Python

Every Python lesson on one page

Practise the def f(x=[]) trap on HoneA question on it now, a coding challenge where there is one, and it is remembered for review. Free, no email needed.