Kurs
Python 3.8 introduced a new operator to the language. Its official name is the assignment expression, and it uses the :=
operator.
However, it's more commonly called the walrus operator as it looks like the tasks of a walrus!
There was controversy in the normally calm Python world when this operator was added to the language, and its use is still relatively limited. Still, the assignment expression is now part of Python, and it can be useful in some situations.
This tutorial will explore how Python's walrus operator works and will discuss situations where it can be used.
Become a Data Engineer
What Is Python's Walrus Operator
The assignment expression, which uses the :=
operator, looks similar to the more common assignment statement, which uses the =
operator. However, there's an important distinction between the two.
A statement is a unit of code that performs some action. An expression is a statement that can be evaluated to a value. Therefore, an expression returns a value, whereas statements that aren't expressions don't return any value.
Python's REPL (Read, Evaluate, Print, and Loop) displays the value of an expression as soon as it's evaluated. However, no value is shown when executing a statement that's not an expression:
>>> import random # Not an expression
>>> 10 * 2 # An expression
20
The import
statement doesn't evaluate to any value. However, 10 * 2
is an expression that evaluates to a value.
The assignment statement, which uses the =
operator, assigns an object to a variable or another reference, but it doesn't return the value. The assignment expression, or walrus operator, performs the same action as the assignment statement but also returns the object:
>>> value = 10 # Assignment statement
>>> value
10
>>> (value := 20) # Assignment expression
20
Note that the assignment statement doesn't return the value, unlike the assignment expression. Therefore, an assignment expression combines several operations:
- It evaluates the expression on the right-hand side of the
:=
operator. - It assigns this value to a variable name.
- It returns the value.
The parentheses are required syntax when using the assignment expression instead of an assignment statement, as in this example, to reduce ambiguity. There's more about the syntax requirements for the walrus operator later in this tutorial.
An assignment expression can be used anywhere expressions can be used in Python. Let's look at an example:
print(f"The number is {(value := 50)}")
print(value)
The number is 50
50
The variable name value
is defined within the f-string in the first line. However, the integer is also returned directly to the curly braces in the f-string. The curly braces in f-strings must include an expression, which explains why an assignment statement can't be used.
Use Cases for Python’s Walrus Operator
Two common use cases for the walrus operator are simplifying and optimizing code.
Simplifying code
A common use case for the walrus operator is to simplify code where a variable would normally need to be defined before being used, such as in some conditional statements. Consider the following while
loop:
import random
value = random.randint(1, 20)
while value < 18:
print(value)
value = random.randint(1, 20)
7
5
1
11
12
1
9
This code runs the while
block as long as the random value generated is smaller than 18
. The variable value
needs to be defined before the while
loop so it can be used in the while
statement. A new random number is assigned to value
at the end of the while
block.
The assignment expression :=
can be used to reduce the lines of code and to call the random.randint()
function in a single place in the program:
import random
while (value := random.randint(1, 20)) < 18:
print(value)
1
9
2
4
9
15
11
A new random number is generated and assigned to value
each time the while
loop starts a new iteration.
Optimizing code
Let's look at another example using list comprehensions. Consider the following code, which filters a list of strings containing dates and creates a list of datetime.datetime
objects containing only dates within a given year:
from datetime import datetime
def format_date(date_str):
return datetime.strptime(date_str, "%Y-%m-%d")
dates = ["2024-01-01", "2022-12-31", "2024-06-15", "2023-08-23", "2024-11-30"]
formatted_dates = [
format_date(date)
for date in dates
if format_date(date).year == 2024
]
print(formatted_dates)
[datetime.datetime(2024, 1, 1, 0, 0), datetime.datetime(2024, 6, 15, 0, 0), datetime.datetime(2024, 11, 30, 0, 0)]
The function format_date()
accepts a string with a date in the yyyy-mm-dd format and returns a datetime.datetime
object. One of the datetime.datetime
object attributes is .year
, which contains the year component of the date.
Only the three 2024 dates are included in the final list, which also contains datetime.datetime
objects instead of strings. The list comprehension includes an if
clause, which calls format_date()
. However, format_date()
is also called in the first expression in the list comprehension, which generates the values to add to the list. Calling the function with the same argument twice is inefficient, especially if the function is a bottleneck in the program's performance.
One option to avoid calling the same function twice is to call the function and assign it to a variable before using it. However, this can't be achieved with an assignment statement while still using a list comprehension. This solution requires a standard for
loop:
from datetime import datetime
def format_date(date_str):
return datetime.strptime(date_str, "%Y-%m-%d")
dates = ["2024-01-01", "2022-12-31", "2024-06-15", "2023-08-23", "2024-11-30"]
formatted_dates = []
for date in dates:
formatted_date = format_date(date)
if formatted_date.year == 2024:
formatted_dates.append(formatted_date)
print(formatted_dates)
[datetime.datetime(2024, 1, 1, 0, 0), datetime.datetime(2024, 6, 15, 0, 0), datetime.datetime(2024, 11, 30, 0, 0)]
The walrus operator provides an alternative that's compatible with a list comprehension since the formatted date returned by the function can be assigned to a variable name and returned in the same expression:
from datetime import datetime
def format_date(date_str):
return datetime.strptime(date_str, "%Y-%m-%d")
dates = ["2024-01-01", "2022-12-31", "2024-06-15", "2023-08-23", "2024-11-30"]
formatted_dates = [
formatted_date
for date in dates
if (formatted_date := format_date(date)).year == 2024
]
print(formatted_dates)
[datetime.datetime(2024, 1, 1, 0, 0), datetime.datetime(2024, 6, 15, 0, 0), datetime.datetime(2024, 11, 30, 0, 0)]
The assignment expression in the list comprehension is the expression enclosed in parentheses, (formatted_date := format_date(date))
. This expression calls the function format_date()
, assigns its value to the variable formatted_date
, and returns the value. Since the assignment expression evaluates to a datetime.datetime
object, the code accesses the .year
attribute, which is compared to 2024
using the equality operator.
The if
clause in the list comprehension is executed before the expression that generates the value to store in the list. Therefore, the walrus operator is used in the if
clause, and the variable defined in this assignment expression is used as the first expression in the list comprehension.
Python’s Walrus Operator Controversy
The assignment expression is not loved by all Python programmers. Some see it as a way of simplifying code, but others feel it makes code less readable and prefer the longer options. Readability can be subjective, so opinions vary on whether the walrus operator makes code more or less readable.
The last example in the previous section shows the trade-offs needed. Without the walrus operator, the code in this example needs an empty list to be initialized first, and then values are appended to it in a for
loop. This solution is longer and less efficient than using list comprehensions, which are optimized for this job.
However, the standard for
loop solution may be more readable for some programmers who prefer to write more code rather than use the walrus operator, especially if performance is not a concern.
Many coding style guides still recommend avoiding the assignment expression or only using it sparingly.
Python’s Walrus Operator: Syntax Rules
It's common to encounter syntax errors when initially exploring the walrus operator owing to its syntax rules.
To avoid confusion between the assignment statement =
and assignment expression :=
, there's no situation in which both options are syntactically valid. For this reason, it's often necessary to enclose the assignment expression in parentheses:
(value := 20) # Valid
value := 20 # Invalid (SyntaxError)
The requirement to include the assignment expression in parentheses in this situation is the opposite of the assignment statement's requirement, which cannot be included in parentheses.
However, the assignment expression doesn't always need parentheses:
import random
if value := random.randint(0, 3):
print(f"{value} is greater than 0")
1 is greater than 0
The assignment expression in the if
statement generates a random number between 0 and 3 and assigns it to the variable value
. The integers 1
, 2
, and 3
are truthy, and the code in the if
block will run when these values are generated. However, when random.randint()
returns 0
, which is falsy, the if
block is not executed.
The assignment statement is not valid after an if
keyword. Therefore, there's no confusion in this situation, and parentheses aren't required.
This example also demonstrates another key point about the assignment expression. The scope of the variable assigned follows the normal LEGB rules. Therefore, the variable value
is available everywhere in the global scope in this example:
import random
if value := random.randint(0, 3):
print(f"{value} is greater than 0")
print(value)
2 is greater than 0
2
If an assignment expression is used within a function definition, the variable will only be available locally within the function. This is the same behavior as for variables created using an assignment statement.
This example can be modified to show another potential pitfall when using the walrus operator. Let's change this code to include a comparison operator in the if
statement:
import random
if value := random.randint(0, 3) < 2:
print(f"{value} is less than 2")
True is less than 2
The output shown hints at the problem with this code since value
is the Boolean True
rather than an integer. The walrus operator has the lowest precedence of all operators, and only the comma has lower precedence. Therefore, the less than operator <
is evaluated first, and its output is assigned to value
. Parentheses are required to deal with this situation:
import random
if (value := random.randint(0, 3)) < 2:
print(f"{value} is less than 2")
0 is less than 2
The assignment expression is now evaluated first since parentheses have the highest precedence. The number returned by random.randint()
is assigned to value
, and it's used as the first operand for the less than operator.
Unlike the assignment statement, the walrus operator can only assign values to variable names and can't be used to assign values to subscripts or attributes:
(name := "James") # Valid
points = {"James": 10, "Mary": 20}
(points["Sarah"] := 30) # Invalid
class Article:
pass
article = Article()
(article.title := "The Walrus Operator") # Invalid
It's not possible to assign a value using a subscript or to an attribute using the walrus operator.
Conclusion
The assignment expression, which uses the :=
syntax and is often referred to as the walrus operator, is a relatively new addition to Python. The assignment statement, which uses the =
operator, is the more common way of assigning a value to a variable name.
However, the walrus operator extends this assignment operation by also returning the value that's assigned, which makes this assignment an expression. Therefore, you can use the assignment expression wherever you can use other expressions in Python.
However, the Python community is split on the usefulness and readability of the walrus operator. For this reason, the assignment expression isn't used as often as other new additions to Python. It's great to know how to use this operator, but don't overuse it!
Become a Data Engineer
I studied Physics and Mathematics at UG level at the University of Malta. Then, I moved to London and got my PhD in Physics from Imperial College. I worked on novel optical techniques to image the human retina. Now, I focus on writing about Python, communicating about Python, and teaching Python.