On Fri, Nov 20, 2015 at 6:46 AM, Chris Angelico <ros...@gmail.com> wrote: > The expressions would be evaluated as closures, using the same scope > that the function's own definition used. (This won't keep things alive > unnecessarily, as the function's body will be nested within that same > scope anyway.) Bikeshed the syntax all you like, but this would be > something to point people to: "here's how to get late-binding > semantics". For the purposes of documentation, the exact text of the > parameter definition could be retained, and like docstrings, they > could be discarded in -OO mode.
Just out of interest, I had a shot at implementing this with a decorator. Here's the code: # -- cut -- import functools import time class lb: def __repr__(self): return "<late-bind>" 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. f.__defaults__ = tuple(lb() if callable(arg) else arg for arg in defs) @functools.wraps(f) def inner(*a,**kw): if len(a) < min_args: return f(*a, **kw) # Will trigger TypeError if len(a) < tot_args: more_args = defs[len(a)-tot_args:] a += tuple(arg() if callable(arg) else arg for arg in more_args) return f(*a,**kw) return inner def sleeper(tm): """An expensive function.""" t = time.monotonic() time.sleep(tm) return time.monotonic() - t - tm seen_args = [] @latearg def foo(spam, ham=lambda: [], val=lambda: sleeper(0.5)): print("%s: Ham %X with sleeper %s" % (spam, id(ham), val)) seen_args.append(ham) # Keep all ham objects alive so IDs are unique @latearg def x(y=lambda: []): y.append(1) return y print("Starting!") foo("one-arg 1") foo("one-arg 2") foo("two-arg 1", []) foo("two-arg 2", []) foo("tri-arg 1", [], 0.0) foo("tri-arg 2", [], 0.0) print(x()) print(x()) print(x()) print(x([2])) print(x([3])) print(x([4])) print("Done!") # -- cut -- 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" 3) Keyword args aren't handled - they're passed through as-is (and keyword-only arg defaults aren't rendered) 4) As commented, the help text can't pick up the text of the function But it does manage to render args at execution time, and the help() for the function identifies the individual arguments correctly (thanks to functools.wraps and the modified defaults - though this implementation is a little unfriendly, mangling the original function defaults instead of properly wrapping). Clock this one up as "useless code that was fun to write". ChrisA -- https://mail.python.org/mailman/listinfo/python-list