Skip to content
AITroveRead. Build. Understand.
Make this comfortable

Python scope: local assignment, nonlocal state and late binding

Last updated: 30 Sept 20264 min read
tutorial
BeginnerBy AITrove Editorial

Python resolves a name through its enclosing namespaces, while assignment inside a function normally binds a local name.

Download Python source kit

Operation contract

The progress counter keeps one integer inside its closure and uses nonlocal to rebind that enclosing name. The label callbacks capture their current receipt ID through a default argument. These mechanisms solve different problems: nonlocal gives several calls shared state; the bound default gives each callback its own selected value. A closure over a loop variable without that binding would read its final value when called later.

Failure and ownership boundary

The counter is not thread-safe. Its read, addition and replacement form one application transition but not one protected transaction. An immutable captured integer is safe from later mutation; capturing a mutable record would still retain its alias. Python closures: capture loop values at the intended time and Python locks: protect the complete inventory transition make those boundaries concrete.

Working program

python
def progress_counter():
    completed = 0
    def record_completion():
        nonlocal completed
        completed += 1
        return completed
    return record_completion

record = progress_counter()
labels = [lambda receipt_id=receipt_id: receipt_id for receipt_id in (41, 42)]
print(record(), record())
print([label() for label in labels])

Output

Output
1 2
[41, 42]

Costs and limits

Each closure retains its reachable captured objects. Creating n label callbacks takes O(n) time and O(n) retained function/default storage. Growing integer counts also incur digit costs.

Common Mistakes

  • nonlocal changes an enclosing binding, not a global variable.
  • A captured reference does not freeze the referenced object.

Connected lessons

Python closures: capture loop values at the intended time, Python function arguments: avoid shared mutable defaults, Python locks: protect the complete inventory transition.

python
scope-bindings
Storage details