[Python-ideas] Syntax for late-binding of arguments [was: Default arguments ...]
Steven D'Aprano
steve at pearwood.info
Fri May 15 10:44:54 CEST 2009
On Fri, 15 May 2009 03:08:18 pm Stephen J. Turnbull wrote:
> Could you summarize that discussion briefly?
Many newbies, and some more experienced programmers, are confused by the
behaviour of functions when parameters are given default mutable
arguments:
>>> def f(x=[]):
... x.append(1)
... return x
...
>>> f()
[1]
>>> f()
[1, 1]
Some people are surprised by this behaviour, and would prefer that the
default value for x be freshly created each time it is needed. This is
one of the most common, and most acrimonious, topics of discussion on
comp.lang.python. The standard idiom for the expected behaviour is to
insert boilerplate code that checks for a sentinel:
def f(x=None):
if x is None: x = []
x.append(1)
return x
The chances of having the standard behaviour changed are slim, at best,
for various reasons including backward compatibility and runtime
efficiency. Also, I believe Guido has ruled that the standard behaviour
will not be changed.
However, some have suggested that if the standard compile-time creation
of defaults won't be changed, perhaps it could be made optional, with
special syntax, or perhaps a decorator, controlling the behaviour. See
these two proof-of-concept decorators, by Geremy Condra, for example:
http://code.activestate.com/recipes/576751/
http://code.activestate.com/recipes/576754/
I'm not convinced by decorator-based solutions, so I'll pass over them.
I assume that any first-class solution will require cooperation from
the compiler, and thus move the boilerplate out of the function body
into the byte code. (Or whatever implementation is used -- others have
suggested using thunks.)
Assuming such compiler support is possible, it only remains to decide on
syntax for it. Most suggested syntax I've seen has marked the default
value itself, e.g.: def f(x = new []). Some have suggested overloading
lambda, perhaps with some variation like def f(x = *lambda:[]).
I suggest that the markup should go on the formal parameter name, not
the default value: we're marking the formal parameter as "special" for
using delayed semantics, not that the default object (usually [] or {})
will be special.
Some years ago, Python overloaded the binary operators * and ** for use
as special markers in parameter lists. I suggest we could do the same,
by overloading the & operator in a similar fashion: inside the
parameter list, &x would mean to delay evaluation of the default
argument:
def f(x=[], &y=[])
x would use the current compile-time semantics, y would get the new
runtime semantics.
I don't have any particular reason for choosing & over any other binary
operator. I think ^ would also be a good choice.
Tagging a parameter with unary-& but failing to specify a default value
should be a syntax error. Likewise for unary-& outside of a parameter
list. (At least until such time as somebody suggests a good use for
such a thing.)
--
Steven D'Aprano
More information about the Python-ideas
mailing list