29

Let we have this code:

def big_function():
    def little_function():
         .......
    .........

The Python documentation says about def statement:

A function definition is an executable statement. Its execution binds the function name...

So, the question is: Does def little_function() execute every time when big_function is invoked? Question is about def statement exactly, not the little_function() body.

shx2
  • 61,779
  • 13
  • 130
  • 153
dondublon
  • 701
  • 1
  • 6
  • 13

2 Answers2

31

You can check the bytecode with the dis module:

>>> import dis
>>> def my_function():
...     def little_function():
...             print "Hello, World!"
...     
... 
>>> dis.dis(my_function)
  2           0 LOAD_CONST               1 (<code object little_function at 0xb74ef9f8, file "<stdin>", line 2>)
              3 MAKE_FUNCTION            0
              6 STORE_FAST               0 (little_function)
              9 LOAD_CONST               0 (None)
             12 RETURN_VALUE  

As you can see the code for the inner function is compiled only once. Every time you call my_function it is loaded and a new function object is created(in this sense the def little_function is executed every time my_function is called), but this doesn't add much overhead.

Bakuriu
  • 98,325
  • 22
  • 197
  • 231
  • 1
    How do you know how much overhead executing the `MAKE_FUNCTION` byte-code instruction involves? Seems like it might require allocating memory which it typically a slow operation in most other languages. – martineau Apr 16 '13 at 07:39
  • 2
    @martineau No, there is no big memory allocation. The `MAKE_FUNCTION` op simply increases the reference code for the code object, it doesn't have to copy. Surely a new function object is created, which involves allocating a new `PyFunctionObject` but considering that *every* operation allocates python object it doesn't hurt performances "significantly"(obviously what is significant depends on the rest of the code of `my_function`). – Bakuriu Apr 16 '13 at 07:45
8

The code in the inner function is compiled only once, so there shouldn't be a significant runtime penalty.

Only inner's function closure gets updated each time the outer function is called. See here, for example, for more details about closures.

Here's a quick demonstration, examining the closure:

def f(x):
   a = []
   b = x + 1
   def g():
      print a, b
   return g

In [28]: y = f(5)

In [29]: y
Out[29]: <function __main__.g>

In [30]: y.func_closure
Out[30]: 
(<cell at 0x101c95948: list object at 0x101c3a3f8>,
 <cell at 0x101c958a0: int object at 0x100311aa0>)

In [31]: y.func_closure[1].cell_contents
Out[31]: 6
shx2
  • 61,779
  • 13
  • 130
  • 153