The walrus operator (:=), formally known as an assignment expression, was introduced in Python 3.8 via PEP 572 and quickly became one of the most talked-about features in the language. It lets you assign values to variables inside an expression — something that wasn't possible before.

In this guide, you'll understand exactly what the walrus operator is, how to use it correctly, when it actually simplifies your code, and when you should avoid it.

What Is the Walrus Operator?

The walrus operator (:=) assigns a value to a variable while simultaneously using that value in an expression. It gets its name from the way it looks like the eyes and tusks of a walrus lying on its side.

Basic syntax:

(variable := expression)

Unlike the traditional assignment operator (=), which is a statement and doesn't return a value, the walrus operator is an expression that returns the assigned value. This means you can use it inside other expressions.

# Without walrus operator
n = len(items)
if n > 10:
    print(f"The list has {n} items")

With walrus operator

if (n := len(items)) > 10: print(f"The list has {n} items")

The parentheses around the walrus expression matter. Though not always required, they are strongly recommended by the official Python documentation to avoid ambiguity.

Classic Use Cases

1. While Loops with Assignment

The most common use case is in while loops where you need to read input and check its validity in one line:

# Classic: reading lines from a file
with open("data.txt") as file:
    while (line := file.readline()):
        print(line.strip())

Before Python 3.8, you had to write:

with open("data.txt") as file: line = file.readline() while line: print(line.strip()) line = file.readline()

The second version duplicates code and is error-prone (forgetting the extra line = file.readline() at the end causes an infinite loop). The walrus operator eliminates this problem entirely.

2. List Comprehensions

In Python functions that get called multiple times inside a comprehension, the walrus operator prevents repeated processing:

# Without walrus: expensive_calc() runs 2x per item when True
bad_results = [expensive_calc(x) for x in data if expensive_calc(x) > 0]

With walrus: expensive_calc() runs 1x per item

results = [val for x in data if (val := expensive_calc(x)) > 0]

This is especially useful in filters that depend on the result of a costly computation. If you're interested in writing cleaner Python, also check our guide on Python type hints, which pair perfectly with assignment expressions.

3. Validation with If

# Regex: capture and check in one go
import re
text = "Contact: [email protected]"
if (match := re.search(r'[\w.+-]+@[\w-]+\.[\w.-]+', text)):
    print(f"Email found: {match.group()}")

Dictionary processing

user = {"name": "John", "age": 25} if (age := user.get("age")) and age >= 18: print(f"{user['name']} is an adult")

Advanced Use Cases

Walrus in Match Case (Python 3.10+)

Since Python 3.10, the walrus operator works together with structural pattern matching for even more expressive code:

match command.split():
    case ["quit" | "exit"]:
        print("Shutting down...")
    case ["hello" | "hi", (name := str())]:
        print(f"Hello, {name}!")
    case _:
        print("Unknown command")

Walrus with Nested Comprehensions

# Extract values that meet criteria and process them
data = [3, 7, 2, 9, 4, 8, 1, 6]
result = [
    (half, double)
    for x in data
    if (half := x / 2) > 2 and (double := x * 2) < 20
]
print(result)  # [(3.5, 6), (4.0, 8)]

Debugging with Walrus

A neat trick is using the walrus operator to inspect intermediate values without refactoring your code:

# Capture intermediate result for debugging
result = [
    (debug := x ** 2 + 1)
    for x in range(5)
]
print(debug)  # Last computed value: 17

Does the Walrus Operator Improve Performance?

The main performance benefit doesn't come from the operator itself, but from eliminating redundant function calls. Consider this:

import time

def heavy_compute(n): """Simulates an expensive calculation""" time.sleep(0.001) return n ** 2

numbers = list(range(100))

Without walrus: heavy_compute runs 2x per item when True

start = time.time() old_result = [heavy_compute(n) for n in numbers if heavy_compute(n) > 50] print(f"Without walrus: {time.time() - start:.3f}s")

With walrus: heavy_compute runs 1x per item

start = time.time() new_result = [v for n in numbers if (v := heavy_compute(n)) > 50] print(f"With walrus: {time.time() - start:.3f}s")

The time difference is dramatic, especially for expensive computations. For an in-depth analysis of Python performance, check the official Python performance tips.

When NOT to Use the Walrus Operator

The walrus operator can hurt readability if overused. Here's when to avoid it:

❌ Overly Complex Expressions

# ❌ Avoid: hard to read and understand
if (a := complex_func(b := another_func(c := 10))) > 5:
    print(a, b, c)

✅ Prefer: clear and explicit

c = 10 b = another_func(c) a = complex_func(b) if a > 5: print(a, b, c)

