while True:
bytes = io.get(x)
if not bytes:
break
...
which I think is objectively less readable.In the second example, you have an extra call to foo for every element of stuff. If foo(z) is expensive, you'd probably want to write this as
[bar(x) for x in map(foo, stuff) if x]
instead - which I personally don't mind, but it's arguably not as clear as having the in-line assignment expression. bytes = next()
while bytes:
# do something ...
bytes = next()
It's possible to use while True and break, but that isn't very elegant. [bar(x) for z in stuff if (x := foo(z))]
is not equivalent to: [bar(foo(z)) for z in stuff if foo(z)]
because here, foo(z) will be called twice. If foo is an expensive operation or performs some other magic in the background that you don't want to call a second time, := lets you express this behavior without having to break your expression out into a huge for-if-append code block: output = []
for z in stuff:
x = foo(z)
if x:
output.append(bar(x))
Of course, the above verbose language might end up being a better code in the end, because mashing together a bunch of terse code is often not very readable. But in real life, transforming a comprehension into a loop is just a bunch of work that gets in the way of me trying to complete a task.When I'm developing out an idea and just want to see if the code works and passes my tests, I would rather have the option to insert behavior swiftly without having to reformat an entire block of code. Then when reviewing my code or deciding whether to make it reusable for something else, I can go back and clean things up.
[bar(x) for x in map(foo, stuff) if x]
I was just trying to be as close to the original as possible.I do find it odd you call out the calling of `foo` twice as a performance drain but then use the chief example of:
> When I'm developing out an idea and just want to see if the code works and passes my tests, I would rather have the option to insert behavior swiftly without having to reformat an entire block of code.
Tests are not something I (personally) consider to be performance sensitive. But like I said I am sort of neutral on this change. I don't really see a massive benefit to it, so I would personally air on the side of "don't add" but I'm not mad it's being added. It's a "meh" for me.
[bar(foo(z)) for z in stuff if foo(z)]
as [bar(y) for y in (z for z in stuff if foo(z))]
or even as [bar(y) for y in filter(foo, stuff)]
... although, I get that `map`, `apply`, and `filter` aren't generally considered pythonic.Overall, I think I agree with you - the new syntax in PEP572 might be handy, but it isn't necessary and I would say that the cognitive overhead of encountering yet another syntax doesn't justify the benefit, much less the technical overhead for the interpreter.
[bar(foo(y)) for y in (z for z in stuff if foo(z))]
(etc.) though, since `bar` takes as input the output of `foo`. This leads to the objectionable duplicate calls to `foo`, hence the new assignment expressions.I like Dunnorandom's
[bar(x) for x in map(foo, stuff) if x]
best for a correct result using existing syntax, or [bar(x) for y in (foo(x) for x in stuff) if y]
if you don't like `map`.