On Fri, Oct 29, 2021 at 07:17:05PM +1100, Chris Angelico wrote:
* Argument defaults (either in __defaults__ or __kwdefaults__) are now tuples of (desc, value) or (desc,) for early-bound and late-bound respectively * Early-bound defaults get mapped as normal. Late-bound defaults are left unbound at time of function call.
Pardon me if this has already been discussed, but wouldn't it be better to leave defaults and kwdefaults alone, and add a new pair of attributes for late bound defaults? `__late_defaults__` and `__late_kwdefaults__`. Otherwise its a backwards-incompatable change to the internals of the function object, and one which is not (so far as I can tell) necessary. Obviously you need a way to indicate that a value in __defaults__ should be skipped. Here's just a sketch. Given: def func(a='alpha', b='beta', @c=expression, d=None) where only c is late bound, you could have: __defaults__ = ('alpha', 'beta', None, None) __late_defaults__ = (None, None, <code for expression>, None) The None values in __defaults__ mean to look in the __late_defaults__ tuple. If the appropriate value there is also None, return it, otherwise the parameter is late-bound. Evaluate it and return the result. That means that param=None will be a little bit more costly to fill at function call time than it is now, but not by much. And non-None defaults won't have any significant extra cost (just one check to see if they are None). And if you really want to keep arg=None as fast as possible, we could use some other sentinel like NotImplemented that is much less common. The advantage is that code that inspects function objects but doesn't know anything about late-binding will still work, except that it will report late-bound parameters as if they were set to None. Anyway, I care less about the implementation and more about not breaking backwards compatibility when it comes to inpecting function objects.
So far unimplemented is the description of the argument default. My plan is for early-bound defaults to have None there (as they currently do), but late-bound ones get the source code.
That's not what I see currently in 3.10: >>> def func(a=1, b=2, c="hello"): ... pass ... >>> func.__defaults__ (1, 2, 'hello') What am I missing? -- Steve