I can not find an adequate explanation for this behavior.
>>> def a():
... foo = 0
... print locals()
... def b():
... print locals()
... b()
>>> a()
{'foo': 0}
{}
But:
>>> def a():
... foo = 0
... print locals()
... def b():
foo
... print locals()
... b()
>>> a()
{'foo': 0}
{'foo': 0}
I understand that in the second case there is a closure, but I can not find a detailed description of what actually is and under what conditions should return the function locals()
.
locals() built-in function prints local symbol table which is bound to a code object, and filled up when interpreter receives a name in a source code.
Second example, when disassembled, will contain LOAD_GLOBAL foo bytecode instruction in b function code. This LOAD_GLOBAL instruction will move up the scope, find outer foo name and bind it to the code object by adding name offset into co_names attribute of the closure's (function b) code object.
locals() function prints local symbol table (as was said before, co_names attribute of function's code object).
Read more about code objects here.