Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

In your example, if you leave out the parentheses around w := bar(), you get "SyntaxError: cannot use assignment expressions with operator" which makes me think it's a bug in the interpreter and not intentionally designed to allow it.

I am baffled to learn that it's kept in scope outside of the statement it's assigned, and I agree it would have a negative impact on readability if used outside of the if statement.



> if you leave out the parentheses around w := bar(), you get "SyntaxError: cannot use assignment expressions with operator" which makes me think it's a bug in the interpreter and not intentionally designed to allow it.

No, I'm pretty sure that's intentional. You want the left-hand side of an assignment to be crystal clear, which "foo() or w := bar()" is not. It looks like it's assigning to (foo() or w).


To be clear:

    def thing(): return True
    
    if thing() or w:= "ok": # SyntaxError: cannot use assignment expressions with operator
        pass
    print(w)

    . . .

    if thing() or (w := "ok"):
        pass
    print(w) # NameError: name 'w' is not defined

The first error makes me think your concern (that w is conditionally undefined) was anticipated and supposed to be guarded against with the SyntaxError. I believe the fact you can bypass it with parentheses is a bug and not an intentional design decision.


Oh I see, you're looking at it from that angle. But no, it's intentional. Check out PEP 572 [1]:

> The motivation for this special case is twofold. First, it allows us to conveniently capture a "witness" for an any() expression, or a counterexample for all(), for example:

  if any((comment := line).startswith('#') for line in lines):
    print("First comment:", comment)
  else:
    print("There are no comments")
I have a hard time believing even the authors (let alone you) could tell me with a straight face that that's easy to read. If they really believe that, I... have questions about their experiences.

The beauty of Python...

[1] https://www.python.org/dev/peps/pep-0572/


Your new example makes me wonder: if I can intentionally conditionally bring variables into existence with the walrus operator, what's the motivation behind the SyntaxError in my statement above? I maintain my belief that the real issue here is, readability aside, if blocks do not implement a new scope, which has always been a problem in the language. The walrus operator just gives you new ways to trip over that problem.

From the PEP:

> An assignment expression does not introduce a new scope. In most cases the scope in which the target will be bound is self-explanatory: it is the current scope. If this scope contains a nonlocal or global declaration for the target, the assignment expression honors that. A lambda (being an explicit, if anonymous, function definition) counts as a scope for this purpose.

I find this particularly strange and inconsistent:

    lines = ["1"]

    [(comment := line).startswith('#') for line in lines]

    print(comment) # 1

    [x for x in range(3)]
    print(x) # NameError: name 'x' is not defined


> what's the motivation behind the SyntaxError in my statement above?

I'm pretty sure it's what I explained here: https://news.ycombinator.com/item?id=28899404


I did not understand what you meant.


I'm saying it's the same reason why (x + y = z) should be illegal even if (x + (y = z)) is legal in any language. It's not specific to Python by any means. The target of an assignment needs to be obvious and not confusing. You don't want x + y to look like it's being assigned to.


I see. It has low precedence in the operator hierarchy [1] so

    False or w := 1:
Is grouped like so:

    (False or w) := 1
Which is a SyntaxError. That's... not a smart place for it to be in the operator hierarchy. I expected it to be near the very top, like await.

1. https://docs.python.org/3/reference/expressions.html#operato...

Edit: 20 minutes later, can't respond.

There are two aspects I have been thinking about while looking at this: Introduction of non-obvious behavior (foot-guns) and readability. Readability is important, but I have been thinking primarily about the foot-gun bits, and you have been emphasizing the readability bits. I can't really accurately assess readability of something until I encounter it in the wild.


If the precedence was higher then you'd get a situation like

  x := 1 if cond else 2
never resulting in x := 2 which is pretty unintuitive.

And you have to realize, even if the precedence works out, nobody is going to remember the full ordering for every language they use. People mostly remember a partial order that they're comfortable with, and the rest they either avoid or look up as needed. Like in C++, I couldn't tell you exactly how (a << b = x ? c : d) groups (though I could make an educated guess), and I don't have any interest in remembering it either.

Ultimately, this isn't about the actual precedence. Even if the precedence was magically "right", it's about readability. It's just not readable to assign to a compound expression, even if the language has perfect precedence.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: