Introduction
Welcome to the next lesson in our journey to master Pythonic idioms. In our last session, we saw how starred unpacking (*rest) provides a declarative way to handle variable-length sequences, replacing clumsy slicing and making your intent clear. This is part of a broader theme: moving away from writing code that tells Python how to do something and toward code that describes what you want.
Today, we'll add another powerful tool to your arsenal: the assignment expression operator, more famously known as the walrus operator (:=). Introduced in Python 3.8, its purpose is to assign a value to a variable as part of a larger expression.
This lesson focuses on how to use the walrus operator to simplify a common but verbose pattern you often see in code migrated from other languages: loops that need to both check a value and then use that same value. By the end, you'll be able to refactor these loops into cleaner, more efficient, and more readable Python.
What is the Walrus Operator (:=)?
At its core, the walrus operator is an assignment expression. Unlike a standard assignment statement (=), which just assigns a value, an assignment expression both assigns the value and evaluates to that value.
Let's see the difference. A standard assignment statement doesn't return anything:
# This is a statement. It doesn't produce a value.
x = 10
# Therefore, this is a SyntaxError:
# print(x = 10)
An assignment expression, however, does produce a value:
# This is an expression. It assigns 10 to x AND evaluates to 10.
print(x := 10)
# Output:
# 10
# The variable is now available to use
print(x)
# Output:
# 10
This ability to assign and return in a single step is what unlocks its power.
The Walrus Operator: Python's Assignment Expressions
For a foundational understanding, let's turn to the excellent Real Python article on this topic. It introduces the operator and explains the key distinction between assignment statements and assignment expressions.
Read the sections 'Walrus Operator Fundamentals' and 'Motivation'. Pay close attention to the 'Hello, Walrus!' example and the explanation of how Python's design avoids common bugs found in languages like C.
Simplifying while Loops: The Primary Use Case
The most significant benefit of the walrus operator is cleaning up while loops that follow a repetitive pattern. Coming from Java, you're likely familiar with this structure:
- Read or compute a value before the loop starts.
- The
whilecondition checks this value. - The value is processed inside the loop.
- A new value is read or computed at the end of the loop, ready for the next iteration.
This leads to duplicated code. Let's see a concrete example of a loop that repeatedly asks for user input until they type "quit".
The "Java-ism" / Repetitive Way:
foods = []
food = input("What food do you like? (type 'quit' to exit) ")
while food != "quit":
foods.append(food)
# The input() call is duplicated here
food = input("What food do you like? (type 'quit' to exit) ")
print(foods)
This is a classic anti-pattern. The call to input() appears twice. A slightly more Pythonic, yet still clunky, alternative uses an infinite loop with a break:
The while True Way:
foods = []
while True:
food = input("What food do you like? (type 'quit' to exit) ")
if food == "quit":
break
foods.append(food)
print(foods)
This avoids repetition but obscures the loop's termination condition inside the body.
The Walrus Operator Way:
Here, we use (:=) to assign the result of input() to food directly within the while condition. The expression assigns the value and then evaluates to it, which is immediately compared to "quit".
foods = []
while (food := input("What food do you like? (type 'quit' to exit) ")) != "quit":
foods.append(food)
print(foods)
This version is concise, avoids repetition, and keeps the exit condition clearly in the while statement. This is the idiomatic Python solution.

