[Python-ideas] proto-PEP: Fixing Non-constant Default Arguments

George Sakkis gsakkis at rutgers.edu
Tue Jan 30 05:16:50 CET 2007


> Chris Rebert wrote:
>
> >The following is a proto-PEP based on the discussion in the thread
> >"fixing mutable default argument values". Comments would be greatly
> >appreciated.

As I see it, the main objection to this is the inversal of the current
default semantics: every default argument is treated as if it were
mutable, or re-evaluatable more generally. Although mutable default
arguments are useful some times and would be nice to have, they are
most likely less common than the immutable ones, so the latter should
be the default. I'm sure that the python-devs and the BDFL would have
thought about it quite a bit when the current semantics were decided,
and it's unlikely they'll change their mind now without very good
reasons.

OTOH, a proposal that leaves the current semantics as is and adds a
mechanism to specify default arguments to be evaluated at call-time
rather than definition-time would have more chances. Here's a
proof-of-concept solution that specifies explicitly the re-evaluatable
default expressions as "deferred":


import inspect

class Deferred(object):
    def __init__(self, expr):
        self.expr = expr

def eval_deferreds(func):
    varnames,_,_,defaults = inspect.getargspec(func)
    num_varnames = len(varnames); num_defaults = len(defaults)
    def wrapper(*args, **kwds):
        if len(args) >= num_varnames: # defaults not used here
            return func(*args,**kwds)
        f_locals = dict(zip(varnames,args))
        used_defaults = min(num_defaults, num_varnames-len(args))
        for var,default in zip(varnames[-used_defaults:],
defaults[-used_defaults:]):
            if var in kwds: # passed as keyword argument; don't use the default
                value = kwds[var]
            elif not isinstance(default, Deferred): # non re-evaluatable default
                value = default
            else: # evaluatable default in f_locals
                value = eval(default.expr, func.func_globals, f_locals)
            f_locals[var] = value
        f_locals.update(kwds) # add any extra keyword arguments
        return func(**f_locals)
    return wrapper


#======= example ==============================

W = 1   # some global

@eval_deferreds
def f(x, y=Deferred('x**2+W'), z=Deferred('[]')):
    z.append(x)
    z.append(y)
    return z

from collections import deque
print f(3)                    # [3,10]
W=3; print f(4)           # [4,19]
print f(4,5)                 # [4,5]
print f(-1, z=deque())  # deque([-1,4])


Regards,
George



More information about the Python-ideas mailing list