Private Names, Separate Parts
A function's local names are private to its own call, which lets you break a large problem into small, independently testable parts. · 11 min
A function does more than package steps — it seals off the names it makes. A variable created inside a function is local: it exists only inside that function, only while the call runs, and the code outside cannot see it. That privacy sounds like a limit, but it is the opposite. Because each function keeps its own names to itself, you can split one large problem into small functions that never trip over each other, and build them one at a time.
Guess before you learn
What happens when you run this program?
``
def make():
secret = 42
make()
print(secret)
``
It raises a NameError. secret is local to make: it exists only while make() runs and is gone the instant the call ends, so the outer print cannot find it. If you expected 42, keep that pencil mark — this privacy is the whole point of the folio, and it is what makes functions safe to combine.
9–12
3–5
The names a function makes are local — they live only inside that function while it runs. When the call ends, they are gone. Two functions can both use a name like x without clashing, because each x belongs to its own function. That privacy is what lets you build one small function at a time.
6–8
A name created inside a function has local scope: it exists only inside that function, only while the call runs, and cannot be seen from outside. Two different functions may each use total and never interfere — each total is separate. This privacy is the reason you can split a big program into small functions: each one manages its own names, so you can write and test them one at a time. Breaking a problem into named parts this way is called decomposition.
9–12
Every function call gets its own private set of names — its local scope. A variable assigned inside a function is created when the call begins and destroyed when it ends; code outside cannot reach it. This is not a restriction to fight but a tool to use: because a function's names are sealed off, you can reason about it in isolation, sure it will not silently change or read anyone else's variables. Decomposition — splitting a task into small functions that each do one job and return a result — turns an overwhelming program into a list of parts you can build and check separately.
K–2
Everyone has their own backpack. What is in your backpack is yours; your friend cannot use it unless you hand it over. A function keeps its own things in its own backpack, private until it hands one back.
Undergrad
Python resolves names by the LEGB rule — Local, Enclosing, Global, Built-in — and assignment inside a function creates a local binding unless declared otherwise. Each call frame owns its locals, so recursion and reuse do not corrupt one another. This encapsulation is the engineering payoff: a well-scoped function has a small, explicit interface — its parameters and its return value — and no hidden channels, which is exactly what makes it independently testable. Decomposition exploits this: express a problem as a composition of such functions, verify each against its contract, and the whole follows.
Postgrad
Lexical scoping fixes each name's binding by program text, resolved along the LEGB chain; a call instantiates a fresh activation record holding the locals, so distinct invocations are non-interfering by construction. Referential transparency at the boundary — outputs determined by inputs, no shared mutable state — is what licenses modular verification: a function's specification abstracts its body entirely. Decomposition is the constructive dual of that abstraction: choose a factoring into subproblems whose contracts compose to the whole, discharge each independently, and defer their internals. Good boundaries, not clever bodies, keep large programs tractable.
local scope
The set of names that exist only inside one function, only while its call runs. Code outside the function cannot see them.
Why is this true?
Why can two different functions each use a variable named count without interfering?
Because each function's names live in its own local scope, created fresh when the call starts and gone when it ends. One function's count and another's are separate names in separate scopes, so assigning one never touches the other.
Trace the scopes: does the inner x change the outer x? — the steps fade as you master them
inside make_x: x = 3
return x inside make_x hands back its local x. What does y hold?y = make_x()
outer x is untouched
print(x) show for the outer x?print(x)
Private names are what make decomposition safe. To decompose a problem is to split it into small functions that each do one job and hand back a result. Because none of them can reach into another's local names, you can write one, test it until it is right, and move on — trusting it will keep working when the next part is added. A function's whole conversation with the rest of the program runs through its parameters coming in and its return value going out.
That completes the functions unit. A function's names are private to its call, so you can decompose any large problem into small parts that each do one job and hand back a result, then test them one at a time. Next unit gives those functions something bigger to work on: lists and strings — collections that hold many values at once, and the everyday algorithms that scan them.
Practice — new ink and old, interleaved
1.What does this print?
``
def mul(a, b):
return a * b
print(mul(6, 7))
``
2.How many times does the body run?
``
for i in range(4):
print('hi')
``
3.What prints?
``
def reset():
score = 0
return score
score = 50
reset()
print(score)
``
4.What prints?
``
age = 20
if age >= 18:
print('adult')
else:
print('minor')
``
5.How many lines does this program print?
``
def pair():
print('a')
print('b')
pair()
pair()
``
6.What is the difference between writing run and writing run()?
7.What does this print?
``
def base():
return 10
def plus5():
return base() + 5
print(plus5())
``
8.How many numbers does range(2, 12) produce?
9.How many times does the body run?
``
i = 0
while i < 4:
i = i + 1
``
10.How many times does the body of this loop run?
``
for i in range(2, 8):
print(i)
``