Unit 3.2 · about 3 h
11 / 324Argument binding, scope and closures
Mutable defaults and late-bound closures leak state between calls. Call semantics decide exactly what a caller can observe.
- explain
You can now
- Explain positional, keyword, default,
*args,**kwargs, positional-only and keyword-only binding, including when defaults are evaluated. - Explain block scope,
globalandnonlocal, and closure capture with the late-binding rule. - Predict
UnboundLocalErrorand shared-mutable-default behavior in unseen code.
Covers
- Argument binding, evaluation time and mutable defaults
- Lexical scope, closures and captured state
Study, in this order
6 steps
- Argument binding formsStas' Python Cookbook - selected path
Functions: Arguments (positional, keyword, defaults,
*args/**kwargs, positional-only and keyword-only); the mutable-default bad/good pair - Call by assignmentStas' Python Cookbook - selected path
Functions: Pass-by-object-reference (mutating a mutable argument versus rebinding a name)
- Closures and late bindingStas' Python Cookbook - selected path
Functions: Closures (the
nonlocalcounter); the late-binding trap with the default-argument bind - Binding and resolution of namesPython language reference
Sections 4.2.1 and 4.2.2: binding constructs, block-wide locals,
UnboundLocalError,globalandnonlocal, the class-block scope rule - Default ArgumentsAlgoMaster Python - structured revision
The Default Arguments lesson and Exercise: Default Arguments; recall after the cookbook pass
- ClosuresAlgoMaster Python - structured revision
The Closures lesson and Exercise: Closures; predict each output before checking
Focus on
- Defaults evaluate once at function definition time. The
Nonesentinel plus a fresh list inside the body is the standard fix. - Arguments pass by assignment: rebinding a parameter is invisible to the caller, while mutating a mutable argument is visible.
- Closures capture variables, not values. Loop closures share the final value; a default argument binds the current one.
- A name bound anywhere in a block is local everywhere in it. Reading it before assignment raises
UnboundLocalError;globalandnonlocalredirect the binding.
Reproduce closed-book
make_funcs returning three closures over range(3) so each returns its own index, plus a nonlocal counter starting at 0. State that the naive loop version returns [2, 2, 2] on CPython 3.14.4.Traps
- Believing closures snapshot values. They read the live cell at call time.
- Using
+=on aglobalwithout aglobalstatement. Augmented assignment binds, so the name goes local and the read fails. - Treating every parameter before
*as positional-only. Only parameters before/are;cindef g(a, b, /, c, *, d)still accepts keywords.
Practice
AlgoMaster Python: Exercise: Default Arguments
Exercises 1 to 5, especially Exercise 4: Avoid a Shared List Default and Exercise 5: Distinguish Omitted From None
Do it after the cookbook pass; write the None-sentinel fix from memory.
AlgoMaster Python: Exercise: Closures
Exercises 1 to 5, especially Exercise 3: Inspect Captured Closure Cells, Exercise 4: Update Captured State with Nonlocal and Exercise 5: Avoid Late Binding in a Loop
Predict each closure output on paper before checking.
Self-test
-
def f(x, acc=[]): acc.append(x); return acc. What dof(1)andf(2)return, and what is the fix?f(1)is[1]andf(2)is[1, 2]: one list is shared across calls. Default toNoneand build a fresh list in the body. -
What does
[fn() for fn in [lambda: i for i in range(3)]]give, and why?[2, 2, 2]. Each closure reads the same celli, which holds 2 after the loop. -
x = 10at module level.def f(): print(x); x = 1. What doesf()do?It raisesUnboundLocalError. The assignment makesxlocal for the whole block, so the print reads an unbound local. -
In
def g(a, b, /, c, *, d), which parameters can be passed by keyword?candd.aandbare positional-only and raiseTypeErrorwhen passed by keyword.
Completion criterion
Stop when
You fix a mutable-default and a late-binding bug on first reading, and your make_funcs returns [0, 1, 2].
Leads to
- 3.5 Decorators, wrappers and parameterized decorators Closures become wrappers; factories reuse default-argument binding.
- 3.3 Attribute lookup, object protocols and dataclasses Class blocks scope names differently from function blocks.
Comes after
- 3.1 Names, identity, mutability, copying and hashabilityFollow mutation across call boundaries and mutable defaults.
Resources for this unit
- Stas' Python Cookbook - selected path Run Part I on dictionaries, iteration, functions, closures, decorators, classes and context managers; Part II on modules, files, paths, environment and arguments; and Part IV on versions, dependencies, packaging and code quality. Finish by building one src-layout package from a blank directory.
- Python language reference Check Data model, Execution model, Import system and Exceptions whenever a claim depends on Python semantics. These pages are the authority when the course and a runtime observation disagree.
- AlgoMaster Python - structured revision Use Functions, Modules and Packages, OOP, Advanced OOP, Exception Handling, Iterators and Generators, Type Hints and Annotations only after the cookbook pass. Open it for recall practice or a diagnosed weak mechanism, not as a second complete course.
Also in this section
- Packaging Python Projects Follow the packaging tutorial once from a blank directory, using a src layout, pyproject.toml, an isolated build and a clean-environment installation test.
- dataclasses - data classes Read the
@dataclassparameters (eq,order,frozen,slots),field()withdefault_factory, frozen instances, post-init processing,replace,asdictandastuple. Use for the validated frozen dataclass in attributes-protocols-dataclasses. - typing - type hints and protocols Read Protocols for structural typing and
runtime_checkablelimits, Generics forTypeVargenerics, andassert_typefor narrowing checks. Use for theProtocoland narrowing parts of type-hints-protocols; PEP 695 syntax itself is in the language reference. - gc - cycle garbage collector Read the cycle collector interface:
collect,get_count,garbageanddisable/enable. Use for the cycle-collection and measurement parts of refcounting-gc-weakrefs. - weakref - weak references Read weak-reference objects (
refcallbacks,proxyobjects) and theWeakValueDictionaryexample. Use for the weak-reference part of refcounting-gc-weakrefs.