[ACCEPTED]-Why are default arguments evaluated at definition time?-language-design

Accepted answer
Score: 41

The alternative would be quite heavyweight 44 -- storing "default argument values" in 43 the function object as "thunks" of code 42 to be executed over and over again every 41 time the function is called without a specified 40 value for that argument -- and would make 39 it much harder to get early binding (binding 38 at def time), which is often what you want. For 37 example, in Python as it exists:

def ack(m, n, _memo={}):
  key = m, n
  if key not in _memo:
    if m==0: v = n + 1
    elif n==0: v = ack(m-1, 1)
    else: v = ack(m-1, ack(m, n-1))
    _memo[key] = v
  return _memo[key]

...writing 36 a memoized function like the above is quite 35 an elementary task. Similarly:

for i in range(len(buttons)):
  buttons[i].onclick(lambda i=i: say('button %s', i))

...the simple 34 i=i, relying on the early-binding (definition 33 time) of default arg values, is a trivially 32 simple way to get early binding. So, the 31 current rule is simple, straightforward, and 30 lets you do all you want in a way that's 29 extremely easy to explain and understand: if 28 you want late binding of an expression's 27 value, evaluate that expression in the function 26 body; if you want early binding, evaluate 25 it as the default value of an arg.

The alternative, forcing 24 late binding for both situation, would not 23 offer this flexibility, and would force 22 you to go through hoops (such as wrapping 21 your function into a closure factory) every 20 time you needed early binding, as in the 19 above examples -- yet more heavy-weight 18 boilerplate forced on the programmer by 17 this hypothetical design decision (beyond 16 the "invisible" ones of generating and repeatedly 15 evaluating thunks all over the place).

In 14 other words, "There should be one, and preferably 13 only one, obvious way to do it [1]": when 12 you want late binding, there's already a 11 perfectly obvious way to achieve it (since 10 all of the function's code is only executed 9 at call time, obviously everything evaluated 8 there is late-bound); having default-arg evaluation 7 produce early binding gives you an obvious 6 way to achieve early binding as well (a 5 plus!-) rather than giving TWO obvious ways 4 to get late binding and no obvious way to 3 get early binding (a minus!-).

[1]: "Although 2 that way may not be obvious at first unless 1 you're Dutch."

Score: 10

The issue is this.

It's too expensive to 26 evaluate a function as an initializer every time the function is called.

  • 0 is 25 a simple literal. Evaluate it once, use 24 it forever.

  • int is a function (like list) that 23 would have to be evaluated each time it's 22 required as an initializer.

The construct 21 [] is literal, like 0, that means "this exact 20 object".

The problem is that some people 19 hope that it to means list as in "evaluate this 18 function for me, please, to get the object 17 that is the initializer".

It would be a crushing 16 burden to add the necessary if statement to 15 do this evaluation all the time. It's better 14 to take all arguments as literals and not 13 do any additional function evaluation as 12 part of trying to do a function evaluation.

Also, more 11 fundamentally, it's technically impossible to implement 10 argument defaults as function evaluations.

Consider, for 9 a moment the recursive horror of this kind 8 of circularity. Let's say that instead 7 of default values being literals, we allow 6 them to be functions which are evaluated 5 each time a parameter's default values are 4 required.

[This would parallel the way collections.defaultdict works.]

def aFunc( a=another_func ):
    return a*2

def another_func( b=aFunc ):
    return b*3

What 3 is the value of another_func()? To get the default for 2 b, it must evaluate aFunc, which requires an eval 1 of another_func. Oops.

Score: 8

Of course in your situation it is difficult 6 to understand. But you must see, that evaluating 5 default args every time would lay a heavy 4 runtime burden on the system.

Also you should 3 know, that in case of container types this 2 problem may occur -- but you could circumvent 1 it by making the thing explicit:

def __init__(self, children = None):
    if children is None:
       children = []
    self.children = children
Score: 7

The workaround for this, discussed here (and very solid), is:

class Node(object):
    def __init__(self, children = None):
        self.children = [] if children is None else children

As 6 for why look for an answer from von Löwis, but 5 it's likely because the function definition 4 makes a code object due to the architecture 3 of Python, and there might not be a facility 2 for working with reference types like this 1 in default arguments.

Score: 7

I thought this was counterintuitive too, until 25 I learned how Python implements default 24 arguments.

