Python Interview Questions on Function Arguments and Scope
Python binds arguments to parameter names when a function is called, but evaluates a default argument when the function definition runs. This distinction explains many questions about shared defaults. Scope questions add another step: work out where a name is looked up when the function executes.
By Ravindra Wadile · Published

649 words · About 3 min read
Python binds arguments to parameter names when a function is called, but evaluates a default argument when the function definition runs. This distinction explains many questions about shared defaults. Scope questions add another step: work out where a name is looked up when the function executes.
The examples use ordinary Python 3 functions. They are small teaching cases, not production recommendations to avoid every default argument or every closure.
Why can a default list retain earlier values?
A mutable default can be the same object across several calls. If the function appends to that object, a later call can observe the earlier append. The problem is shared state that the caller did not expect.
def add_label(label, labels=None):
if labels is None:
labels = []
labels.append(label)
return labels
first = add_label("draft")
second = add_label("ready")
assert first == ["draft"]
assert second == ["ready"]
provided = ["review"]
assert add_label("done", provided) is provided
assert provided == ["review", "done"]
This version creates a fresh list only when the caller supplies no list. When the caller supplies a list, the function deliberately mutates it. That is an important part of the contract. Replacing the default with None does not make the function pure or prevent all sharing.
Why use an explicit None check?
An empty list is false in a Boolean test. If you use if not labels, you also replace an empty list supplied by the caller. An explicit identity check distinguishes “no list was provided” from “the provided list currently has no entries.”
There are cases where None is itself a meaningful value. A separate sentinel object can represent an omitted argument then. Choose that extra machinery only when the API needs the distinction.
What does a closure capture?
A closure can retain access to a surrounding binding. Do not assume it stores a frozen copy of every value mentioned in its body. When several callbacks look up the same surrounding variable later, the timing of that lookup matters.
def build_checks():
checks = []
for limit in (2, 4, 6):
checks.append(lambda value, bound=limit: value < bound)
return checks
checks = build_checks()
assert [check(3) for check in checks] == [False, True, True]
Here each default bound is evaluated while that particular function is created. The three checks therefore use three different bounds. Explain why this works rather than describing the extra parameter as a magical closure fix.
When would nonlocal be useful?
An inner function that needs to rebind an enclosing function's variable can declare it nonlocal. That is different from changing the contents of an object already referenced by that variable. A small counter closure is a useful example, but ask whether explicit object state would be clearer for a larger feature.
A global name refers to the module-level namespace. Adding global merely to silence an error can create a harder shared-state problem. First decide who should own the state and how callers should control it.
How to explain an argument-binding error
List the positional arguments, then match any keyword arguments. Check whether one parameter receives two values, whether a required parameter is missing and whether the signature allows the supplied keyword. Read the signature before guessing that Python has chosen a surprising overload.
For a practice variation, change the helper to return a new list without modifying a supplied one. Write assertions for both versions. They are two different contracts, so one is not automatically more correct than the other.
Questions worth asking yourself
- When was the default object created?
- Which binding does this callback read later?
- Does this operation mutate an object or rebind a name?
- What should happen to a caller-provided empty list?
These checks turn a common output puzzle into a useful design discussion about state ownership and a clear function contract.
References and next practice
docs.python.org — py calls · docs.python.org — py model
Browse interview topics · Practise a question in Interview Challenge · How to Answer Technical Interview Questions Out Loud