Skip to content

A default is computed once

Level: 201 · for Python programmers

One line: A default argument is evaluated once, when def runs, and the one object it produced is handed to every call that leaves the argument out — so def add(item, acc=[]) accumulates across calls, def stamp(n=next(count)) stamps the same number forever, and None is the idiom because it moves the work inside the call.

def add(item, acc=[]):
    acc.append(item)
    return acc

add(1)      # [1]
add(2)      # [1, 2]

def is a statement, and it runs like any other: in order, once, at the moment control reaches it, which for a module-level function is when the module is imported. Part of what it does is evaluate every default expression and store the resulting objects on the new function, in __defaults__. A call that omits the argument then binds the parameter name to that stored object. That binding is the rule from Assignment does not copy: no copy is made, so if the object can change and the function changes it, the change is still there for the next call.

The trap is therefore not "mutable defaults". It is a default that the function mutates, and a default whose expression you expected to run again. The same fact is a feature the moment you want it: a cache that outlives the call, or a loop variable frozen at the value it had.

The run

Verified output of a_default_is_computed_once_py.py — regenerated by tools/run_examples.py, never hand-typed.

1. THE DEFAULT IS ONE OBJECT, MADE WHEN def RAN
     def add(item, acc=[]): acc.append(item); return acc
     add(1)                                   [1]
     add(2)                                   [1, 2]         the same list, still holding the first call's item
     add(3)                                   [1, 2, 3]
     add.__defaults__                         ([1, 2, 3],)   where the one list lives
     add(4, [])                               [4]            a list you pass is yours; only the default is shared
     add.__defaults__                         ([1, 2, 3],)   and the default did not change

2. WHEN THE DEFAULT IS EVALUATED
     def stamp(n=next(count)): return n
     stamp()                                  1
     stamp()                                  1              evaluated once, at def -- not once per call
     next(count)                              2              the counter moved once, when def ran

3. THE IDIOM: None MEANS 'MAKE A NEW ONE'
     def add2(item, acc=None):
         if acc is None:
             acc = []
         acc.append(item)
         return acc
     add2(1)                                  [1]
     add2(2)                                  [2]            a fresh list per call

4. A DEFAULT THAT CANNOT CHANGE IS SAFE
     def tally(item, seen=()): return seen + (item,)
     tally(1)                                 (1,)
     tally(2)                                 (2,)           + built a new tuple; the default is still ()
     tally.__defaults__                       ((),)

5. A DATACLASS REFUSES THE TRAP, AND NAMES THE FIX
     @dataclass                               ValueError
     class Bag:
         items: list = []
     @dataclass
     class Bag:
         items: list = field(default_factory=list)
     b1, b2 = Bag(), Bag()
     b1.items is b2.items                     False          default_factory ran once per instance

6. THE SAME FACT, USED ON PURPOSE
     fs = [lambda: i for i in range(3)]
     [f() for f in fs]                        [2, 2, 2]      each lambda reads i when called, and i ended at 2
     fs = [lambda i=i: i for i in range(3)]
     [f() for f in fs]                        [0, 1, 2]      a default is computed when the lambda is made
     def fib(n, memo={}):
         if n < 2:
             return n
         if n not in memo:
             memo[n] = fib(n - 1) + fib(n - 2)
         return memo[n]
     fib(30)                                  832040
     len(fib.__defaults__[0])                 29             the default is the cache, and it outlives the call

What the run shows

Section 1: the default is one object, made when def ran. add(1), add(2) and add(3) return [1], [1, 2] and [1, 2, 3] because each call's acc is the same list, and add.__defaults__ shows it: a tuple holding that one list. A list you pass yourself is yours, so add(4, []) returns [4] and leaves the default alone.

Section 2: when the default is evaluated. next(count) ran once, when the def statement did, and stamp() returns 1 on every call. The counter has moved once: the next value is 2. Any expression in that position behaves the same way, and the ones that surprise people are the ones that look like they should be fresh: time.time(), datetime.now(), os.getcwd(), a call that opens a connection.

Section 3: the idiom. A default of None and an if acc is None: acc = [] inside the body move the construction into the call, so add2(1) and add2(2) each get a new list. The test is is None, not if not acc: the second would also replace an empty list the caller passed on purpose. When None is itself a meaningful argument, the same idiom uses a private sentinel object.

Section 4: a default that cannot change is safe. tally shares its default () between calls exactly as add shared its list, and it does not matter: a tuple cannot change, + builds a new one, and tally.__defaults__ still holds the empty tuple. Strings, numbers, None and frozen sets are safe for the same reason. The mutable default is only a trap when the function mutates it.

Section 5: a dataclass refuses the trap, and names the fix. items: list = [] in a @dataclass body raises ValueError before the class exists, because the generated __init__ would hand that one list to every instance. field(default_factory=list) is the fix the message names: a function that is called once per instance, so b1.items is b2.items is False. It is section 3's idiom with the None test done for you.

Section 6: the same fact, used on purpose. Three lambdas built in one loop return [2, 2, 2], because each reads i when it is called, and by then i is 2; lambda i=i: i returns [0, 1, 2], because a default is computed when the lambda is made, which is once per iteration. A closure captures the variable, not the value is that page. fib's memo={} is the other deliberate use: the default is the cache, it outlives every call, and after fib(30) it holds 29 entries, one for each n from 2 to 30. It works, and it is also hidden state that nothing but fib.__defaults__[0].clear() can reset.

If you are coming from C

C has no default arguments, so the question never arises. C++ has them, and answers it the other way: a default argument expression is evaluated at each call, so void f(std::vector<int> v = {}) gives every call its own vector. A Python programmer coming from C++ therefore carries exactly the wrong intuition into section 1.

If you are coming from Rust

Rust has no default arguments either. The caller passes an Option, and the function decides in its own body, with unwrap_or_default() or a match, so the value is constructed at the call every time; a builder does the same for a longer parameter list. The Rust library's Optional arguments ↗ and unwrap_or_default ↗ are that shape. Python's None idiom in section 3 is the same design written by hand.

If you are coming from ABAP

A method's IMPORTING iv_flag TYPE abap_bool DEFAULT abap_false takes a literal or a constant, never an expression, so nothing is evaluated at all, and an importing parameter cannot be written to, so nothing can be accumulated in one. Both halves of this page's trap are unwritable in ABAP. The nearest thing to section 6's cache is a static attribute (CLASS-DATA), which at least says on its face that it is shared. (Not machine-checked — CI cannot run ABAP.)

Try it

  1. Search a codebase you maintain for a default that is a list, dict or set: grep -rnE 'def .*=(\[\]|\{\}|set\(\)|list\(\)|dict\(\))' --include='*.py'. For each hit, read the body for an append, an item assignment or an update on that name.
  2. Find a default that is a call (=time.time(), =datetime.now(), =os.getcwd(), =Path.cwd(), =SomeClass()) and print the function's __defaults__ twice, a minute apart. Section 2 says they will match.
  3. Pick a function of yours with the None idiom and check whether its guard is is None or if not x. Pass it an empty list on purpose and see whether it appends to yours.
  4. Take a class with a list attribute and rewrite it as a @dataclass with items: list = []. Section 5 says it will not get as far as an instance.
  5. Look for a memo={} or cache={} default in code you run, and ask who can clear it between tests.

See also