Worse, no one uses it. I've yet to come across anyone that advocates it or remembers it.
Worse, no one uses it. I've yet to come across anyone that advocates it or remembers it.
values = [
value
for line in buffer.readlines()
if (value := line.strip())
]
Previously, I would have needed to either duplicate effort like: values = [
line.strip()
for line in buffer.readlines()
if line.strip()
]
Or used a sub-generator: values = [
value
for value in (
line.strip() for line buffer.readlines()
)
if value
]
Or rewritten it altogether using a (slower) for loop calling append each time: values = []
for line in buffer.readlines():
line = line.strip()
if line:
values.append(line)
The assignment expression is perfect for this sort of use case, and is a clear win over the alternatives IMO.Edit: fixed initial example
values = [
value
for line in buffer.readlines()
for value in (line.strip(),)
if value
] nonblank = [value for line in file for value in [line.strip()] if value] open("file", "r").readlines.
map{|line| line.strip}.
filter{|line| line != ""}
or some smarter but less readable ways.I prefer the left-to-right transformations style to Python's list comprehension and inside-to-outside function composition. The reason is that it reminds me of how data flow into *nix pipelines. I spent decades working with them and I've been working with Ruby for the last half of that time. With Python in the last quarter of my career.
It's a matter of choices and preferences of the original designed of the language. Both ways work.
If function is None, the identity function is assumed, that is, all elements of iterable that are false are removed.
So it just removes false-y values.
Very handy I've used it a ton
filter(None, xs)
is equivalent to: filter(lambda x: x, xs)
That is, it will return an iterator over the truthy elements of the passed iterable.Certainly, I agree; I would usually use:
(x for x in xs if x)
Or, if I know more about the kind of falsy values xs actually needs removed, something more explicit like: (x for x in xs if x is not None)
Because Python’s multiplicity of falsy values can also be something of a footgun (particularly, when dealing with something a collection of Optionals where the substantive type has a falsy value like 0 or [] included.)Instead of:
filter(None, xs)
Which is terse but potentially opaque.Though it's additional syntax, I kind of wish genexp/list/set comprehensions could use something like “x from” as shorthand for “x for x in”, which would be particularly nice for filtering comprehensions.
stripped_lines = (line.strip() for line in buffer.readlines())
non_empty_stripped_lines = [line for line in stripped_lines if stripped_lines]Breaking down things in clear steps is underrated I think.
>>> if m := re.search(r'(.*)s', 'oh!'):
... print(m[1])
...
>>> if m := re.search(r'(.*)s', 'awesome'):
... print(m[1])
...
aweFeature creep is programming language's worse enemy after a certain maturity level.
I absolutely love Go in this matter. They took forever to add Generics and generally sides with stability over features.
start = 0
while (end := my_str.find("x", start)) != -1:
print(my_str[start:end])
start = end + 1
vs start = 0
while True:
end = my_str.find("x", start)
if end == -1:
break
print(my_str[start:end])
start = end + 1
I'm still on the fence myself so I sympathise with your view, but the first version is certainly a bit tidier in this case.Many of my code were like this:
foo = one_or_none()
if foo:
do_stuff(foo)
Now I have the following: if foo := one_or_none():
do_stuff(foo)
This kind of code happens quite frequently, looks nicer with walrus operator to me. if one_or_none() as foo:
do_stuff(foo)I am not fully up to speed with 3.10, but quickly checked the docs and it doesn't appear to have been added in 3.10 either.
Let me know if I'm missing something.
for foo in one_or_none():
do_stuff(foo)
If you don't control one_or_none, but it returns an Optional, you can wrap it with something like: def optional_to_tuple(opt_val: Optional[T]) -> Tuple[]|Tuple[T]:
return (opt_val,) if opt_val is not None else ()In both cases, "foo" continues to exist after the "if" even though the second example makes it look like "foo" is scoped to the "if".
So to my eye, the following would look super weird (assume do_more_stuff can take None):
if foo := one_or_none():
do_stuff(foo)
do_more_stuff(foo)
whereas the following would look fine: foo = one_or_none()
if foo:
do_stuff(foo)
do_more_stuff(foo) let some_func () = 5
let a = some_func () in if a < 5 then "<5" else ">=5"