❌ Replacing Simple Assignments

# ❌ Unnecessary
if (x := 10) > 5:
    print(x)

✅ Better

x = 10 if x > 5: print(x)

❌ Inside F-Strings (Be Careful!)

# ⚠️ Works but confusing
print(f"{(x := 5)} squared is {x ** 2}")

✅ Clearer

x = 5 print(f"{x} squared is {x ** 2}")

The Community Debate

The walrus operator sparked one of the biggest controversies in Python history. Language creator Guido van Rossum stepped down as BDFL (Benevolent Dictator for Life) in part due to disagreements over PEP 572. Despite the controversy, the proposal was accepted and shipped in Python 3.8.

Arguments in favor:

  • Eliminates duplicated code
  • Cleaner code for common patterns (while + read, if + assign)
  • Prevents errors from forgetting the extra assignment in loops

Arguments against:

  • Steeper learning curve for beginners
  • Potential for abuse and unreadable code
  • Visual similarity with the assignment operator (=) can be confusing

If you want to dive deep into the technical reasoning, read the full PEP 572.

Variable Scope with Walrus

One important detail is the scope of variables created with :=. Unlike what many think, the assigned variable lives on in the current scope, not just inside the expression:

# The 'match' variable persists after the expression
if (match := re.search(r'\d+', "abc123def")):
    print(f"Number found: {match.group()}")

'match' still exists here!

print(match.group()) # Works: prints "123"

In list comprehensions before Python 3.12, the variable leaks out

[item for _ in range(1) if (item := "leaked")] print(item) # Python < 3.12: "leaked"; Python 3.12+: NameError

This scoping behavior was one of the main criticisms during the walrus operator discussion. For all the technical details, see the scope section of PEP 572 and the official Python FAQ on walrus.

Walrus with Dictionaries and JSON

The operator is extremely handy when processing JSON data and dictionaries, especially when you need to check if a key exists and process its value at the same time:

import json

data = json.loads('{"user": "john", "email": "[email protected]", "age": 28}')

Check and process required fields

if (name := data.get("user")) and (email := data.get("email")): print(f"Processing: {name} <{email}>")

Batch processing with walrus

users = [ {"name": "John", "points": 150}, {"name": "Jane", "points": 80}, {"name": "Bob", "points": 200}, {"name": None, "points": 0} ]

Filter valid users with high scores

valid = [ {"name": u["name"], "points": u["points"], "tier": "gold" if u["points"] >= 150 else "silver"} for u in users if u["name"] and (points := u["points"]) > 50 ]

This pattern is common in ETL pipelines and data processing workflows. For more examples, see the Real Python walrus operator tutorial and the GeeksforGeeks reference.

Real-World Examples

Large File Processing

For files too large to fit in memory, combining while with the walrus operator is the ideal approach:

from pathlib import Path

def process_large(filepath: str, chunk: int = 8192) -> int: total = 0 with Path(filepath).open("rb") as f: while (data := f.read(chunk)): total += data.count(b"\n") return total

Usage

lines = process_large("server_log.txt") print(f"Total log entries: {lines}")

Real-Time Result Caching

# Simple cache with walrus
def fetch_api_data(url: str) -> dict:
    if url not in cache and (response := requests.get(url)).ok:
        cache[url] = response.json()
    return cache.get(url)

These patterns are widely used in production applications. For more context, check the article on Python 3.9 new features at Real Python, which covers comprehension improvements that benefit walrus usage.

Best Practices

  1. Always use parentheses around the walrus expression to avoid ambiguity
  2. Only use it where it truly simplifies the code (while + read, if + regex, comprehensions)
  3. Never nest multiple walrus operators in the same expression
  4. Prefer readability over conciseness — clear code beats short code
  5. Combine with type hints to make code even more expressive
# ✅ Well-written example
import re
from pathlib import Path

def process_log(filepath: str) -> list[str]: errors: list[str] = [] pattern = re.compile(r'ERROR: (.+)')

with Path(filepath).open() as f:
    while (line := f.readline()):
        if (match := pattern.search(line)):
            errors.append(match.group(1))

return errors</code></pre>

Summary

The walrus operator (:=) is a powerful addition to Python that, when used wisely, makes your code cleaner, more efficient, and more expressive. Key takeaways:

  • ✅ Assigns values inside expressions
  • ✅ Eliminates duplicated code in while loops and comprehensions
  • ✅ Improves performance by preventing redundant calls in filters
  • ✅ Available since Python 3.8
  • ⚠️ Use sparingly — readability always comes first

To continue learning, explore these official resources:

Master this tool and your Python code will be more Pythonic, efficient, and elegant!