ZetCode

Python walrus operator

last modified May 10, 2026

In this article we show how to use the walrus operator in Python.

Python 3.8 introduced the walrus operator, written as :=. The name comes from the fact that the symbol resembles the eyes and tusks of a walrus seen from the side. Formally, the feature is called an assignment expression because it allows you to assign a value to a variable *inside* a larger expression.

The walrus operator makes code more compact by combining an assignment with a condition, a return value, or a comprehension. Instead of writing the assignment on a separate line and then testing the variable, you can do both in one step. However, it should be used judiciously – clarity should never be sacrificed for brevity.

print(is_new := True)

Here we assign True to the variable is_new and immediately pass that value to print(). The expression (is_new := True) evaluates to True, so the output is True.

is_new = True
print(is_new)

Without the walrus operator, we need two lines: one for the assignment and one for the print. The walrus version collapses them into a single expression.

Reading input

In the following example, we use the walrus operator inside a while loop to read user input until the user types “quit”.

read_words.py
#!/usr/bin/python

words = []

while (word := input("Enter word: ")) != "quit":
    words.append(word)

print(words)

The expression (word := input("Enter word: ")) does two things: it prompts the user for input and assigns the result to word. The whole expression then returns that value, which is compared against "quit". Without the walrus operator you would need to call input() separately inside the loop body, duplicating the prompt or writing an endless loop with a break.

$ ./read_words.py
Enter word: cloud
Enter word: falcon
Enter word: rock
Enter word: quit
['cloud', 'falcon', 'rock']

Walrus with if condition

Suppose that all our words must have at least three characters. The walrus operator can capture the length of a word and test it in one if-statement.

test_length.py
#!/usr/bin/python

words = ['falcon', 'sky', 'ab', 'water', 'a', 'forest']

for word in words:
    if ((n := len(word)) < 3):
        print(f'warning, the word {word} has {n} characters')

Inside the condition if ((n := len(word)) < 3), the variable n is assigned the length of the current word, and that length is then compared to 3. If the condition is true, we already have n available to use in the warning message. Without the walrus, we would need to call len(word) twice or introduce a separate assignment line.

$ ./test_length.py
warning, the word ab has 2 characters
warning, the word a has 1 characters

Reading a file

The walrus operator is especially handy when reading a file line by line.

words.txt
falcon
sky
cloud
water
rock
forest
read_file.py
#!/usr/bin/python

with open('words.txt', 'r') as f:

    while line := f.readline():

        print(line.rstrip())

The readline() method returns an empty string when the end of the file is reached. The expression line := f.readline() assigns each line to line and then the while loop evaluates it; a non-empty string is truthy, an empty string is falsy, so the loop stops automatically. This eliminates the need for a separate line = f.readline() before the loop and at the end of each iteration.

Traversing a container

When working with lists of dictionaries that may contain missing values, the walrus operator helps to extract and test data in one go.

traversing.py
#!/usr/bin/python

users = [ 
    {'name': 'John Doe', 'occupation': 'gardener'},
    {'name': None, 'occupation': 'teacher'}, 
    {'name': 'Robert Brown', 'occupation': 'driver'}, 
    {'name': None, 'occupation': 'driver'}, 
    {'name': 'Marta Newt', 'occupation': 'journalist'} 
] 
  
for user in users:  
    if ((name := user.get('name')) is not None): 
        print(f'{name} is a {user.get("occupation")}') 

The expression name := user.get('name') retrieves the value for the key 'name' (which may be None) and binds it to name. The is not None check then decides whether to print the user. The alternative would require a separate line to fetch the name before the if.

$ ./traversing.py
John Doe is a gardener
Robert Brown is a driver
Marta Newt is a journalist

Only users with a non-None name are shown.

Regular expressions

Regular expression searches are another natural fit. The walrus operator allows you to perform a match and check its success at the same time.

search.py
#!/usr/bin/python

import re

data = 'There is a book on the table.'

pattern = re.compile(r'book')

if match := pattern.search(data):
    print(f'The word {pattern.pattern} is at {match.start(), match.end()}') 
else:
    print(f'No {pattern.pattern} found')

match := pattern.search(data) runs the search and assigns the result (a match object or None). Because assignment expressions evaluate to the assigned value, the if block executes only when a match is found, and we already have the match object ready for further inspection.

$ ./search.py
The word book is at (11, 15)

The word book was found at indices 11 to 15.

List comprehensions

One of the most powerful uses of the walrus operator is inside list comprehensions. It lets you reuse a computed value without calling the function twice.

comprehension.py
#!/usr/bin/python

def normalize(s):
    return s.strip().title()

raw_names = ['  john ', 'JANE', ' bob  ', 'ALICE']

# Without walrus - normalization called twice
names1 = [normalize(n) for n in raw_names if len(normalize(n)) > 3]

# With walrus - computed once and captured
names2 = [name for n in raw_names if (name := normalize(n)) and len(name) > 3]

print(names1)
print(names2)

In the first comprehension normalize(n) is called twice for elements that pass the filter. In the second, the walrus operator captures the result of normalize(n) in the variable name, which is then immediately used in both the condition and the output expression. This can improve performance when the computation is expensive.

Important: Variables assigned with the walrus operator inside a comprehension leak into the surrounding scope (in Python 3.8+). After the comprehension runs, name will still hold the last normalized value. This is different from the iteration variable n, which also leaks. Be mindful of unintentional side effects.

Function calls

The walrus operator can also be used to capture the return value of a function call inside a while loop or an if statement, avoiding a separate assignment beforehand.

retry.py
#!/usr/bin/python

import random

def unreliable_operation():
    # Simulates an operation that sometimes fails
    return random.choice([None, 42, None, 42])

attempts = 0
while (result := unreliable_operation()) is None and attempts < 10:
    attempts += 1
    print(f'Attempt {attempts} failed, retrying...')

if result is not None:
    print(f'Success after {attempts} attempts, result={result}')
else:
    print('All attempts failed')

Here the loop condition both calls the function and binds its return value to result. We then check if it is None and whether we have retries left. After the loop, result is available for the final decision. This pattern avoids declaring result before the loop and updating it inside.

Socket communication

When writing network code, the walrus operator can simplify loops that read data in chunks until the connection is closed. In this example we connect to a web server and receive the response piece by piece.

socket_client.py
import socket
import ssl

HOST = "example.com"
PORT = 443  # HTTPS default port

# Create a default SSL context (loads trusted CA certificates)
context = ssl.create_default_context()

# Build the request
request = (
    f"GET / HTTP/1.1\r\n"
    f"Host: {HOST}\r\n"
    f"Connection: close\r\n"
    f"\r\n"
).encode("utf-8")

# Establish the connection
with socket.create_connection((HOST, PORT)) as sock:

    # Wrap the raw socket with SSL
    # 'server_hostname' is required for the SSL handshake (SNI)
    with context.wrap_socket(sock, server_hostname=HOST) as ssock:
        ssock.sendall(request)

        # Use the walrus operator to read the encrypted stream
        while chunk := ssock.recv(4096):
            print(chunk.decode("utf-8", errors="ignore"), end="")

The call to s.recv(4096) returns a bytes object of up to 4096 bytes. When the server closes the connection, recv() returns an empty bytes object (b''), which is falsy. By using the walrus operator in the loop condition, we capture each chunk in the variable line and immediately evaluate it. The loop terminates automatically once the empty chunk signals the end of the response.

Source

The Python Language Reference

In this article we have worked with the Python walrus operator.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Python tutorials.