Lesson 3 of 20 · about 15 min
Environments: where names live
Every name lookup follows a chain of frames, starting where the code is running and moving outward. This environment model explains scope, closures and nonlocal precisely.
🧭 What you need first: Functions that make functions , Scope and the call stack
You’re at work and someone asks “where’s the stapler?” You check your desk first. Not there? You check your team’s shelf. Then the office supply room. Then, as a last resort, you buy one from the store. The first place you find it wins.
Python looks up names exactly like that. It searches a chain of places, called frames, from the most local outward, and uses the first match. That chain is the environment.
The surprising part, and the key to closures: a function’s chain leads to where it was written, not to where it happens to be called. A stapler request from the marketing team always checks the marketing shelf, even if the person asking is visiting the finance floor.
Another way: nicknames in families
In your house, “Mom” means your mom. At a friend’s house, “Mom” means theirs. The same name refers to different people depending on which household (frame) you’re in. When a name isn’t used in your household, you fall back to what the wider world calls it.
Follow the lookups
Step through. When inner runs, it finds y in its own frame, x in outer’s frame, and z in the global frame. Edit the code: add a local x = 100 inside inner and see which x wins.
Watch the code run, one step at a time
- 1z = 1
- 2
- 3def outer(x):
- 4 def inner(y):
- 5 return x + y + z
- 6 return inner(10)
- 7
- 8print(outer(5))
➜ yellow = the line about to run · ✓ = the line that just ran
The real words
- An environment is a sequence of frames. Each frame maps names to values and has a parent frame.
- Calling a function creates a new frame whose parent is the frame where the function was defined. This rule is called lexical scope (or static scope): you can tell which names a function sees just by reading where it’s written.
- Looking up a name: check the current frame; if it’s not there, check its parent; and so on to the global frame, then Python’s built-ins (
print,len…). If no frame has it:NameError. The short version is LEGB: Local, Enclosing, Global, Built-in. - Assigning to a name inside a function creates or updates it in the local frame, even if an outer frame has the same name.
- nonlocal
xsays “when I assign to x, update the x in the enclosing function’s frame.”global xdoes the same for the global frame. Both are signs of mutable state, which lesson 12 explores. - Shadowing: a local name hides an outer name with the same spelling. Legal, but often confusing.
This “environment model” is precisely how Python (and most languages) actually work. In F24 you’ll build an interpreter that implements it.
A counter with memory
make_counter() should return a function. Each time that function is called, it returns the next number: 1, 2, 3… Each counter keeps its own count. You'll need nonlocal.
Defined vs called
x = 'global'. def show(): return x. def caller(): x = 'local'; return show(). What does caller() return?
When a function is called, what is the parent of its new frame?
Got it?
Finish the lesson to color it in on your map and collect its concept cards.
Did this lesson click?