1

My understanding is that when Python parses the source code of a function, it compiles it to bytecode but doesn't run this bytecode before the function is called (which is why illegal variable names in functions does not throw an exception unless you call the function).

Default arguments are not instantiated during this initial setup of the function, but only when the function is called for the first time, regardless of whether the arguments are supplied or not. This same instance of the default argument is used for all future calls, which can be seen by using a mutable type as a default argument.

If we put the function inside of another function, however, the default argument now seems to be re-instantiated each time the outer function is called, as the following code shows:

def f(x):
    def g(y, a=[]):
        a.append(y)
        return a

    for y in range(x, x + 2):
        print('calling g from f:', g(y))
    return g(y + 1)

for x in range(2):
    print('calling f from module scope:', f(x))

This prints out

calling g from f: [0]
calling g from f: [0, 1]
calling f from module scope: [0, 1, 2]
calling g from f: [1]
calling g from f: [1, 2]
calling f from module scope: [1, 2, 3]

Does this mean that every time f is called, the bytecode of g is rebuild? This behavior seems unnecessary, and weird since the bytecode of f (which include g?) is only build once. Or perhaps it is only the default argument of g which is reinstantiated at each call to f?

jmd_dk
  • 12,125
  • 9
  • 63
  • 94
  • 2
    Your understanding is wrong. A function definition is executable code, which Python runs whenever the scope it is in is called. For a module-level function, that happens when the module is imported; for a nested function, that happens when the outer function is called. Either way, the default arguments are instantiated at that point. – Daniel Roseman Dec 19 '16 at 21:51
  • Nice, this brings back the symmetry. The inner function is to the outer function what the outer function is to the module. – jmd_dk Dec 19 '16 at 21:54
  • The bytecode is not rebuilt each time, the code object is reused, as stated in [this answer](http://stackoverflow.com/a/7839697/2681632). – Ilja Everilä Dec 19 '16 at 21:55

3 Answers3

3

The inner function is rebuilt using existing bytecode for the inner function. It's easy to see using dis.

>>> import dis
>>> def make_func():
...     def my_func():
...         pass
...     return my_func
>>> dis.dis(make_func.__code__)
  3       0 LOAD_CONST               1 (<code object my_func at [...]", line 3>)
          3 MAKE_FUNCTION            0
          6 STORE_FAST               0 (my_func)

  5       9 LOAD_FAST                0 (my_func)
         12 RETURN_VALUE

Now if you do:

>>> f1 = make_func()
>>> f2 = make_func()
>>> f1 is f2
False
>>> f1.__code__ is f2.__code__
True
Shain
  • 83
  • 6
1

First misconception: "when Python parses the source code of a function, it compiles it to bytecode but doesn't run this bytecode before the function is called (which is why illegal variable names in functions does not throw an exception unless you call the function)." To be clear, your misconception is that "illegal variable names in functions does not throw an exception unless you call the function". Unassigned names will not be caught until the function is executed.

Check out this simple test:

In [1]: def g(a):
   ...:     123onetwothree = a
  File "<ipython-input-5-48a83ac30c7b>", line 2
    123onetwothree = a

Second misconception: "default arguments are not instantiated during this initial setup of the function, but only when the function is called for the first time...". This is incorrect.

In [7]: def f(x=[]):
   ...:     print(x)
   ...:     x.append(1)
   ...:     print(x)
   ...:
   ...:

In [8]: f.__defaults__
Out[8]: ([],)

In [9]: f()
[]
[1]

In [10]: f.__defaults__
Out[10]: ([1],)

In [11]:

As for your example, every time you run f the default argument is reinstantiated because you define g inside f. The best way to think of it is to think of the def statement as a constructor for function objects, and the default arguments like parameters to this constructor. Every time you run def some_function it is like calling the constructor all over again, and the function is redefined as if had written g = function(a=[]) in the body of f.

In response to comment

In [11]: def f(x=h()): pass
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-11-ecbb4a9f8673> in <module>()
----> 1 def f(x=h()): pass

NameError: name 'h' is not defined
juanpa.arrivillaga
  • 88,713
  • 10
  • 131
  • 172
  • I don't buy your example. `123onetwothree` is a syntax error, meaning that bytecote cannot be produced from it. Try some syntactically legal code, e.g. `b = c` instead, where `c` does not exist. – jmd_dk Dec 19 '16 at 21:57
  • For my second misconception: Try `def f(x=h())`, where `h` does not exist. Again, no exception is thrown before calling the function. – jmd_dk Dec 19 '16 at 21:58
  • I thought that is what you meant by `illegal variable names in functions does not throw an exception unless you call the function`. – juanpa.arrivillaga Dec 19 '16 at 21:59
  • @jmd_dk Of course an exception is thrown before calling the function. See the edit – juanpa.arrivillaga Dec 19 '16 at 22:00
  • Ah, you are right. It does not throw an exception if you do it in the inner function though. – jmd_dk Dec 19 '16 at 22:03
  • 2
    @jmd_dk for your second misconception: ["The default values are evaluated at the point of function definition in the defining scope, ..."](https://docs.python.org/3/tutorial/controlflow.html#default-argument-values). Note the "evaluated at the point of function definition": if that happens to be inside another function, you'll not be getting an exception for `def f(x=h())` for example until you call the outer function, or in other words evaluate its body. – Ilja Everilä Dec 19 '16 at 22:08
0

Just look at the bytecode for f with dis:

dis(f)
  2           0 BUILD_LIST               0
              3 LOAD_CONST               1 (<code object g at 0x7febd88411e0, file "<ipython-input-21-f2ef9ebb6765>", line 2>)
              6 LOAD_CONST               2 ('f.<locals>.g')
              9 MAKE_FUNCTION            1
             12 STORE_FAST               1 (g)

  6          15 SETUP_LOOP              46 (to 64)
             18 LOAD_GLOBAL              0 (range)
             21 LOAD_FAST                0 (x)
             24 LOAD_FAST                0 (x)
             27 LOAD_CONST               3 (2)
             30 BINARY_ADD
             31 CALL_FUNCTION            2 (2 positional, 0 keyword pair)
             34 GET_ITER
        >>   35 FOR_ITER                25 (to 63)
             38 STORE_FAST               2 (y)

(snipped for brevity)

The code object loaded for g:

3 LOAD_CONST               1 (<code object g at 0x7febd88411e0, file "<ipython-input-21-f2ef9ebb6765>", line 2>)

doesn't contain any mutable structures, it just contains the executable code and other immutable information. You could take a peek at it too:

dis(f.__code__.co_consts[1])
  3           0 LOAD_FAST                1 (a)
              3 LOAD_ATTR                0 (append)
              6 LOAD_FAST                0 (y)
              9 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
             12 POP_TOP

  4          13 LOAD_FAST                1 (a)
             16 RETURN_VALUE

Every time f is called, MAKE_FUNCTION is called which re-creates the function from the byte code that already exists there.

Dimitris Fasarakis Hilliard
  • 150,925
  • 31
  • 268
  • 253
  • But why design the language to do this re-create though? Surely it's bad for performance? – jmd_dk Dec 19 '16 at 22:08
  • 1
    @jmd_dk but what if a free variable affects the what the inner function is defined? That is a common pattern with closures (e.g with function factories), and the function has to be recreated. It's *suppose* to be recreated. – juanpa.arrivillaga Dec 19 '16 at 22:10
  • True. So basically a function is "cleaned up" when it goes out of scope, whether that scope is another function or a module, just like any other variable. That actually makes sense. – jmd_dk Dec 19 '16 at 22:31
  • @jmd_dk I'm not sure exactly what you mean, but a function is cleaned up like any other object: when the reference count becomes 0. – juanpa.arrivillaga Dec 19 '16 at 22:37
  • The code object for `g` is contained inside the code of `f` (a reference that keeps it "alive"), the function that's created with `MAKE_FUNCTION` (an object of `type function` is cleared up when execution of `f` finishes as a local var. – Dimitris Fasarakis Hilliard Dec 19 '16 at 22:42