How it works
A default expression is evaluated when the def statement executes, not every time the function is called. Repeated calls that omit that argument reuse the same default object.
Immutable defaults such as numbers and strings are usually straightforward. A mutable list or dictionary can accumulate changes across calls. A None sentinel allows the body to create a fresh container for each omitted argument.
Choices and common mistakes
Use is None rather than a truthiness check when an explicitly provided empty container should be respected.
The sentinel pattern prevents accidental sharing for omitted arguments; it does not prevent intentional mutation of a caller-provided container. Document that separately.
Try it yourself
Write a function that appends a tag to a supplied list or creates a new list when none is supplied. Verify both independent calls and an explicitly shared list.
Syntax
def collect(item, items=None):
if items is None:
items = []
items.append(item)
return itemsExamples
See the shared-list mistake
Observe why a mutable default can retain earlier results.
def collect(item, items=[]):
items.append(item)
return items
print(collect("a"))
print(collect("b"))- The empty list is created once when def executes. Both calls omit items and therefore use that same list.
- append mutates the shared object. The second result includes a from the earlier call rather than starting empty.
- This is an intentional bug demonstration; do not use this signature for independent request data.
- Expected output: ['a'] ['a', 'b']
Create a fresh list per call
Use a sentinel to choose when allocation happens.
def collect(item, items=None):
if items is None:
items = []
items.append(item)
return items
print(collect("a"))
print(collect("b"))- Each omitted argument is None. The body creates a new list on each call, so the results do not share storage.
- The return value is the list after appending. The default itself is never mutated.
- Use this for independent batches, but document that a supplied list will be modified.
- Expected output: ['a'] ['b']
See definition-time evaluation
Distinguish a default value from a live variable lookup.
limit = 3
def take(items, count=limit):
return items[:count]
limit = 1
print(take([10, 20, 30]))
print(take([10, 20, 30], count=limit))- count captures the integer three when def executes. Rebinding limit later does not replace that saved default.
- The second call explicitly evaluates the current limit and supplies one, overriding the default.
- Pass changing configuration explicitly rather than expecting defaults to refresh themselves.
- Expected output: [10, 20, 30] [10]
Best practices
- Use is None rather than a truthiness check when an explicitly provided empty container should be respected.
- The sentinel pattern prevents accidental sharing for omitted arguments; it does not prevent intentional mutation of a caller-provided container. Document that separately.
Runtime & versions
Python interpreter. Feature availability depends on the language and runtime version used by your project; consult the official reference below.
Python language reference