[Python-ideas] Default arguments in Python - the return - running out of ideas but...

Mike Meyer mwm-keyword-python.b4bdba at mired.org
Sat May 16 20:32:41 CEST 2009


On Sat, 16 May 2009 14:05:25 +0200
spir <denis.spir at free.fr> wrote:

> Le Sat, 16 May 2009 12:21:13 +1000,
> Steven D'Aprano <steve at pearwood.info> s'exprima ainsi:
> 
> > On Sat, 16 May 2009 01:51:31 am spir wrote:
> > > > * requires people to learn one more feature
> > > > (so newbies will still be confused that def f(x=[]) doesn't behave
> > > > as they expect).
> > >
> > > That's the relevant drawback for me.
> > > A solution that does not solve the issue. A new syntactic pattern to
> > > allow call time evaluation of defaults is a (costly) solution for
> > > people who don't need it.
> 
> Let me expand on 'costly'.

By "expand", you mean make things even more costly?

> I stated that defaults are part of a func def, and should be cached when the definition is evaluated, in a way that they are separate from local vars. This means that a local var should not point to the same object as the one cached.
> I did not enter implementation stuff, but this obviously requires, I guess, that defaults are (deep?)copied into locals at call time, when the object is mutable. Pseudo code for
> 
>     def f(arg=whatever)
> 
> # at definition time
>     f.__defaults__["arg"] = whatever
> # at call time
>     if <arg not provided by caller>
>         if <cached object is safely immutable>
>              arg = f.__defaults__["arg"]
>         else:
>              arg = copy(f.__defaults__["arg"])
> 
> The advantage is that if ever "whatever" is a complex expression, it will not be re-evaluated on each call. Unlike with the late-binding proposal.

Right. It'll be *copied*. So consider:

x = 1000000 * [[]]
def f(y=x):
    y[18][0] = None
    y[23] = None
    y[0][1] = None


So instead of simply creating a new reference to the object (which you
get with either the current semantics or the re-evaluate at call time
semantics), you now copy a list with a million elements on every
call. For this case, the current semantics are the only one that works
well: you can put the expression into the call list, and don't need
either an extra variable to avoid rebuilding your long list on every
call (if you re-evaluate the argument) or using a sentinel and
assignment from that extra variable to avoid copying it if you copy
the values.

> As I see it, re-evaluating 'whatever' at call time does not serve any purpose -- except possibly that the result may change at runtime intentionally, which is another topic.

The problem is *not* with the behavior of default values to
arguments. The problem is with the behavior of multiple references to
mutable objects. People who aren't used to object/reference semantics
don't understand them (of course, they don't understand multiple
references to immutable objects either, but that's a different
problem). They will be confused when they body of f above throws an
exception when you don't pass it y - no matter *what* the calling
semantics!

Admittedly, default values for arguments are nastier than other
because people don't see it as multiple references to one object until
it's pointed out. Both copying and reevaluation change that.

    <mike
-- 
Mike Meyer <mwm at mired.org>		http://www.mired.org/consulting.html
Independent Network/Unix/Perforce consultant, email for more information.

O< ascii ribbon campaign - stop html mail - www.asciiribbon.org



More information about the Python-ideas mailing list