I’m with Steven. 

On Fri, Oct 29, 2021 at 06:22 Chris Angelico <rosuav@gmail.com> wrote:
On Fri, Oct 29, 2021 at 11:52 PM Steven D'Aprano <steve@pearwood.info> wrote:
> > Except that that's still backward-incompatible, since None is a very
> > common value.
>
> How is it backwards incompatible? Any tool that looks at __defaults__
> finds *exactly* what was there before: a tuple of default values, not a
> tuple of tuples (desc, value) or (value,) as in your implementation.

It's fundamentally impossible to make this change without some measure
of backward incompatibility. Is it such an advantage to be almost the
same? It means that tools will be correct often enough that people
might not even notice a problem, and then they subtly give a
completely false value. With my scheme, they *always* give a clear and
obviously wrong value, and it's easy to see what has to be done.

Python doesn't, as a general rule, opt for things that encourage
subtle data-dependent bugs.

> With your implementation, it will always lie. Always. Every single time,
> no exceptions: it will report that the default value is a tuple
> (desc, value), which is wrong.

Yes, it will clearly need to be updated for the new version. Instead
of being almost correct, and then flat-out lying in situations where
it should learn a better way.

> With mine, it will be correct nearly always, especially if we use
> NotImplemented as the sentinel instead of None. And the cases that it
> gets wrong will only be the ones that use late-binding. It will never
> get an early-bound default wrong.

I disagree that "correct nearly always" is a good thing.

> > which is basically the same as I have,
> > only all in a single attribute.
>
> Right. And by combining them into a single attribute, you break
> backwards compatibility. I think unnecessarily, at the cost of more
> complexity.

What if, in the future, a third type of optional argument is added -
such as "leave it unbound, no default given"? How would your scheme
handle this? Mine handles it just fine: give a new kind of value in
__defaults__. (Maybe None would work for that.)

> Remember that __defaults__ is writable. What happens if somebody sticks
> a non-tuple into the __defaults__? Or a tuple with more than two items?
>
>     func.__defaults__ = ((desc, value), (descr, value), 999, (1, 2, 3))

Writing to it goes through a checker already. You already can't write
func.__defaults__ = "foo".

> So under your scheme, the interpreter cannot trust that the defaults are
> tuples that can be interpreted as (desc, value).

Technically that's true in my current implementation, because I
haven't written the proper checks, but the interpreter is in full
control of what goes into that attribute.

> Right, but you said that the early bound defaults **currently** have a
> None there. They don't. The current status quo of early bound defaults
> is that they are set to the actual default value, not a tuple with None
> in it. Obviously you know that. So that's why I'm asking, what have
> I misunderstood?

You've misunderstood what I meant by "currently". Ignore it. That was
just one of the open issues.

> > even do up their own from scratch, I would welcome it. It's much
> > easier to poke holes in an implementation than to actually write one
> > that is perfect.
>
> I've suggested an implementation that, I think, will be less complex and
> backwards compatible. I don't know if it will be faster. I expect in the
> common case of early binding, it will be, but what do I know about C?

I asked for someone to WRITE an implementation, not suggest one. :)
It's easy to poke holes in someone else's plans. Much harder to
actually write something. Go ahead and put some code behind your
words, and then we can test them both.

> > And fundamentally, there WILL be behavioural changes
> > here, so I'm not hugely bothered by the fact that the inspect module
> > needs to change for this.
>
> It's not just the inspect module. __defaults__ is public[1]. Anyone and
> everyone can read it and write it. Your implementation is breaking
> backwards compatibility, and I believe you don't need to.
>
> [1] Whether it is *officially* public or not, it is de facto public.
>

What does "de facto public" mean, and does the language guarantee that
its format will never change?

And, once again, you're offering something that is often correct and
sometimes a flat-out lie, where I'm offering something that adds a
clear and obvious structure. If you naively assume that everything in
__defaults__ is a two-element tuple (which is the case for early-bound
defaults), you'll get an obvious IndexError when you hit a late-bound
default, so even a basic adjustment will still be safe. By your
method, unless something is aware of late defaults, it will subtly get
things wrong.

ChrisA
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-leave@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/BEQ25J4HICMLQVSQNNLGHAZNAXTJ74TL/
Code of Conduct: http://python.org/psf/codeofconduct/
--
--Guido (mobile)