Kuen
Kuen

Reputation: 163

Variable scope for nested functions in python

I have a demo example:

def outer(func):

    def inner(foo):   
        arg_inner=some_value     
        func(arg_inner,foo)     

    return inner

 def third_party(arg1,arg2):
     #do something

     final = outer(third_party)
     final(3)

I wonder how comes the inner function knows the value of func while being called. How the scope of inner function and outer are connected?

Upvotes: 2

Views: 106

Answers (2)

Martijn Pieters
Martijn Pieters

Reputation: 1122142

The compiler connects them.

When compiling outer and inner, the func name in outer is marked as a closure, and a free variable in inner. On execution, the interpreter then knows to attach a reference to the func name from outer to the inner function object as a closure cell.

You can see the result in the disassembly of the bytecode:

>>> import dis
>>> dis.dis(outer)
  2           0 LOAD_CLOSURE             0 (func)
              3 BUILD_TUPLE              1
              6 LOAD_CONST               1 (<code object inner at 0x1079a5ab0, file "<stdin>", line 2>)
              9 MAKE_CLOSURE             0
             12 STORE_FAST               1 (inner)

  4          15 LOAD_FAST                1 (inner)
             18 RETURN_VALUE        
>>> inner = outer(lambda a, b: None)
>>> dis.dis(inner)
  3           0 LOAD_DEREF               0 (func)
              3 LOAD_GLOBAL              0 (arg_inner)
              6 LOAD_FAST                0 (foo)
              9 CALL_FUNCTION            2
             12 POP_TOP             
             13 LOAD_CONST               0 (None)
             16 RETURN_VALUE        

The LOAD_CLOSURE wraps the func name in a closure for inner to use; MAKE_CLOSURE builds a function object (from the byte code object loaded with LOAD_CONST) with attached closure cells as a tuple. In inner, the LOAD_DEREF opcode loads the value from the func closure.

The closure can be found back on the resulting inner function object:

>>> inner.func_closure
(<cell at 0x107a25a28: function object at 0x107a28b18>,)
>>> inner.func_code.co_freevars
('func',)
>>> inner.func_closure[0].cell_contents
<function <lambda> at 0x107a28b18>

so the inner function object carries the closure with it for later dereferencing.

Upvotes: 3

Hesam Qodsi
Hesam Qodsi

Reputation: 1585

look at http://docs.python.org/2/reference/executionmodel.html#naming-and-binding.It contains explanations about scops.

Upvotes: 0

Related Questions