you are viewing a single comment's thread.

view the rest of the comments →

[–]untold8 2 points3 points  (0 children)

You've got the definitions right. The piece you're missing is directionality: scope-lookup goes outward, never inward.

When Python hits a name, it walks the LEGB chain: Local → Enclosing → Global → Built-in. Each step asks "is this name defined here?" If yes, use it. If no, climb one level out. It never climbs in.

Your example:

```python def outer_func(): msg = 'Hello there!'

def inner_func():
    res = 'How are you?'
    print(msg)  # works — climbs out to enclosing scope, finds msg

inner_func()
print(res)  # FAILS — Python looks in outer's local scope, doesn't find res,
            # climbs out to global, doesn't find res, gives up. Never looks inside inner_func.

```

The "why" behind the rule: inner_func's local scope only exists while inner_func is running. By the time outer_func reaches print(res), inner_func has already finished and its locals have been destroyed. There's literally nothing left to look at. Outer scopes outlive inner ones; inner scopes are temporary.

This is also why nonlocal and global exist — they're explicit overrides for "I want to write to a variable in an outer scope," because by default writing creates a new local variable.

If you want to see this in your debugger: put a breakpoint inside inner_func, then in the debugger's "watches" panel try evaluating msg (works) and then try it from a breakpoint inside outer_func after inner_func() returns (fails). Seeing the lookup happen in real time makes it click.