Late-binding of function defaults (was Re: What is a function parameter =[] for?)

Ian Kelly ian.g.kelly at gmail.com
Mon Nov 23 11:40:50 EST 2015


On Mon, Nov 23, 2015 at 1:23 AM, Chris Angelico <rosuav at gmail.com> wrote:
> def latearg(f):
>     tot_args = f.__code__.co_argcount
>     min_args = tot_args - len(f.__defaults__)
>     defs = f.__defaults__
>     # With compiler help, we could get the original text as well as something
>     # executable that works in the correct scope. Without compiler help, we
>     # either use a lambda function, or an exec/eval monstrosity that can't use
>     # the scope of its notional definition (since its *actual* definition will
>     # be inside this decorator). Instead, just show a fixed bit of text.

You should be able to get the correct globals from f.__globals__.

For locals, the decorator might capture the locals of the previous
stack frame at the moment the decorator was called, but that's
potentially a pretty heavy thing to be retaining for this purpose; the
definition of a @latearg function would indefinitely keep a reference
to every single object that was bound to a variable in that scope, not
just the things it needs. For better specificity you could parse the
expression and then just grab the names that it uses. Even so, this
would still act somewhat like early binding in that it would reference
the local variables at the time of definition rather than evaluation.

Nonlocals? Just forget about it.

> This does implement late binding, but:
> 1) The adornment is the rather verbose "lambda:", where I'd much
> rather have something shorter
> 2) Since there's no way to recognize "the ones that were adorned", the
> decorator checks for "anything callable"

A parameter annotation could be used in conjunction with the decorator.

@latearg
def x(y: latearg = lambda: []):
    ...

But that's even more verbose. In the simple case where all the
defaults should be late, one could have something like:

@latearg('*')
def x(y=lambda: []):
    ...

The argument could be generalized to pass a set of parameter names as
an alternative to the annotation.

> 3) Keyword args aren't handled - they're passed through as-is (and
> keyword-only arg defaults aren't rendered)

I would expect that Python 3 Signature objects would make this a lot
simpler to handle.



More information about the Python-list mailing list