22 comments

[ 4.1 ms ] story [ 61.0 ms ] thread
I'm amazed at how long I've been working in python and never known that assignment works in this way. I suppose I just avoid parts of the language that I don't understand.

Looking through the archives, it's a really interesting blog in general

I mean, it generally looks like a terrible idea, so absent of malice to the reader (or code golf shenanigans) nobody would do this. Not a surprise that the behavior is surprising...
It's fallout of the fact that assignment must be a statement, and cannot be an expression.

Eg. This is wrong in python, while it would be allowed in c-like languages

    print(a=5)
It’s possible now with the walrus operator though:

    print(a:=5)
Some languages wear the things that make you go WTF on their sleeves. In contrast, Python's surprises pop up slowly over time, and in places where you'd least expect them.
I wonder if it’s because of their subtlety. I assume you are referencing JavaScript, where the warts are impossible to ignore because you can’t write an everyday program as a beginner without running into them. This sort of thing from the article is just not something I would typically be trying to do in the normal course of shuffling data around.
Usually this op chain feature is used to allow `if a == b == c:`. This is resolved as (a==b) and (a==c). Following this logic the assignment statement is extended the same way as far as I understand. It's just more surprising.
The two behaviors don't seem very related.

In `if a == b == c`, the value of `b` is evaluated only once, even if `b` is in fact a bigger/side-effecting expression, and `c` might not be evaluated at all.

In `a = b = c`, the `c` is evaluated first, and then `a` and `b` are assigned to, left-to-right.

`==` doesn't have side effects though, so you don't have to worry about the order of uses of a variable that appears twice; it will have the same value both Tony's l times. It seems like a bad idea to me to have operators with side effects use the same rules as ones that don't just because they happen to have similar syntactic representations, but I've never written a non-toy programming language, so I guess there could be something I'm missing.
I don't think so. The assignment behavior here is explicit, not some emergent fact of operator chaining in general. I think it's meant to allow you to say

  a = b = expr
and have both a and b end up with the value of expr. And by defining it in the fashion that it does, it only requires the target list expressions to be evaluated once as an lvalue (or whatever python's equivalent is).
Thinking about it some more, the surprising behavior here is pretty much just that it does the assignments in left-to-right order instead of right-to-left order. If you try and break down `a = b = expr` into a series of binary assignment operators, the natural answer is `a = (b = expr)` (b doesn't exist yet so you can't write `(a = b) = expr`) where `(b = expr)` behaves as though it returns the value again. And I believe this is in fact how it works in Ruby (as Ruby assignments return the value). But Python assignments don't actually return the value, as they are statements and not expressions.

Presumably Python does the assignments in left-to-right order because any observable side-effects in the evaluations of a and b would be expected to be in left-to-right order. It just results in counterintuitive behavior like this.

(comment deleted)
Something like this should not be possible. Instead of adding new "features" left and right, Python must deal with problems like these.

Fix the bugs, improve performance, sort out packaging. Leave the language as it is, improve the implementation. It is already possible to write any kind of software using existing features.

Let's avoid making this language harder than it has to be. It was a truly simple language, now it is turning into Frankenstein's monster and you have to google for new added syntax every time you need to use it.

At this point, fixing the issue would break all kinds of software that's depending on it working that way.
This. It's known behavior that you can leverage.
There are efforts to improve these things.

I converted some projects to pyproject.toml (the new standard for defining a project) and between it and the Poetry build/packaging tool it's pretty much solved the packaging nightmare.

There is now a branch of CPython that removes the GIL with significantly better performance in some benchmarks. There is active discussion on the mailing lists about how to get it into mainline.

How heavy is Poetry tool? What's it's selling point when compared with setuptools?

With setuptools, I just make setup.py and run pip install inside the root directory. If I have wheel, pip builds a wheel for my package.

Sorry, but I don't want to learn yet another packaging meta-language when plain Python worked fine for decades. Python already tried with setup.cfg ini files and people rejected it. Why would pyproject.toml be any better?

Why not improve setuptools instead? That is what sorting out packaging means. Stop copying other languages and focus on Python. Core devs took typing syntax, pattern matching and packaging from Rust and bolted it all on Python like it fits.

Well, it does not fit. Stick to your own.

Poetry still uses setuptools! It makes it a lot easier for mortals. This is the blog post that convinced me to switch away from my old build system: https://blog.ganssle.io/articles/2021/10/setup-py-deprecated...
The blog post recommends build, not Poetry. But I guess the Python devs finally started taking this seriously.

Since build is semi-official Python project, I will use it instead of Poetry. We can no longer rely on setup.py, it seems.

Thanks.

I just tried Poetry on Windows.

Installation is a mess, and official recommendation of "copying a powershell line" was already a red flag. Now I can't remove it.

Poetry is a convoluted, insecure mess. Please avoid it and use official PyPi recommendations.

setuptools design is a mess. pip will likely support it forever though so you are fine.
Python has a long history of adopting good ideas from other languages.

None of the things you mentioned were invented by Rust.