A function's an object. At load 23 time, Python creates the function object, evaluates 22 the defaults in the def statement, puts them 21 into a tuple, and adds that tuple as an 20 attribute of the function named func_defaults. Then, when 19 a function is called, if the call doesn't 18 provide a value, Python grabs the default 17 value out of func_defaults.

For instance:

>>> class C():

>>> def f(x=C()):

>>> f.func_defaults
(<__main__.C instance at 0x0298D4B8>,)

So all calls 16 to f that don't provide an argument will 15 use the same instance of C, because that's 14 the default value.

As far as why Python does 13 it this way: well, that tuple could contain 12 functions that would get called every time 11 a default argument value was needed. Apart 10 from the immediately obvious problem of 9 performance, you start getting into a universe 8 of special cases, like storing literal values 7 instead of functions for non-mutable types 6 to avoid unnecessary function calls. And 5 of course there are performance implications 4 galore.

The actual behavior is really simple. And 3 there's a trivial workaround, in the case 2 where you want a default value to be produced 1 by a function call at runtime:

def f(x = None):
   if x == None:
      x = g()
Score: 5

This comes from python's emphasis on syntax 27 and execution simplicity. a def statement 26 occurs at a certain point during execution. When 25 the python interpreter reaches that point, it 24 evaluates the code in that line, and then 23 creates a code object from the body of the 22 function, which will be run later, when 21 you call the function.

It's a simple split 20 between function declaration and function 19 body. The declaration is executed when 18 it is reached in the code. The body is 17 executed at call time. Note that the declaration 16 is executed every time it is reached, so 15 you can create multiple functions by looping.

funcs = []
for x in xrange(5):
    def foo(x=x, lst=[]):
        return lst
for func in funcs:
    print "1: ", func()
    print "2: ", func()

Five 14 separate functions have been created, with 13 a separate list created each time the function 12 declaration was executed. On each loop 11 through funcs, the same function is executed 10 twice on each pass through, using the same 9 list each time. This gives the results:

1:  [0]
2:  [0, 0]
1:  [1]
2:  [1, 1]
1:  [2]
2:  [2, 2]
1:  [3]
2:  [3, 3]
1:  [4]
2:  [4, 4]

Others 8 have given you the workaround, of using 7 param=None, and assigning a list in the 6 body if the value is None, which is fully 5 idiomatic python. It's a little ugly, but 4 the simplicity is powerful, and the workaround 3 is not too painful.

Edited to add: For more 2 discussion on this, see effbot's article 1 here: http://effbot.org/zone/default-values.htm, and the language reference, here: http://docs.python.org/reference/compound_stmts.html#function

Score: 1

I'll provide a dissenting opinion, by addessing 45 the main arguments in the other posts.

Evaluating 44 default arguments when the function is executed 43 would be bad for performance.

I find this 42 hard to believe. If default argument assignments 41 like foo='some_string' really add an unacceptable amount 40 of overhead, I'm sure it would be possible 39 to identify assignments to immutable literals 38 and precompute them.

If you want a default 37 assignment with a mutable object like foo = [], just 36 use foo = None, followed by foo = foo or [] in the function body.

While 35 this may be unproblematic in individual 34 instances, as a design pattern it's not 33 very elegant. It adds boilerplate code and 32 obscures default argument values. Patterns 31 like foo = foo or ... don't work if foo can be an object like 30 a numpy array with undefined truth value. And 29 in situations where None is a meaningful argument 28 value that may be passed intentionally, it 27 can't be used as a sentinel and this workaround 26 becomes really ugly.

The current behaviour 25 is useful for mutable default objects that 24 should be shared accross function calls.

I would 23 be happy to see evidence to the contrary, but 22 in my experience this use case is much less 21 frequent than mutable objects that should 20 be created anew every time the function 19 is called. To me it also seems like a more 18 advanced use case, whereas accidental default 17 assignments with empty containers are a 16 common gotcha for new Python programmers. Therefore, the 15 principle of least astonishment suggests 14 default argument values should be evaluated 13 when the function is executed.

In addition, it 12 seems to me that there exists an easy workaround 11 for mutable objects that should be shared 10 across function calls: initialise them outside 9 the function.

So I would argue that this 8 was a bad design decision. My guess is that 7 it was chosen because its implementation 6 is actually simpler and because it has a 5 valid (albeit limited) use case. Unfortunately, I 4 don't think this will ever change, since 3 the core Python developers want to avoid 2 a repeat of the amount of backwards incompatibility 1 that Python 3 introduced.

More Related questions