5. Assignment Expressions
By Bernd Klein. Last modified: 30 Nov 2023.
Introduction
This section in our Python tutorial explores the "assignment expression operator" affectionately known to many as "the walrus operator. So, you can call it either the "walrus operator" or the "assignment expression operator," and people in the Python programming community will generally understand what you mean. Despite being introduced in Python version 3.8, we can still view it as a relatively recent addition to the language. The assignment expressions have been discussed in PEP 572 and this is what was written about the naming:
During discussion of this PEP, the operator became informally known as "the walrus operator". The construct's formal name is "Assignment Expressions" (as per the PEP title), but they may also be referred to as "Named Expressions" (e.g. the CPython reference implementation uses that name internally).
The purpose of this feature is not a new way to assign objects to variables, but it gives programmers a convenient way to assign variables in the middle of expressions.
You might still be curious about the origin of the term "walrus operator." It's affectionately named this way because, with a touch of imagination, the operator's characters resemble the eyes and tusks of a walrus.
We will introduce the assignment expression by comparing it first with a simple assignment statement. A simple assignment statement can also be replaced by an assignment expression, even though it looks clumsy and is definitely not the intended use case of it:
x = 5
# can be written as:
(x := 5) # valid, but not recomended!
# the brackets are crucial
OUTPUT:
5
First you may wonder about the brackets, when you look at the assignment expression. It is not meant as a replacement for the simple "assignment statement". Its primary role is to be utilized within expressions
The following code shows a proper usecase:
x = 4.56
z = (square := x**2) - (6.6 / square)
print(z)
OUTPUT:
20.476194644506
Though you may argue that the following code might be even clearer:
x = 4.56
square = x**2
z = square - (6.6 / square)
print(z)
OUTPUT:
20.476194644506
Here's another Python code example that demonstrates a similar scenario. First with the walrus operator:
# With the walrus operator
n = int(input("Please give a number: "))
if (square := n ** 2) > 3:
print("The number is greater than 3.")
print(f"The square of {n} is {square}.")
OUTPUT:
The number is greater than 3. The square of 42 is 1764.
Now without the walrus operator:
n = int(input("Please give a number: "))
square= n ** 2
if square > 3:
print("The number is greater than 3.")
print(f"The square of {n} is {square}.")
OUTPUT:
The number is greater than 3. The square of 42 is 1764.
Live Python training
Enjoying this page? We offer live Python training courses covering the content of this site.
Beneficial applications
Now, let's explore some examples where the usage of the assignment expression is really beneficial compared to code not using it.
def f(x):
return x + 4
numbers = [3, 7, 2, 9, 12]
odd_numbers = [result for x in numbers if (result := f(x)) % 2]
odd_numbers
OUTPUT:
[7, 11, 13]
You can observe that if we choose to avoid using the assignment expression in the list comprehension, we would need to call the function twice. So using the assgment expression makes the code in this context more efficient.
def f(x):
return x + 4
numbers = [3, 7, 2, 9, 12]
odd_numbers = [f(x) for x in numbers if f(x) % 2]
odd_numbers
OUTPUT:
[7, 11, 13]
Use Case: Regular Expressions
There is also a great advantage when we use regular expressions. Like in our previous examples we present again two similiar code snippets, the first one without and the second one with the walrus operator:
import re
txt = """The Python training course started at 2022-02-4
the other one at 2022-01-24
only one date per line, if at all
the dates may also be in this format 2020/10/15
or 20-10-04"""
for line in txt.split('\n'):
date = re.search(r'(\d{2,4})[-/](\d{2})[-/](\d{2})', line)
if date:
year, month, day = date.groups()
print(f"Found date: {year}-{month}-{day}")
OUTPUT:
Found date: 2022-01-24 Found date: 2020-10-15 Found date: 20-10-04
Now, the code using assignment expressions:
import re
txt = """The Python training course started at 2022-02-4
the other one at 2022-01-24
only one date per line, if at all
the dates may also be in this format 2020/10/15
or 20-10-04"""
# Split the text into lines and search for dates in each line
for line in txt.split('\n'):
# Using the assignment expression to find and store the date match
if (date := re.search(r'(\d{2,4})[-/](\d{2})[-/](\d{2})', line)):
year, month, day = date.groups()
print(f"Found date: {year}-{month}-{day}")
OUTPUT:
Found date: 2022-01-24 Found date: 2020-10-15 Found date: 20-10-04
Assignment Expression in File Reading
Fixed-width Files
Files in which each line has the same length are often referred to as "fixed-width" or "fixed-length" files. In these files, each line (or record) is structured so that it contains a predefined number of characters, and the fields within the lines have fixed positions. The files can be read by using the read
-method and the walrus operator:
with open('training_course_info.txt') as fh:
while ((data := fh.read(53)) != ''):
print(data.rstrip())
OUTPUT:
Python Training Course for Beginners Berlin 08 Python Intermediate Course Hamburg 06 Python Advanced Training Course Frankfurt 08
Now the same in traditional coding style:
with open('training_course_info.txt') as fh:
data = fh.read(52)
while data:
print(data.rstrip())
data = fh.read(52)
OUTPUT:
Python Training Course for Beginners Berlin 08 Python Intermediate Course Hamburg 0 6 Python Advanced Training Course Frankfurt 08
The benefits for the most recent code snippet are
- It uses a more traditional coding style, which may be more familiar to some developers.
- It assigns and uses the data variable explicitly, making the code easier to understand for those not familiar with assignment expressions.
However, a significant drawback of this approach is that it requires the explicit assignment and reassignment of the data
variable, which can be seen as less concise and somewhat less elegant.
Using readline
Most people use a for
loop to iterator over a text file line by line. With the walrus operator, we can also elegantly go through a text using the method readline
:
word_to_find = "Advanced"
with open("training_course_info.txt") as file:
while (line := file.readline()):
if word_to_find in line:
print(line)
OUTPUT:
Python Advanced Training Course Frankfurt 08
Code snippet using readline
but not the assignment expression:
word_to_find = "Advanced"
with open("training_course_info.txt") as file:
line = "It shouldn't be empyt that's all"
while line:
line = file.readline()
if word_to_find in line:
print(line)
OUTPUT:
Python Advanced Training Course Frankfurt 08
Now with the walrus operator:
word_to_find = "Training"
with open("training_course_info.txt") as file:
while (line := file.readline()):
if word_to_find in line:
print(line)
OUTPUT:
Python Training Course for Beginners Berlin 08 Python Advanced Training Course Frankfurt 08
Another Use Case
In the chapter on while loops of our Python Tutorial, we had a little number guessing game:
import random
lower_bound, upper_bound = 1, 20
to_be_guessed = random.randint(lower_bound, upper_bound)
guess = 0
while guess != to_be_guessed:
guess = int(input("New number: "))
if guess > to_be_guessed:
print("Number too large")
elif guess < to_be_guessed:
print("Number too small")
else:
print("Congratulations. You made it!")
OUTPUT:
Number too small Congratulations. You made it!
As you can see, we had to initialize guess
to zero to be able to enter the loop. We can do the initialization directly in the loop condition with an assignment expression and simplify the whole code by this:
import random
lower_bound, upper_bound = 1, 20
to_be_guessed = random.randint(lower_bound, upper_bound)
while (guess := int(input("New number: "))) != to_be_guessed:
if guess > to_be_guessed:
print("Number too large")
elif guess < to_be_guessed:
print("Number too small")
else:
print("Congratulations. You made it!")
OUTPUT:
Number too small Congratulations. You made it!
Critiques of the Walrus Operator in Python
We said in the beginning of this page that some Python programmers longed for this construct for quite a while. One reason why it was not introduced earlier was the fact that it can also be used to write code which is less readable. In fact, the application of the walrus operator contradicts several principles highlighted in the Zen of Python. To grasp these contraventions, let's delve into the ensuing scenarios.
The Zen of Python says "Explicit is better than implicit". The walrus operator violates this requirement of the Zen of Python. This means that explicit operations are always better than implicit operations. The walrus operator assigns a value to a variable and implicitly returns the value as well. Therefore, it does not align with the concept of "Explicit is better than implicit."
The following code snippet is shows an extreme example which is not recommended to use:
a, b, c = 1, 2, 3
x = 4
y = (c := (a := x*2.3) + (b := x*4.5 -3))
What are your thoughts on the following piece of code using the Python assignment operator inside of a print function call?
print((a := x*2.3) + (b := x*4.5 -3) + (x := 4))
OUTPUT:
28.2
These Python code snipets certainly violate two other demands of the Zen of Python:
- Beautiful is Better Than Ugly
- Complex is Better Than Complicated
The principle "There should be one and preferably only one obvious way to do it" from the Zen of Python emphasizes the importance of having a clear and singular approach. When we have the option to separate assignment and return operations into distinct statements, opting for the less readable and more complex walrus operator contradicts this principle.
z = (x:=3) + (y:=4)
This is the preferred way to do it
x, y = 3, 4
z = x + y
Live Python training
Enjoying this page? We offer live Python training courses covering the content of this site.
Upcoming online Courses