
On Wed, Oct 27, 2021 at 10:38 AM Rob Cliffe <rob.cliffe@btinternet.com> wrote:
On 26/10/2021 18:25, Chris Angelico wrote:
On Tue, Oct 26, 2021 at 11:10 PM Steven D'Aprano <steve@pearwood.info> wrote:
Based on the multi-pass assignment model, which you still favour, those WOULD be quite inconsistent, and some of them would make little sense. It would also mean that there is a distinct semantic difference between:
def f1(x=>y + 1, y=2): ... def f2(x=>y + 1, y=>2): ... Sure. They behave differently because they are different.
These are different too:
# Block 1 y = 2 x = y + 1
# Block 2 x = y + 1 y = 2 Yes, those ARE different. Those are more equivalent to changing the order of the parameters in the function signature, and I think we all agree that that DOES make a difference. The question is whether these could change meaning if you used a different type of assignment, such as:
y := 2 x = y + 1
Does that suddenly make it legal? I think you'll find that this sort of thing is rather surprising. And that's what we have here: changing from one form of argument default to another changes whether left-to-right applies or not.
I don't want that. And based on an experiment with a less-experienced Python programmer (admittedly only a single data point), neither do other people. Left-to-right makes sense; multi-pass does not. As I may be the data point in question:
You're not - I sat down with one of my brothers and led him towards the problem in question, watching his attempts to solve it. Then showed him what we were discussing, and asked his interpretations of it.
One of my posts seems to have got lost again, so I reproduce some of it (reworked): What I DON'T want to see is allowing something like this being legal: def f(a := b+1, b := e+1, c := a+1, d := 42, e := d+1): If no arguments are passed, the interpreter has to work out to evaluate first d, then e, then b, then a, then finally c. If some arguments are passed, I guess the same order would work. But it feels ... messy. And obfuscated.
At no point will the interpreter reorder things. There have only ever been two (or three) options seriously considered: 1) Assign all parameters from left to right, giving them either a passed-in value or a default 2) First assign all parameters that were passed in values, and those with early-bound defaults; then, in a separate left-to-right pass, assign all late-bound defaults 3) Same as one of the other two, but validated at compilation time and raising SyntaxError for out-of-order references
And if this is legal (note: it IS a legitimate use case): def DrawCircle(centre=(0,0), radius := circumference / TWO_PI, circumference := radius * TWO_PI): the interpreter has to work out whether to evaluate the 2nd or 3rd arg first, depending on which is passed. AFAICS all this may need multiple passes though the args at runtime. Complicated, and inefficient. *If* it could all be sorted out at compile time, my objection would become weaker.
This is not as legit as you might think, since it runs into other problems. Having codependent arguments is not going to be solved by this proposal (for instance, what happens if you pass a radius of 3 and a circumference of 12?), so I'm not going to try to support narrow subsets of this that just happen to "work" (like the case where you only ever pass one of them).
There has been support for evaluating all early-bound defaults before all late-bound defaults. I have been persuaded that this is a reasonable option. AFAICS there would be little practical difference from straight left-to-right evaluation of defaults, since assigning an early-bound default should not have a side effect. So it could even be an implementation choice.
If it's an implementation choice, then it'll mean that code is legal on some interpreters and not others. Whether that's a major problem or not I don't know, but generally, when you can't depend on your code working on all interpreters, it's not best practice (eg "open(fn).read()" isn't recommended, even if it happens to close the file promptly in CPython). ChrisA