
On Sun, Oct 31, 2021 at 3:16 PM Steven D'Aprano <steve@pearwood.info> wrote:
On Sun, Oct 31, 2021 at 12:29:18PM +1100, Chris Angelico wrote:
That's optional parameters. It's entirely possible to have optional parameters with no concept of defaults; it means the function can be called with or without those arguments, and the corresponding parameters will either be set to some standard "undefined" value, or in some other way marked as not given.
That's certainly possible with languages like bash where there are typically (never?) no explicit parameters, the function is expected to pop arguments off the argument list and deal with them as it sees fit. Then a missing argument is literally missing from the argument list, and all the parameter binding logic that the Python interpreter handles for you has to be handled manually by the programmer. Bleh.
We could emulate that in Python by having the interpreter flag "optional without a default" parameters in such a way that the parameter remains unbound when called without an argument, but why would we want such a thing? That's truly a YAGNI anti-feature.
If you really want to emulate bash, we can just declare the function to take `*args` and manage it ourselves, like bash.
Python, so far, has completely conflated those two concepts: if a parameter is optional, it always has a default argument value. (The converse is implied - mandatory args can't have defaults.)
If a mandatory parameter has a default argument, it would never be used, because the function is always called with an argument for that parameter. So we have a four-way table:
Mandatory Optional Parameters Parameters ------------- ----------- ------------ No default: okay YAGNI [1] Default: pointless okay ------------- ----------- ------------
[1] And if you do need it, it is easy to emulate with a sentinel:
def func(arg=None): if arg is None: del arg process(arg) # May raise UnboundLocalError
None of this is relevant to the question of when the default values should be evaluated.
Agreed on all points, but I think the YAGNIness of it is less strong than you imply. Rather than a sentinel object, this would be a sentinel lack-of-object. The point of avoiding sentinels is, like everywhere else, that the sentinel isn't really a value - it's just a marker saying "this arg wasn't passed". So I agree with you that this isn't a feature of huge value, and it's not one that I'm pushing for, but it is at least internally consistent and well-defined. Usually, what ends up happening is that there's code somewhere saying "if arg is _sentinel:" (or "if arg is None:"), which would have exactly the same meaning without the sentinel, if we had a way to say "if arg isn't set". ChrisA