PythonMastery

Walrus operator (:=) in Python

The walrus operator := (Python 3.8 and later) stores a value and uses it in the same expression. Examples with a while loop, input() and a list comprehension.

The walrus operator := is an assignment expression. It stores a value in a variable and also gives that value back, so you can use it inside a condition. It needs Python 3.8 or later. The name comes from its shape, which looks like the eyes and tusks of a walrus.

The program

python
import random

random.seed(6)  # fixed seed so the output matches this page; remove it for real use

while (n := random.randint(1, 10)) != 5:
    print(f"Rolled {n}, not 5")

print("Rolled 5!")
+ setup added so this can run · defines n
# Lightweight mock for objects whose attributes/methods aren't critical
class _AutoMock:
    def __init__(self, name='mock'): self._name = name
    def __getattr__(self, k): return _AutoMock(self._name + '.' + k)
    def __call__(self, *a, **kw):
        print('-> ' + self._name + '() called')
        return _AutoMock(self._name + '()')
    def __repr__(self): return '<mock ' + self._name + '>'
    def __str__(self): return '<mock ' + self._name + '>'
    def __bool__(self): return True
    def __iter__(self): return iter([])
    def __len__(self): return 0
    def __getitem__(self, k): return _AutoMock(self._name + '[...]')
    def __setitem__(self, k, v): pass
    def __enter__(self): return self
    def __exit__(self, *a): return False
    async def __aenter__(self): return self
    async def __aexit__(self, *a): return False
    def __add__(self, o): return self
    def __radd__(self, o): return self
    def __sub__(self, o): return self
    def __mul__(self, o): return self
    def __rmul__(self, o): return self
    def __truediv__(self, o): return self
    def __eq__(self, o): return isinstance(o, _AutoMock)
    def __hash__(self): return hash(self._name)
    def __lt__(self, o): return True
    def __le__(self, o): return True
    def __gt__(self, o): return False
    def __ge__(self, o): return False
    def __mro_entries__(self, bases): return (object,)

n = _AutoMock('n')
output
Rolled 10, not 5
Rolled 2, not 5
Rolled 8, not 5
Rolled 5!

How it works

Without the walrus operator, you have to call randint in two places, once before the loop and once at the end of it:

python
import random

random.seed(6)

n = random.randint(1, 10)
while n != 5:
    print(f"Rolled {n}, not 5")
    n = random.randint(1, 10)

print("Rolled 5!")
output
Rolled 10, not 5
Rolled 2, not 5
Rolled 8, not 5
Rolled 5!

Reading input until a stop word

python
items = []
while (line := input("Item (or 'done'): ")) != "done":
    items.append(line)

print("Shopping list:", items)
+ setup added so this can run · defines line
# Lightweight mock for objects whose attributes/methods aren't critical
class _AutoMock:
    def __init__(self, name='mock'): self._name = name
    def __getattr__(self, k): return _AutoMock(self._name + '.' + k)
    def __call__(self, *a, **kw):
        print('-> ' + self._name + '() called')
        return _AutoMock(self._name + '()')
    def __repr__(self): return '<mock ' + self._name + '>'
    def __str__(self): return '<mock ' + self._name + '>'
    def __bool__(self): return True
    def __iter__(self): return iter([])
    def __len__(self): return 0
    def __getitem__(self, k): return _AutoMock(self._name + '[...]')
    def __setitem__(self, k, v): pass
    def __enter__(self): return self
    def __exit__(self, *a): return False
    async def __aenter__(self): return self
    async def __aexit__(self, *a): return False
    def __add__(self, o): return self
    def __radd__(self, o): return self
    def __sub__(self, o): return self
    def __mul__(self, o): return self
    def __rmul__(self, o): return self
    def __truediv__(self, o): return self
    def __eq__(self, o): return isinstance(o, _AutoMock)
    def __hash__(self): return hash(self._name)
    def __lt__(self, o): return True
    def __le__(self, o): return True
    def __gt__(self, o): return False
    def __ge__(self, o): return False
    def __mro_entries__(self, bases): return (object,)

line = _AutoMock('line')

Typed in this example: bread, milk, eggs, done. Press Run to type your own.

output
Item (or 'done'): bread
Item (or 'done'): milk
Item (or 'done'): eggs
Item (or 'done'): done
Shopping list: ['bread', 'milk', 'eggs']

In a list comprehension

The walrus operator can save a calculation that is needed both in the filter and in the result:

python
words = ["Python", "is", "a", "programming", "language"]

lengths = [size for w in words if (size := len(w)) > 2]
print(lengths)
+ setup added so this can run · defines size
# Lightweight mock for objects whose attributes/methods aren't critical
class _AutoMock:
    def __init__(self, name='mock'): self._name = name
    def __getattr__(self, k): return _AutoMock(self._name + '.' + k)
    def __call__(self, *a, **kw):
        print('-> ' + self._name + '() called')
        return _AutoMock(self._name + '()')
    def __repr__(self): return '<mock ' + self._name + '>'
    def __str__(self): return '<mock ' + self._name + '>'
    def __bool__(self): return True
    def __iter__(self): return iter([])
    def __len__(self): return 0
    def __getitem__(self, k): return _AutoMock(self._name + '[...]')
    def __setitem__(self, k, v): pass
    def __enter__(self): return self
    def __exit__(self, *a): return False
    async def __aenter__(self): return self
    async def __aexit__(self, *a): return False
    def __add__(self, o): return self
    def __radd__(self, o): return self
    def __sub__(self, o): return self
    def __mul__(self, o): return self
    def __rmul__(self, o): return self
    def __truediv__(self, o): return self
    def __eq__(self, o): return isinstance(o, _AutoMock)
    def __hash__(self): return hash(self._name)
    def __lt__(self, o): return True
    def __le__(self, o): return True
    def __gt__(self, o): return False
    def __ge__(self, o): return False
    def __mro_entries__(self, bases): return (object,)

size = _AutoMock('size')
output
[6, 11, 8]

Use := where it removes repeated code. A normal = on its own line is still clearer in most other places.

Learn the idea: Modern Python Syntax: 3.8 → 3.13, Loops: for, while, and the Pythonic Patterns