Understand the idea
A problem you can recognise
A helper usually plans thirty minutes per session, but some students prefer twenty. A default can make the common request simple while preserving an explicit override. That convenience becomes a bug if the default unexpectedly remembers changes from an earlier call.
We will examine where argument values come from, where names are visible, and when default values are created. These details explain behaviour that otherwise feels inconsistent.
Understand the idea
Understand the idea
Parameters name the inputs a function accepts. Positional arguments are matched by position, while keyword arguments identify their parameter names explicitly. A default provides a value when the caller omits that argument. Choose defaults that make sense for the operation rather than using them to hide a required decision.
Default expressions are evaluated when the function definition executes, not freshly on every call. A mutable default such as items=[] can therefore be shared across calls. Use None as a sentinel and construct a new list inside the function when each omitted argument should mean a fresh collection. Passing an existing list intentionally still permits the function to mutate that list, so document that contract.
Assignments inside a function normally create local bindings. Reading an outer value is different from assigning an outer binding. Prefer returning a new value and assigning it in the caller over reaching for global state. This keeps the source of a change visible and makes the function easier to reuse. A function parameter is a local name bound to the supplied object, not an automatic deep copy of that object.
See how it works
Example 1: Use a default intentionally
PYTHON exampleRead the code, then try a change.
Try this example
def total(days, minutes=30):
return days * minutes
print(total(5))
print(total(minutes=20, days=3))
Expected output:
ReferenceCompare this with the explanation.
150
60
The omitted minutes argument receives its default on the first call. Explicit keywords make the second call readable without depending on argument order.
Your turn
Example 2: Build a fresh optional list
PYTHON exampleRead the code, then try a change.
Try this example
def record(value, items=None):
if items is None:
items = []
items.append(value)
return items
print(record("HTML"))
print(record("Python"))
Expected output:
ReferenceCompare this with the explanation.
['HTML']
['Python']
Each omitted items argument causes a new list to be constructed inside that call. The second result does not accidentally contain the first call’s subject.
See how it works
Example 3: Separate local and caller bindings
PYTHON exampleRead the code, then try a change.
Try this example
minutes = 30
def adjust(value):
minutes = value + 5
return minutes
print(adjust(10))
print(minutes)
Expected output:
ReferenceCompare this with the explanation.
15
30
The assignment in adjust creates a local name. Returning its value does not overwrite the caller’s minutes binding. The caller can explicitly store the result if that is desired.
Understand the idea
Think through the important decisions
At each call, identify which parameter receives an explicit argument and which uses its default. A positional argument is matched by position; a keyword argument names the parameter. Read the function's signature before inferring meaning from the call's English-looking values.
Local assignment creates or rebinds a name in the function's local scope unless another explicit language rule applies. A local name with the same spelling as an outer name does not automatically update that outer binding. Trace the local and outer names separately, then observe the outer value after the function returns.
Defaults are evaluated when the function definition executes, not newly for every call. A mutable default can therefore be shared across calls. The common None-sentinel pattern creates a fresh collection inside the function when no collection was supplied. Explain the lifetime of the object rather than memorising that None is somehow always a better default.
Check your understanding
Check the reasoning
Use a fresh collection inside the call instead of a mutable default shared between calls.
A matching screenshot or output is one check, not proof that a program is correct for all inputs. Change one input, predict the new result and run again. If the result differs, find the first instruction where your prediction stops matching the program state. Keep that small case as a regression check when you fix the cause.
Spot the mistake
The mistake worth understanding
Do not use a mutable default as hidden storage unless that shared state is intentionally part of the design. Also avoid assuming that assigning to a local variable changes the caller's variable. Mutating a shared object and rebinding a local name are different operations.
Your turn
Independent practice
Write a function that returns a fresh list of revision tasks when no list is supplied. Call it twice and prove the results do not accumulate unintentionally. Add one optional keyword argument and explain its default.
Your turn
Practice hint
Use items=None, then test items is None inside the function.
Your turn
Try a different case
Predict two consecutive calls, not just the first successful one. Then compare an omitted optional argument with an explicitly supplied argument. Explain which values are shared and which are newly created for each call.