Assignment makes a name local¶
Level: 201 · for Python programmers
Stub — an outline, not a lesson. There is no runnable example behind this page yet, so nothing on it has been through the check that backs every other claim in this library. The bullets below are the questions the finished page has to answer.
One line: The compiler reads the whole function before any of it runs and decides where each name lives, and one assignment to a name anywhere in the body makes it local everywhere in the body — so a total += 1 on the last line turns a print(total) on the first line into UnboundLocalError, and global and nonlocal are declarations to the compiler, not operations.
What the finished page has to answer¶
- The order a name is looked up in, local, enclosing, global, builtin, and the fact that the kind of a name is fixed at compile time while its value is found at run time.
co_varnames,co_freevarsandco_nameson a function's__code__as the three lists the compiler produced, shown as a dated table rather than a key, because bytecode details move between releases. - What counts as an assignment:
=,+=,del, afortarget,with … as,except … as,import, adeforclassstatement, and a walrus. Each one makes the name local to the enclosing function. UnboundLocalErroris aNameError, and the message since 3.11 says what happened: the name is local and has no value yet.globalandnonlocal: where each is allowed, thatnonlocalneeds an enclosing function scope and will not reach a class body or the module, and that aglobalat module level is a no-op.- The class-body scope, which nested functions and comprehensions do not see, so a comprehension in a class body cannot read a class attribute two lines above it.
- What
locals()returns after PEP 667 (3.13): a snapshot in a function, the live namespace at module and class level, dated.
See also¶
- A closure captures the variable, not the value: section 6 is this page in one row
- Naming and binding ↗ in the language reference, and Why am I getting an
UnboundLocalErrorwhen the variable has a value? ↗ in the FAQ - PEP 667 ↗: the
locals()change - Scope is about names ↗ in the Rust library