1.2.30 · D1Introduction to Programming (Python)

Foundations — Variable scope — LEGB rule (Local, Enclosing, Global, Built-in)

2,760 words13 min readBack to topic

This page has one job: teach every piece of vocabulary the parent note leans on, from "what is a name?" onward — so that by the time you meet the four-letter rule (which we spell out in full at the end), each letter already means something you can see. Nothing here assumes you have coded before.


1. A name is a label, not a box

Look at the figure. The yellow value 5 sits somewhere in memory. The blue label x is an arrow pointing at it. This picture is the whole game:

  • x = 5 — attach label x to the value 5.
  • x = 9 — peel x off 5, re-stick it onto 9. The 5 is untouched; only the arrow moved.

2. A namespace is a page of labels

In the figure, one namespace is drawn as a two-column page. The parent note says namespaces are "backed by a dictionary" — a dictionary is exactly this: a table of keys (names) and values. You will meet that formally in Namespaces and the dict model; here you just need the picture of a page of labels.

Why the topic needs this: the lookup rule is the order in which Python flips through several such pages looking for your name. No pages, nothing to search.


3. A function is a fresh page created on the spot

The figure shows two pages stacked: the module page (always open, at the bottom) and a function page (opened by a call, sitting on top). This stack — pages piling up as calls happen — is the physical thing the lookup rule walks through. You'll go deeper in Functions and Parameters.


4. Nesting — a page opened while another is still open

This "still-open page underneath" is what Closures and Nested Functions is built on. Without nesting, there would be no "enclosing" page to search at all.


5. Built-ins — the page you never wrote


6. Reading vs. writing a name — the two verbs

This distinction is the hinge of the entire topic. Reading triggers the search through pages. Writing does something surprising: it puts the label on the current function's page by default, no search at all. Because a single x = ... marks x as belonging to the current page for the whole function, a read earlier in that function can fail — the famous UnboundLocalError. Keep this split crisp; everything downstream depends on it.

Why "decided in advance" — the compile-time WHY

Look at the figure. In the left panel Python scans and finds count = ..., so it pre-stamps count as belonging to the function's page — before any line runs. In the right panel, execution reaches count + 1 on the first line, tries to read count off the function's page, and finds the label not yet stuck to anythingUnboundLocalError. This is why the decision feels surprising: it was made during the pre-scan, not when the offending line ran.


7. Opting out — global and nonlocal

The default "a write lands on the current page" is only a default. Two keywords let you redirect where a write's label gets stuck.


8. The edge cases: not every block makes a page

A smart trap is assuming any indented block gets its own page. It doesn't. Only three things create a new namespace page.


9. Naming the rule: L → E → G → B

Now every letter means something you can picture, so the acronym is safe to state.

That single sequence — plus the write rule (§6) and the two opt-out keywords (§7) — is the whole parent topic. Every worked example there is just Python walking these four arrows.


Prerequisite map

Read this map bottom-up: each box is one section above, and the arrows mean "you need this before that." The three sources on the left (name, read-vs-write) and the page-machinery in the middle all feed the final rule.

A name is a label on a value

A namespace is a page of labels

A function call opens a new page

Nesting keeps the outer page open

Built-in page is always open at the bottom

Comprehensions and class bodies are special pages

Reading versus writing a name

global and nonlocal redirect a write

Search order Local Enclosing Global Built-in

The full LEGB scope rule

  • name → namespace: you can't have a page of labels until you know a label is just a pointer.
  • namespace → function call: pages come into being because calls create them.
  • read-vs-write → global/nonlocal: the keywords only make sense once you see that writing has a default target you might want to override.
  • everything → the full rule: the acronym is only trustworthy once each letter, plus the write rule and the edge-case pages, is understood.

Equipment checklist

Cover the right side and test yourself. If any answer is fuzzy, re-read that section before tackling the parent note.

A name in Python is best pictured as
a sticky label (an arrow) pointing at a value in memory, not a box holding it.
A namespace is
a page/table mapping names to values, i.e. {name → value}.
What event creates a new namespace page
calling a function — each call opens a fresh, private page.
What are the four page-types the lookup rule searches
Local (current function), Enclosing (outer function), Global (module), Built-in (Python's pre-loaded page).
Where do built-in names like len live
on the built-in page, which Python keeps open at the very bottom and checks last.
The difference between reading and writing a name
reading finds/uses a label (triggers the L→E→G→B search); writing (x = ...) places a label, defaulting to the current function's page with no search.
Why is a write's target "decided in advance"
Python pre-scans the whole function body before running it and stamps every written name as belonging to that page — so even a read above the write uses that page.
What does global x do
redirects writes of x to the module (Global) page instead of making a new local label.
What does nonlocal x do
redirects writes of x to the nearest enclosing function's page; the name must already exist there.
Do for/if blocks create a new page
no — only functions, the module, class bodies, and comprehensions do; names in for/if belong to the enclosing page.
Does a comprehension's loop variable leak out
no — a comprehension has its own private page in Python 3, so the loop variable is gone afterwards (unlike a plain for loop, which leaks).
Can a method see a class-body name as "enclosing"
no — the enclosing search skips class bodies; a method must reach class-body names via self. or ClassName..
The exact search order
Local → Enclosing → Global → Built-in, stopping at the first match, else NameError.

Connections

  • Yeh note Hinglish mein →
  • Functions and Parameters — a call is what opens a new Local page.
  • Closures and Nested Functions — built on the Enclosing (E) page staying open.
  • Namespaces and the dict model — the "page of labels" made precise.
  • NameError and UnboundLocalError — the failure modes of the search and the write rule.
  • Modules and import — how the Global page gets filled.