The following video walks through this exact refactoring process, making the benefit crystal clear.
Python walrus operator := 🦦
The video 'Python walrus operator :=' by Bro Code offers a very direct, practical demonstration of refactoring a user input loop.
Watch the segment from 01:46 to 03:52. The presenter starts with the repetitive while True loop and refactors it line-by-line into the walrus operator version. This clearly illustrates the 'before' and 'after'.
This pattern is extremely common when processing data in chunks, like reading from a file or a network socket.
File Reading Example:
# Before: Repetitive read() call
with open('my_file.txt', 'rb') as f:
chunk = f.read(8192)
while chunk:
process_data(chunk)
chunk = f.read(8192)
# After: Walrus operator
with open('my_file.txt', 'rb') as f:
while (chunk := f.read(8192)):
process_data(chunk)
Improving Comprehensions and Other Expressions
While loops are the main use case, the walrus operator is also valuable for making comprehensions more efficient when a filter condition depends on an expensive computation that you also need in the expression part.
Before: Inefficient Comprehension
Imagine process_data is a slow function. In the code below, it's called twice for every item: once to check if the result is valid, and again to add it to the list.
def process_data(item):
# some expensive computation...
return result
data = [item1, item2, item3, ...]
# Inefficient: process_data() is called twice
processed_results = [process_data(x) for x in data if process_data(x) is not None]
After: Efficient Walrus Comprehension
With (:=), you can capture the result of the expensive call and reuse it.
# Efficient: process_data() is called only once
processed_results = [result for x in data if (result := process_data(x)) is not None]
This avoids redundant computation and keeps the logic within a single, readable list comprehension.
Python Walrus operator - with practical examples!
The video 'Python Walrus operator - with practical examples!' by BugBytes demonstrates this exact use case with a JSON parsing example.
Watch from 05:22 to 09:02. The example shows how to filter and parse a list of JSON strings. Notice how (:=) avoids calling the parsing function twice, making the list comprehension both cleaner and more performant.
Rules, Gotchas, and When to Avoid It
The walrus operator is powerful, but it's not a universal replacement for standard assignment. Misusing it can harm readability. As you requested, here is the "brutal honesty" about its pitfalls.
-
Parentheses are Crucial: The
:=operator has lower precedence than most other operators. This means you often need to wrap the assignment expression in parentheses to ensure it's evaluated correctly.- WRONG:
if value := len(my_list) > 10:will assign the booleanTrueorFalsetovalue. - RIGHT:
if (value := len(my_list)) > 10:correctly assigns the length tovalueand then compares it to 10.
- WRONG:
-
Not for Standalone Assignments: It is not meant to replace simple assignment. Doing
(x := 10)on its own line is valid but highly un-Pythonic. Always preferx = 10for simple assignments. -
Readability Over Cleverness: This is a core Python principle. If using the walrus operator makes an expression dense and difficult to understand, it's better to use a temporary variable and a separate line. The goal is clarity.
The Walrus Operator: Python's Assignment Expressions
The Real Python article we looked at earlier has a fantastic section on syntax rules and common pitfalls.
Read the sections 'Walrus Operator Syntax' and 'Walrus Operator Pitfalls'. This covers illegal uses (like with attributes obj.x := 1), precedence issues that necessitate parentheses, and the important constraint that your code will only run on Python 3.8 and later.
Exercise: Refactor a Loop
Now it's your turn to apply what you've learned. Below is a function that simulates receiving and processing messages from a queue. It uses a while True loop. Your task is to refactor it using the walrus operator to make the exit condition more explicit.
# A mock message queue
class MessageQueue:
def __init__(self, messages):
self._messages = messages
def get_message(self):
if self._messages:
return self._messages.pop(0)
return None # Return None when the queue is empty
# --- YOUR TASK IS BELOW ---
def process_all_messages_before(queue):
"""
Processes all messages from a queue using a while-True loop.
"""
processed = []
while True:
msg = queue.get_message()
if msg is None:
break
print(f"Processing message: {msg}")
processed.append(msg.upper())
return processed
def process_all_messages_after(queue):
"""
Refactor this function to use the walrus operator,
eliminating the while-True loop and break statement.
"""
processed = []
# TODO: Refactor the loop below using the walrus operator
while (msg := queue.get_message()) is not None:
print(f"Processing message: {msg}")
processed.append(msg.upper())
return processed
# --- Testing your solution ---
message_list = ["hello", "world", "end_of_stream"]
q = MessageQueue(message_list.copy())
print("--- Testing your refactored function ---")
results = process_all_messages_after(q)
print("Final processed list:", results)
assert results == ["HELLO", "WORLD", "END_OF_STREAM"]
print("✅ Test passed!")
Try to complete the process_all_messages_after function. The solution is hidden below.
Click to see the solution
def process_all_messages_after(queue):
"""
Refactor this function to use the walrus operator,
eliminating the while-True loop and break statement.
"""
processed = []
# The refactored loop combines the get_message() call and the check
while (msg := queue.get_message()) is not None:
print(f"Processing message: {msg}")
processed.append(msg.upper())
return processed
# --- Testing your solution ---
message_list = ["hello", "world", "end_of_stream"]
q = MessageQueue(message_list.copy())
print("--- Testing your refactored function ---")
results = process_all_messages_after(q)
print("Final processed list:", results)
assert results == ["HELLO", "WORLD", "END_OF_STREAM"]
print("✅ Test passed!")
Conclusion
You have successfully learned how to wield the walrus operator (:=), a tool that, when used correctly, embodies the Pythonic principles of clarity and conciseness. You've seen how to transform repetitive, multi-step loops into elegant one-liners that clearly state their intent.
Key Takeaways:
- The walrus operator
(:=)is an assignment expression: it assigns a value to a variable and evaluates to that value in a single step. - Its primary use case is to simplify
whileloops that would otherwise require duplicate code or awhile True/breakstructure. - It is also useful for improving the efficiency of comprehensions by avoiding repeated calls to expensive functions.
- Always prioritize readability. Use parentheses to clarify precedence, and if an expression becomes too complex, revert to a simpler, multi-line approach.
In our next lesson, we will continue exploring powerful looping constructs by learning how to apply extended unpacking directly in for loop variables (e.g., for a, *rest in rows:). This will further enhance your ability to write declarative code that elegantly handles complex data structures.