Scope: global, nonlocal & LEGB
Scope is the set of rules that decides which variable a name refers to, and Python resolves it with the LEGB rule — searching Local, then Enclosing, then Global, then Built-in scopes and using the first match it finds.
Learn Scope: global, nonlocal & LEGB in our free Python course — an interactive lesson with runnable examples, a practice exercise and a quick reference.
Part of the free Python course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
Understanding scope is the difference between confidently sharing state and being baffled by a mysterious UnboundLocalError . Once you know how Python looks up names, the global and nonlocal keywords stop feeling like magic.
When Python sees a name, it searches four scopes in order and stops at the first match: L ocal → E nclosing → G lobal → B uilt-in. Here all four hold an x , and each scope sees the closest one:
This is why you can call len() anywhere — it's found in the Built-in scope, the last stop on the search.
Reading a global needs nothing special. But reassigning one from inside a function requires the global keyword — otherwise the assignment quietly creates a new local instead:
Without global counter , the line counter += 1 would try to read a local counter that doesn't exist yet — and crash. The keyword says "operate on the outer one."
nonlocal is global 's cousin for enclosing scopes. It lets an inner function reassign a variable in the function that wraps it — the foundation of closures that remember state between calls:
This one bites everyone. If a function assigns to a name anywhere in its body, Python marks that name local for the whole function — even on lines before the assignment. Reading it first then fails:
Why can you list.append() to a global list without the global keyword, but can't reassign it? Because mutating an object and rebinding a name are different operations:
Fix this counter closure so it actually increments. Replace each ___ , then run it.
✅ To touch an enclosing function's variable, use nonlocal x , not global x .
✅ Don't name variables after built-ins ( list , dict , str , id , sum ). A local name shadows the built-in for that scope.
Build a closure make_averager() that remembers every number it's given and returns the running average. Use nonlocal to track the total and count.
Lesson complete — scope holds no mystery now!
You can trace any name through the LEGB order, reassign globals with global , build stateful closures with nonlocal , diagnose the UnboundLocalError trap, and tell mutation apart from rebinding. That's a superpower for reading and debugging real code.
🚀 Up next: Custom Exceptions & Exception Chaining — design robust error handling.
Practice quiz
What does the LEGB rule stand for, in search order?
- Loop, Else, Generator, Block
- Late, Early, Global, Bound
- Local, Enclosing, Global, Built-in
- Local, External, Global, Base
Answer: Local, Enclosing, Global, Built-in. Python resolves a name by searching Local, then Enclosing, then Global, then Built-in scopes.
Given x='global', an outer with x='enclosing', and inner with x='local', what does inner print for x?
- local
- global
- enclosing
- UnboundLocalError
Answer: local. LEGB stops at the first match — inner's own Local x wins.
When do you need the global keyword inside a function?
- To read a module-level variable
- To call a built-in function
- Never
- To reassign (rebind) a module-level variable
Answer: To reassign (rebind) a module-level variable. Reading a global needs nothing; rebinding one from inside a function requires 'global'.
What does nonlocal target?
- The module/global scope
- The nearest enclosing function's scope
- The built-in scope
- A brand-new local
Answer: The nearest enclosing function's scope. nonlocal lets an inner function reassign a variable in the function that wraps it.
A make_counter closure uses 'nonlocal count; count += 1'. What do three calls print?
- 1 2 3
- 1 1 1
- 0 1 2
- 3 3 3
Answer: 1 2 3. nonlocal makes the count persist and increment across calls, giving 1, 2, 3.
Why does reading a name then assigning it later in the same function raise UnboundLocalError?
- The name is misspelled
- Python forbids reassignment
- An assignment anywhere makes the name local for the WHOLE function
- The global was deleted
Answer: An assignment anywhere makes the name local for the WHOLE function. Because assignment anywhere marks the name local everywhere, the earlier read hits an uninitialized local.
items=[1,2,3] is global. A function calls items.append(4) with no 'global'. What is items afterward?
append MUTATES the existing object — no assignment, no local — so the global list is changed.
config={'v':1} is global. A function does config={'v':2} with no 'global'. What is the global config after?
- {'v': 2}
- {}
- UnboundLocalError
- {'v': 1}
Answer: {'v': 1}. Rebinding creates a NEW local config; the global is untouched, staying {'v': 1}.
To update an enclosing function's variable from an inner function, you use...
- global
- nonlocal
- static
- extern
Answer: nonlocal. nonlocal targets the enclosing function; global would wrongly look for a module-level name.
Why can you call len() from any scope without importing it?
- It is a keyword
- It is automatically local
- It lives in the Built-in scope, the last LEGB stop
- It is enclosing-scoped
Answer: It lives in the Built-in scope, the last LEGB stop. Built-ins like len, print, and range are found in the B scope, searched last in LEGB.