Excellent.
DAY 13 is where Python stops being “a language” and becomes a runtime with stack frames, scopes, and name resolution.

If you master this day:

  • You’ll never be confused by scopes again
  • Closures will feel obvious
  • Recursive bugs will make sense
  • Interview questions on functions become easy points

🧠 DAY 13 — Functions Internals

(Stack Frames, Call Mechanics & LEGB Rule)


🔑 CORE IDEA OF DAY 13

A Python function call creates a stack frame.
A stack frame defines scope, lifetime, and execution context.

Nothing here is abstract — it’s all concrete runtime behavior.


1️⃣ What REALLY Happens When You Call a Function

def f(x):
    y = x + 1
    return y

f(10)

At runtime:

  1. A frame object is created
  2. Parameters are bound
  3. Local namespace is created
  4. Bytecode executes
  5. Frame is destroyed (or reused)

2️⃣ Python Call Stack (Visual Model)

Image
Image
Image
Image

Example:

def a():
    b()

def b():
    c()

def c():
    pass

a()

Call stack (top → bottom):

Frame c
Frame b
Frame a
Global frame

Python always executes the top frame.


3️⃣ What Is a Stack Frame? (CPython Level)

Each frame contains:

  • Local variables (f_locals)
  • Global variables (f_globals)
  • Builtins (f_builtins)
  • Operand stack
  • Instruction pointer

Conceptually:

PyFrameObject
 ├── locals
 ├── globals
 ├── builtins
 ├── value stack
 └── instruction index

4️⃣ Function Objects vs Function Calls (INTERVIEW TRAP)

def f():
    pass
  • f is a function object
  • Nothing executes yet
f()
  • Function object is called
  • Frame is created
  • Code runs

🧠 Functions are objects; calls create frames


5️⃣ Local Variables Are FAST (VERY IMPORTANT)

def f():
    x = 10
    return x

Why locals are fast:

  • Stored in an array
  • Accessed by index
  • No dictionary lookup

This is why:

for i in range(10):
    ...

is faster than global variable access.


6️⃣ LEGB Rule (Name Resolution Order)

When Python sees a name:

Local → Enclosing → Global → Builtins

Example:

x = 100

def f():
    x = 10
    print(x)

f()

Python finds x in local scope first.


7️⃣ Global vs Local Binding (SUBTLE BUT IMPORTANT)

x = 10

def f():
    x = x + 1   # ❌ UnboundLocalError

Why?

  • Assignment makes x local
  • Python looks for local x
  • It doesn’t exist yet

Correct:

def f():
    global x
    x = x + 1

8️⃣ global vs nonlocal (INTERVIEW FAVORITE)

global

  • Refers to module-level variable

nonlocal

  • Refers to enclosing function scope
def outer():
    x = 10
    def inner():
        nonlocal x
        x += 1
    inner()
    print(x)

✔ Output: 11


9️⃣ Closures: Functions Remember Their Environment

def outer(x):
    def inner():
        return x
    return inner

f = outer(10)
print(f())

Why this works:

  • inner captures x
  • Stored in a cell object
  • Survives outer frame destruction

This is the foundation of:

  • Decorators
  • Callbacks
  • Functional patterns

🔟 Why Recursion Is Expensive in Python

def fact(n):
    if n == 0:
        return 1
    return n * fact(n-1)

Each recursive call:

  • Creates a new frame
  • Uses stack memory
  • Has overhead

Python does:

  • ❌ No tail-call optimization
  • ❌ No frame reuse

This is why deep recursion hits:

RecursionError

11️⃣ Inspecting Function Internals (Power Tool)

def f(a, b):
    return a + b

f.__code__.co_varnames
f.__code__.co_consts
f.__defaults__

You can inspect:

  • Bytecode
  • Arguments
  • Constants
  • Defaults

This is how debuggers and profilers work.


🔥 INTERVIEW TRAPS (DAY 13)

Q1

x = 10
def f():
    print(x)
    x = 20

f()

UnboundLocalError


Q2

def f(x):
    def g():
        return x
    return g

h = f(5)
print(h())

5
(Closure)


Q3

Why are locals faster than globals?

✔ Array lookup vs dict lookup
✔ Fixed offsets
✔ No hashing


🧠 DAY 13 MENTAL CHECKLIST

Before debugging functions:

  1. Where is the name defined?
  2. Is assignment happening?
  3. Is closure involved?
  4. How many frames exist?
  5. Is recursion safe here?

📝 DAY 13 ASSIGNMENT (MANDATORY)

1️⃣ Predict output (NO RUNNING):

x = 5

def outer():
    x = 10
    def inner():
        print(x)
    return inner

f = outer()
f()

2️⃣ Explain clearly:

  • Why nonlocal exists
  • Why Python avoids tail-call optimization

3️⃣ Whiteboard challenge:

Draw:

  • Global frame
  • Outer frame
  • Inner frame
    Show variable resolution.

🔜 DAY 14 PREVIEW

DAY 14 — Function Arguments & Call Semantics
(positional-only, keyword-only, defaults, *args, **kwargs traps)

This day destroys:

  • Default argument myths
  • Argument confusion
  • Interview trick questions

When ready, say 👉 “START DAY 14”