def foo():
if n := randint(0, 3):
return n ** 2
return 1337
[(x, y, x/y) for x in input_data if (y := f(x)) > 0]
E.g.
if(x:=f() is not None):
print(x)
You can read more about it here: https://www.python.org/dev/peps/pep-0572/ match1 = re1.match(text)
if match1 is not None:
do_stuff()
else:
match2 = re2.match(text)
if match2 is not None:
do_other_stuff()
Which is a bit clunky. you only want to evaluate match2 in case match1 fails, but that means a new level of nesting. Instead, with this proposal, you could do this: if (match1 := re1.match(text)) is not None:
do_stuff();
elif (match2 := re2.match(text)) is not None:
do_other_stuff()
Evaluate and assign in the if-statement itself. This is not dissimilar to the equals operator in C. In C, you would frequently find loops like
`while ((c = read()) != EOF) { ... }`. This would presumably allow a similar pattern in python as well.More information can be found in PEP-572: https://www.python.org/dev/peps/pep-0572/
Short version.
(x =: y) is an expression that:
1. assigns the value y to the variable x
2. has the value y.
So `print((x := 1) + 1)` prints '2', and sets x=1.
A ton of languages [eg: c, js] have '=' work this way. And a ton of style guides for those languages tell you to avoid using it like that, because it's confusing. So this is a bit controversial.
This PEP solves very little problem, saves a few characters of code, but adds complexity to readability.
The use of "=" for assignment has long been a pet peeve of mine. It was a mistake when C did it, and it's been a mistake for so many subsequent languages to copy it.
"=" shouldn't be an operator at all, it makes a lot more sense to use ":=" and "==".
Pascal's use of ":=" for assignment and "=" for equality, strikes me as almost as clear.
Still, at least C makes consistent use of '=' for assignment, unlike that god-forsaken trainwreck of a language, VB.Net, which uses it for both assignment and for equality depending on context.
if foo := bar[baz]:
bar[baz] += 1
return foo
else:
bar[baz] = 1
return 0
Where foo is a dict keeping track of multiple things, and a non-existing key (baz) is never an error but rather the start of a new count. Faster and more readable than if baz in list(bar.keys()):
....
Similar to Swift’s ‘if let’, it seems.[ {X, Y, X/Y} || X <- Some_Function (), Y <- Some_Other_Function () ]
And people bitch about Erlang syntax.
Edit: "/" is the division operator
try:
bar[baz] += 1
except KeyError:
bar[baz] = 1
Also you can check if a key is in a dict simply by doing "if baz in bar" no need for "list(bar.keys())", which will be slow (temp object + linear scan) vs O(1) hashmap lookup.Code starts becoming a lot harder to reason about when more than one state is mutated on the same line. The good design of Python makes this harder than in say C and I think this is a step in the wrong direction in that regard.
The two real things this solves are checking for truthyness in an if and reusing values in a filterting comprehension. Instead of the syntax we have now that can be used anywhere, adds a whole new concept and feels kind of out-of-place, I would have much preferred a solution that can only be used in vetted places, doesn't add a new thing people need to learn and follows the style of the language
For example, my preferred solution for `if` would have been:
if thing() as t:
print(t)
Usage of `as` is already established by the `with` block [value for x in y
if value
where value = x * 2]
The order is unfortunately a bit weird here, but there is no need to add the whole concept of a different type of assignment and this syntax will feel instantly recognizable to people familiar mathematical notation, which is where the existing list comprehension syntax comes from and so has been established as well. while continue = "yes":
instead of: while continue == "yes":
Those mistakes introduce bugs that are hard to spot because they don't cause an immediate error, linters can hardly help with them and even a senior can make them while being tired. m = re.match(r'\s*(\d+)\s*', args)
if m:
start = int(m.group(0))
end = start + 10
m = re.match(r'\s*(\d+)\s*,\s*(\d+)\s*', args)
if m:
start, end = map(int, m.groups())
With the new syntax this becomes: if m := re.match(r'\s*(\d+)\s*', args):
start = int(m.group(0))
end = start + 10
if m := re.match(r'\s*(\d+)\s*,\s*(\d+)\s*', args)
start, end = map(int, m.groups())
This pattern occurs just often enough to be a nuisance. For another example drawn from the standard library, here's modified code from "platform.py" # Parse the first line
if (m := _lsb_release_version.match(firstline)) is not None:
# LSB format: "distro release x.x (codename)"
return tuple(m.groups())
# Pre-LSB format: "distro x.x (codename)"
if (m := _release_version.match(firstline)) is not None:
return tuple(m.groups())
# Unknown format... take the first two words
if l := firstline.strip().split():
version = l[0]
if len(l) > 1:
id = l[1]
I like C way of assignment being an expression. I think having separate statement and then assignment expresdion is a mess. It's still useful though as Python was missing where keyword like feature from Haskell which is necessary to avoid duplicating computation in list comprehension.