On Thu, Dec 2, 2021 at 7:31 PM Paul Moore email@example.com wrote:
On Wed, 1 Dec 2021 at 22:27, Greg Ewing firstname.lastname@example.org wrote:
On 2/12/21 4:40 am, Paul Moore wrote:
the intended use is that people must supply a list[int] or not supply the argument *at all*.
I don't think this is a style of API that we should be encouraging people to create, because it results in things that are very awkward to wrap.
Hmm, interesting point, I agree with you. It's particularly telling that I got sucked into designing that sort of API, even though I know it's got this problem. I guess that counts as an argument against the late bound defaults proposal - or maybe even two:
- It's hard (if not impossible) to wrap functions that use late-bound defaults.
- The feature encourages people to write such unwrappable functions
when an alternative formulation that is wrappable is just as good.
(That may actually only be one point - obviously a feature encourages people to use it, and any feature can be over-used. But the point about wrappability stands).
Wrappability when using an arbitrary sentinel looks like this:
_SENTINEL = object() def f(value=_SENTINEL): if value is _SENTINEL: value = ...
# in another module def wrap_f(): othermodule.f(othermodule._SENTINEL)
def wrap_f(): othermodule.f(othermodule.f.__defaults__)
I'm not sure either of these is any better than the alternatives. You're reaching into a module to access its internal implementation details. Is that really better than messing with *args?
def wrap_f(): args =  or  # pass or don't pass? othermodule.f(*args)
It only looks easy when None is used, and even then, it's largely an implementation detail.