
On Thu, Nov 4, 2021 at 4:30 AM Steven D'Aprano <steve@pearwood.info> wrote:
On Thu, Nov 04, 2021 at 03:07:22AM +1100, Chris Angelico wrote:
def func(spam: list = []) -> str: ...
Which part of that becomes late bound?
The name "spam" of course. Without the parameter, nothing gets bound at all.
True, but the parameter will be bound at the same time (minor implementation details aside) whether there's an early default, a late default, or no default, and (orthogonally) whether a value was provided by the caller. There's nothing early or late there.
The [], not the name "list", not the name "spam".
The type annotation is irrelevant. Its an annotation. There's no sense that the earliness/lateness of the binding comes into it at all. The parameter spam is declared to be a list even if the function is never called and nothing gets bound to it at all.
Correct, the annotation is irrelevant - but it is between the name and the default. That's why I'm using annotated examples here: to emphasize that separation (which will happen in many codebases), and to highlight the distinction between annotating the name and annotating the expression.
And the [] is merely the expression that is bound to the parameter.
I say "merely", but of course the expression is important in the sense that if you want the default value to be an empty list, it won't do to use "Hello world" as the expression. Obviously.
Uhh, but that's exactly the bit that's evaluated at a different time. So it's not "merely" the expression; it is the exact thing that we're changing the behaviour of.
But the parameter is, in a sense, more important than the default value, because the default value is only a default. If you pass an argument to the function, the default doesn't get used. So if you talk about the function (either in code, the function's body, or in actual prose), you will say something like:
if condition: spam.append(eggs)
"if this condition is true, append eggs to spam" rather than "...to the empty list" because it might not be the empty list. We talk about the parameter. The parameter is late bound. Not the empty list.
Of course! The parameter is FAR more important than the default, and that's why it comes first. But it's not the parameter that changes.
Late binding or early binding is a property of the identifier, not of the value that gets bound to it.
I disagree :) The identifier is bound in exactly the same way.
I'm sure that we can make it unambiguous to the compiler, but we may not make it stand out sufficiently to the human reader. It should be front and centre, like the `*` in the `*args` and `**kwargs` syntax.
But *a and **kw are actually different parameter types. They change the meaning of the parameter itself, and therefore annotate that.
I personally feel that the @ symbol works as a prefix modifier on the parameter. But if you hate the @ symbol, I think that other prefix modifiers may also work:
$parameter=expression # perhaps a little too like Sparameter !parameter=expression # "bang" parameter :-) ^parameter=expression >parameter=expression # unary greater than :-) ~parameter=expression
The at sign doesn't particularly bother me, the prefix does. So I'm not really a fan of any of these alternatives. ChrisA