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
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')
Rolled 10, not 5 Rolled 2, not 5 Rolled 8, not 5 Rolled 5!
How it works
(n := random.randint(1, 10))picks a number, stores it inn, and gives the number to the!=comparison.- The loop body can then use
n, because it already holds the number that was just checked. - The brackets are needed. Without them,
n := random.randint(1, 10) != 5would storeTrueorFalseinn.
Without the walrus operator, you have to call randint in two places, once before the loop and once at the end of it:
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!")
Rolled 10, not 5 Rolled 2, not 5 Rolled 8, not 5 Rolled 5!
Reading input until a stop word
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.
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:
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')
[6, 11, 8]
Use := where it removes repeated code. A normal = on its own line is still clearer in most other places.