6

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().

  • If I were you, I'd accept Michael Shuller's answer (the check mark under the upvote/downvote on his comment). Share the love and show appreciation for his efforts! – rsegal Aug 10 '12 at 03:50
  • @rsegal The answer of Rostyslav Dzinko is depeer. – Veniamin Krol Aug 10 '12 at 14:30
  • True! I should have phrased it better: I was trying to say more that you should shower your favor on _an_ answer, rather than necessarily my favorite. But I did, and do still, feel that Michael's answer more directly addressed your question, pointing out that Python searches up-scope for ambiguous values, which was the behavior you demonstrated! – rsegal Aug 10 '12 at 14:44
  • @rsegal I was interested in the internals. To be honest, I also have not fully satisfied with the answer by Rostyslav Dzinko. I need more details! – Veniamin Krol Aug 10 '12 at 14:55

2 Answers2

5

If you don't assign to foo within the closure, Python resolves it to the foo of the scope one level up (and on up until it finds a foo somewhere or throws an exception).

By mentioning foo within b() in the second example, you put foo into the locals within b(), but it resolves to the foo within the body of a(). If you assign, say, foo = 1 in b(), you would see

 {'foo': 0}
 {'foo': 1}

as the output.

Michael Schuller
  • 494
  • 4
  • 14
4

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.

Rostyslav Dzinko
  • 39,424
  • 5
  • 49
  • 62