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
?
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:
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.
As for your example, every time you run
f
the default argument is reinstantiated because you defineg
insidef
. The best way to think of it is to think of thedef
statement as a constructor forfunction
objects, and the default arguments like parameters to this constructor. Every time you rundef some_function
it is like calling the constructor all over again, and the function is redefined as if had writteng = function(a=[])
in the body off
.In response to comment