"# or if you know what the default is:
c = some_default
if not cond: c = 42
func(a, b, c)"

I would argue that, of the three examples; this one is the most problematic because it overly couples your code to an implementation detail of the function and makes it unnecessarily brittle to change.

On top of that, in the case of a sentinel; it might even encourage people to circumvent the "guarantees" that your API relies upon.

For example, I might make a function where the optional parameter could be anything even a None is valid, so I make my own secret sentinel that, as Steven D'Aprano put it
"will *never* in any conceivable circumstances become a valid argument value": 

_default = object()
def func(a, b, c=_default): ...


Now the pattern above violates the "nobody will *ever* pass me this" condition because it imports _default. Then some intern sees your code and starts importing _default and using it elsewhere because it's such a neat pattern...

"The "hack" part comes when, for technical reasons,
a sentinel is used when one isn't logically part of the API."

That's my entire point.

"TBH I think that the name-is-main idiom isn't too bad"

The bad part is a lot of Python newbies see it on day one or two because it's so common and try explaining it without talking about the interpreter and watching their eyes glaze over. You just showed them how to assign values to variables and that unbound variables cause errors and now you're pulling __name__ out of thin air? Where the heck did that come from? Anyway, this is a tangent I promised myself I wouldn't go on...

On Thu, Dec 2, 2021 at 11:16 PM Chris Angelico <rosuav@gmail.com> wrote:
On Fri, Dec 3, 2021 at 3:47 PM Abe Dillon <abedillon@gmail.com> wrote:
> This seems like an exceedingly minor complaint. The obvious answer is: you invoke the default behaviour by not passing the argument. And yes, I know that's less explicit, but that's kinda the whole point of defaults to begin with. It's behavior when the user decides not to specify their own. Nobody's asking "what do I pass to logging.config.fileConfig to explicitly invoke the logging module's default behavior?!" The answer is simply: don't configure logging. It's not rocket science.
>

I'd say it isn't "less explicit". It's actually the ultimate
definition of not doing something: you just... don't do it. The same
line of explanation is important when working with callback functions:

def callback(*a):
    print("Doing stuff")

button.signal_connect("clicked", callback())

Oops, you called the callback instead of passing it to the
signal_connect function. What's the solution? DON'T call the callback
at this point. How do you NOT call something? Well, how do you call
it? You put parentheses after it. So how do you not call it? You don't
put the parentheses after it.

Python is fairly good at giving you syntactic features that do things,
such that you can not do them by omitting them.

So I don't think anyone is truly concerned about the pure "don't pass
the argument" case. The problem is the "maybe pass the argument" case.
That ends up looking like this:

c = 42
if cond: func(a, b)
else: func(a, b, c)

# or
args = {}
if not cond: args["c"] = 42
func(a, b, **args)

# or if you know what the default is:
c = some_default
if not cond: c = 42
func(a, b, c)

The first two will, of course, continue to work. The second one is a
fairly clear way to indicate whether or not a parameter should get a
value. The problem with the third is that it requires a documented and
future-guaranteed default value, published and promised to work, and
that simply isn't always possible or practical.

So, yes, some forms of default might be a little unclear. And if
you're converting an existing codebase, it may be necessary to
maintain "if c is None: c = some_default" in the body forever, or
until such time as you can remove the support for None as a parameter.
But None isn't truly the default value.

> "Can we agree that, like salt, sugar, and bike-shedding, sometimes you
> can have *too much* explicitness in code?"
>
> Surely *you* would never agree that there's such a thing as *too much* bike-shedding! Isn't that your raison d'être?!
>
> I never said one had to be a Nazi about stamping out every use of the sentinel... er... idiom (if you prefer it to hack).

Idiom. Sentinels are extremely important, and PEP 671 isn't proposing
to eliminate them. The "hack" part comes when, for technical reasons,
a sentinel is used when one isn't logically part of the API.

I agree with Steve here: sometimes, the true default is extremely
clunky, and it's better to use a sentinel. That's part of why they're
never going away (and None will continue to be a popular sentinel).
PEP 671 doesn't propose changing every function ever written, because
the vast majority of them are perfectly fine as they are.

> "The caller's perspective is important. The caller has to read the
> function signature (either in the documentation, or if they use
> `help(function)` in the interpreter, or when their IDE offers up
> tooltips or argument completion). The caller has to actually use the
> function. We should consider their perspective."
>
> I never said we shouldn't. That's just not what I was talking about at that time and you were misinterpreting my argument.

Yes, absolutely. And that's why the function should be able to put the
information in the signature when it's appropriate to, and in the body
when it isn't.

> Just because it's an idiom, doesn't mean it's actually good. I think the `if __name__ == "__main__":` idiom is a horrible, round-about way of saying "if this file is being run as a script". You might look at that and say "What do you mean? That's exactly what it says!" because you've seen the idiom so many times, just like how you think "x=None" explicitly says "give x the default value", but if you look closely `if __name__ == "__main__":` *doesn't* actually say anything like "if this file is being run as a script", it says a bunch of esoteric and confusing garbage that you've learned to read as "if this file is being run as a script".
>

TBH I think that the name-is-main idiom isn't too bad, but mainly
because I've seen far worse idioms. If someone has a better proposal,
I'd be happy to hear it, but the existing idiom isn't horrible.

(But you're right. Well-known idioms can indeed be really REALLY bad.
Just look at the sorts of things that pre-C99 C code would do.)

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/QM2ALIHLSDK3KT7MIGPMHDD6UX55YNBP/
Code of Conduct: http://python.org/psf/codeofconduct/