You are looking at a closure; the Python compiler marks y
in bar
as a free variable because y
is not assigned to.
y
is a local in foo
and because there is a nested scope that uses that name as a free variable, y
is marked as a closure.
When the code runs, Python creates a closure for y
immediately when creating the bar()
function (it is recreated every time you call foo()
; only the bytecode for the function remains unchanged, a constant attached to the foo
function code).
Only when bar()
is invoked does Python need to look up y
, which means dereferencing the closure, and from there dereferencing the current value of y
.
You can see all this in action with some introspection and disassembly of the bytecode:
>>> import dis
>>> def foo():
... def bar(): return y
... y = 10
... return bar
...
>>> foo()
<function foo.<locals>.bar at 0x10d53ce60>
>>> foo()()
10
This builds a function with a closure and returns it without calling. This allows us to introspect the bar()
function created by foo()
.
>>> dis.dis(foo)
2 0 LOAD_CLOSURE 0 (y)
3 BUILD_TUPLE 1
6 LOAD_CONST 1 (<code object bar at 0x10d5138a0, file "<stdin>", line 2>)
9 LOAD_CONST 2 ('foo.<locals>.bar')
12 MAKE_CLOSURE 0
15 STORE_FAST 0 (bar)
3 18 LOAD_CONST 3 (10)
21 STORE_DEREF 0 (y)
4 24 LOAD_FAST 0 (bar)
27 RETURN_VALUE
Note how the Python compiler has inserted a LOAD_CLOSURE
for y
at the top there. MAKE_CLOSURE
creates the function object with attached closure, for bar
.
>>> dis.dis(foo())
2 0 LOAD_DEREF 0 (y)
3 RETURN_VALUE
All bar()
has to do is dereference the attached closure.
>>> foo.__code__.co_consts
(None, <code object bar at 0x10d5138a0, file "<stdin>", line 2>, 'foo.<locals>.bar', 10)
>>> foo.__code__.co_cellvars
('y',)
>>> foo().__closure__
(<cell at 0x10d5e2c20: int object at 0x10d188940>,)
>>> foo().__closure__[0].cell_contents
10
The closure points to y
, and looking up the contents gives you 10
, as expected.
Compiled languages could do this as well; someone managed to compile Scheme to C, with preserving the closures: Compiling Scheme to C, for example.