The walrus operator :=
informally named after its resemblance to the eyes and tusks of a walrus was part of the the new features Python rolled out in Python 3.8 release on October 14, 2019.
The walrus operator makes it possible to assign variables within an expression. You might not get what that means but keep on reading.
What is the Walrus Operator?
The walrus operator is formally known as Assignment Expression or Named Expressions. It enables you to name the result of an expression, removing the need to rewrite that expression when you want a result.
The walrus operator uses the notation
NAME := expr
where expr
is any valid Python expression other than an unparenthesized tuple (you can't make multiple assignments using one walrus operator in a single line of code) , and NAME
is an identifier.
Code explanation
Let's look at the walrus operator in action using a python shell
>>> 1 + 1
>>> 2
>>> expr = 1 + 1
>>> expr
2
>>> (expr := 1 + 1)
2
>>> expr
2
In the first statement, we write the expression 1 + 1
and the shell evaluates our expression to give us 2
. But in the second line we assign the expression to a variable expr
and the shell does nothing. That's because the value of the expression is stored in the variable and the only way we can get it is if we call the variable. So what if we wanted to call the expression and assign a value to it so that we would have to write 1 + 1
every time we wanted two? That is where the walrus operator comes in. You see in the last block of code we assign the expression 1 + 1
to the variable expr
using the walrus operator and it immediately gets evaluated to 2
.
Now imagine if our expression was something way more complex than 1 + 1
like the formula for roots of a quadratic equation which is {-b ± i√-(b2 – 4ac)}/2a
. It's going to be hectic writing that expression over and over again. So you I hope you now get the importance of the walrus operator.
Practical examples
These are examples I got from the Python official documentation to further buttress my point on the benefits of this operator.
In this example, the assignment expression helps avoid calling len()
twice:
if (n := len(a)) > 10:
print(f"List is too long ({n} elements, expected <= 10)")
A similar benefit arises during regular expression matching where match objects are needed twice, once to test whether a match occurred and another to extract a subgroup:
discount = 0.0
if (mo := re.search(r'(\d+)% discount', advertisement)):
discount = float(mo.group(1)) / 100.0
The operator is also useful with while-loops that compute a value to test loop termination and then need that same value again in the body of the loop:
# Loop over fixed length blocks
while (block := f.read(256)) != '':
process(block)
Another motivating use case arises in list comprehensions where a value computed in a filtering condition is also needed in the expression body:
[clean_name.title() for name in names
if (clean_name := normalize('NFC', name)) in allowed_names]
Do's and don'ts of the walrus operator
According to Python's official documentation, here are a few places where assignment expressions are not allowed, in order to avoid ambiguities or user confusion:
Unparenthesized assignment expressions are prohibited at the top level of an expression statement. Example:
y := f(x) # INVALID (y := f(x)) # Valid, though not recommended
This rule helps users choose between an assignment statement and an assignment expression by ensuring there is no place where both are allowed.
Unparenthesized assignment expressions are prohibited at the top level of the right hand side of an assignment statement. Example:
y0 = y1 := f(x) # INVALID y0 = (y1 := f(x)) # Valid, though discouraged
Again, this rule is included to avoid two visually similar ways of saying the same thing.
Unparenthesized assignment expressions are prohibited for the value of a keyword argument in a call. Example:
foo(x = y := f(x)) # INVALID foo(x=(y := f(x))) # Valid, though probably confusing
This rule is included to disallow excessively confusing code, and because parsing keyword arguments is complex enough already.
Unparenthesized assignment expressions are prohibited at the top level of a function default value. Example:
def foo(answer = p := 42): # INVALID ... def foo(answer=(p := 42)): # Valid, though not great style ...
This rule is included to discourage side effects in places where the meaning is already confusing for many users (similar to the recommendation against mutable default values). It also aligns with the similar rule for calls mentioned earlier.
Unparenthesized assignment expressions are prohibited as annotations for arguments, return values and assignments. Example:
def foo(answer: p := 42 = 5): # INVALID ... def foo(answer: (p := 42) = 5): # Valid, but probably never useful ...
The reasoning here is similar to the two previous cases; this ungrouped assortment of symbols and operators composed of
:
and=
is hard to read correctly.Unparenthesized assignment expressions are prohibited in lambda functions. Example:
(lambda: x := 1) # INVALID lambda: (x := 1) # Valid, but unlikely to be useful (x := lambda: 1) # Valid lambda line: (m := re.match(pattern, line)) and m.group(1) # Valid
This makes sure that
lambda
always binds less tightly than:=
. Having a name binding at the top level inside a lambda function is not very useful because you can't really use it. If the name is used more than once, you'll probably need to use parentheses anyway, so this rule won't often impact your code.Assignment expressions inside of f-strings require parentheses. Example:
>>> f'{(x:=10)}' # Valid, uses assignment expression '10' >>> x = 10 >>> f'{x:=10}' # Valid, passes '=10' to formatter ' 10'
This shows that what looks like an assignment operator in an f-string is not always an assignment operator. The f-string parser uses
:
for formatting options. To keep backwards compatibility, you must use parentheses around assignment operators inside f-strings. As mentioned above, using the assignment operator this way is not recommended.
Conclusion
The walrus operator is a powerful addition to Python that enhances code efficiency and readability by allowing variable assignment within expressions. By reducing redundancy and simplifying complex expressions, it streamlines coding tasks and improves performance. However, it's essential to use it judiciously and be aware of the contexts where its use is restricted to avoid confusion and maintain code clarity. Embracing the walrus operator can lead to more concise and maintainable code, making it a valuable tool for Python developers.
Additional Resources
Be sure to check these links to official Python documentation, I also added a link to an article by Real Python which I find helpful