Show all four levels of scope in one program and print which name is resolved at each point.
LEGB is the order that Python follows when it meets a name in your code. It stands for Local, Enclosing, Global, and Built-in. Python looks in those four places one after the other, and the first place that has a matching name is the one that wins.
name = 'global'
def outer():
name = 'enclosing'
def middle():
name = 'local'
return name # L - found in the local scope
def read_enclosing():
return name # E - not local, found one level up
return middle(), read_enclosing(), name
print(outer()) # ('local', 'enclosing', 'enclosing')
print(name) # global
print(len('abc')) # B - len comes from the built-in scopeThe middle function has its own name, so Python finds it in the local scope. The read_enclosing function has no local name, so the search moves up one level and finds the name in the enclosing function. Inside outer itself, the same name resolves to enclosing, because Python checks the enclosing scope before it checks the global one. At module level the name is global, and len is found in the built-in scope because nothing else in the program defines it.
The rule that matters most is this one: a function never reads a variable from the scope of whoever called it. Python works out names when it compiles the code, using the static structure of the program. That is why a function defined inside another function can see the outer function's variables, and why a function defined at module level cannot see the local variables of a caller, even when the caller is another function.
Reading a global from inside a function is perfectly normal, and it needs no special keyword. Writing to a name is the different case. Assigning to a name inside a function creates a local variable by default, which is exactly why you need the global or nonlocal keywords when you want to rebind a name that lives further out.