Functions & Scope
A function is a vending machine: you put coins in the slot (arguments), something happens inside that you cannot see, and a snack drops out (the return value). Write the recipe once, press the button a thousand times. Scope is the machine's private kitchen: variables created inside the function live ONLY inside it โ the kitchen's mess never leaks into the shop, and two machines can both have a bowl called "mix" without fighting. When a function runs, the machine goes onto a stack of "machines currently working"; when it returns, it is lifted off and its whole kitchen is thrown away. That stack of working machines is the call stack โ remember the tray-pile stack from Module 2? Same object. And a machine that presses its own button is recursion, waiting for you at the end of Module 3.
Machines on the stack
step 1 / 6The program itself is a machine already running โ the bottom of the pile.call stack, depth 1
Scope resolution โ LEGB
| layer | meaning | example |
|---|---|---|
| L โ Local | inside the current function | the tax in price_with_tax |
| E โ Enclosing | the function wrapping this one | closures read the outer function's names |
| G โ Global | module top level | constants, imports |
| B โ Built-in | print, len, rangeโฆ | shadowable โ naming a variable `list` breaks list() |
The rules
- Assignment inside a function creates a LOCAL, even if the name exists globally โ reading falls through LEGB, writing does not. `global`/`nonlocal` override (and are usually a design smell).
- Default parameter values are evaluated ONCE at def time: def f(acc=[]) shares one list across every call. Use None + create inside โ the most famous Python bug.
- Arguments pass object references: rebinding a parameter does nothing outside; MUTATING a passed list is visible outside.
- return ends the call immediately; a function without return returns None.
- Each call gets a fresh frame on the call stack (~depth limit 1000 โ sys.setrecursionlimit); frames die at return, taking their locals with them.
def price_with_tax(price, rate=0.08):
tax = price * rate # local: dies at return
return price + tax
total = price_with_tax(10.0) # 10.8
print(total)
# print(tax) # NameError โ the kitchen is gonex = 10. def f(): x = 5. Call f(). What is x now, outside?
price_with_tax(price, rate) returns price + price*rate, rounded to 2 decimals (round(x * 100) / 100). Keep everything local; just return.