Some practical use cases for the walrus operator¶

I remember there was a controversy around the inclusion of the walrus operator (denoted by := and detailed here) in Python 3.7. However, I never really looked into when you would actually use it.

Recently, I was writing some code that performed some duplicate operations in a list comprehension and searched for a way to avoid duplication. The solution: the walrus operator!

Here's a list of tokens, each (for some reason) has ended up with a space either side.

In [1]:
tokens = [
    " hello ",
    "   ",
    " world ",
    " ! ",
    " how ",
    "   ",
    " are ",
    "   ",
    " you ",
    "   ",
    " today ",
    " ? ",
]

We'd like to remove the extra space, as well as remove any blank/empty tokens.

We can do this with the string's strip method. However, we'd need to call strip twice. Once in the list comprehension to check if it's an empty string, and then again for each expression we want to include in the final list.

An example:

In [2]:
cleaned_tokens = [token.strip() for token in tokens if token.strip()]

cleaned_tokens
Out[2]:
['hello', 'world', '!', 'how', 'are', 'you', 'today', '?']

We do this with a single call to strip using the walrus operator.

Note: the the expression with the walrus operator needs to be enclosed in parentheses.

In [3]:
cleaned_tokens = [cleaned_token for token in tokens if (cleaned_token := token.strip())]

cleaned_tokens
Out[3]:
['hello', 'world', '!', 'how', 'are', 'you', 'today', '?']

What if we wanted to filer values out of the list comprehension?

Below, we'll filter our tokens that have less than 2 characters after being cleaned.

In [4]:
token_lengths = [len(token.strip()) for token in tokens if len(token.strip()) > 1]

token_lengths
Out[4]:
[5, 5, 3, 3, 3, 5]

Again, we can do this with the walrus operator.

Note: the comparison operation needs to be outside the parentheses that enclose the walrus operator.

In [5]:
token_lengths = [len_token for token in tokens if (len_token := len(token.strip())) > 1]

token_lengths
Out[5]:
[5, 5, 3, 3, 3, 5]

Outside of list comprehensions, the most common other use case seems to be in if statements, where we want to "capture" a value in a variable.

For an example without using the walrus operator:

In [6]:
kv = {"a": 0, "b": 1, "c": 2}


def fn(character: str, d: dict):
    index = d.get(character)
    if index is not None:
        return f"Found {character} at index {index}"
    else:
        return f"Couldn't find {character}"
In [7]:
fn("a", kv)
Out[7]:
'Found a at index 0'
In [8]:
fn("z", kv)
Out[8]:
"Couldn't find z"

fn looks alright, but what if we could perform the get and is None and assign the value to index all in one go?

In [9]:
def fn(character: str, d: dict):
    if (index := d.get(character)) is not None:
        return f"Found {character} at index {index}"
    else:
        return f"Couldn't find {character}"
In [10]:
fn("a", kv)
Out[10]:
'Found a at index 0'
In [11]:
fn("z", kv)
Out[11]:
"Couldn't find z"

There's also a way to use the walrus operator in while loops (which I don't seem to use a lot).

In [12]:
letters = ["a", "b", "c", "d", "e"]

i = 0
while letters[i].upper() != "D":
    print(f"Got: {letters[i].upper()}")
    i += 1
Got: A
Got: B
Got: C

The above calls letters[i].upper() twice, which we can simplify with our trusty walrus operator:

In [13]:
i = 0
while (upper_letter := letters[i].upper()) != "D":
    print(f"Got: {upper_letter}")
    i += 1
Got: A
Got: B
Got: C

Finally, using the walrus operator with the any operator, where we want to find if any item in a list comprehension is True, and an instance of one of those results.

Without the walrus operator, we have to do this ugly filter, check, index at zero:

In [14]:
animals = ["ant", "baboon", "cat", "dog", "elephant"]

c_animals = [animal for animal in animals if animal.startswith(("c", "C"))]

if c_animals:
    print(f"Found {c_animals[0]}")
else:
    print("No animal starts with C")
Found cat

However, with the walrus operator we can do all of that in a single line!

In [15]:
if any((c_animal := animal).startswith(("c", "C")) for animal in animals):
    print(f"Found {c_animal}")
else:
    print("No animal starts with C")
Found cat

There you go. There's some use cases for the walrus operator without confusing you by using the phrase "named expression" once!