The Walrus Operator `:=` in Python

The Walrus Operator `:=` in Python
5 min read

Python is constantly evolving, with each new version bringing various optimizations and new tools. In Python 3.8, the Walrus Operator (:=) was introduced, sparking significant debate within the community. This article delves into the Walrus Operator, its implications, and its uses.

The Story Behind the Walrus Operator and Guido van Rossum's Resignation

Guido van Rossum, the creator of Python, played a central role in decision-making for Python's development. For a long time, he single-handedly determined Python's direction, studying user feedback and personally selecting changes for each new version. This earned him the semi-humorous title of Python's "Benevolent Dictator for Life."

In 2018, Guido announced his resignation from this position, citing PEP 572, which introduced the Walrus Operator. The document led to heated debates among Python developers. Many believed that the ideas in PEP 572 contradicted Python's philosophy and reflected Guido's personal opinion rather than industry best practices. Some developers found the syntax of := complex and unintuitive. Nevertheless, Guido approved PEP 572, and the Walrus Operator was included in Python 3.8.

The backlash was overwhelming. Guido received numerous negative comments and eventually decided to step down. In his resignation letter, he expressed his frustration: "I don't ever want to have to fight so hard for a PEP and see so much negativity directed at me."

After Guido's resignation, the project governance model was revised. A steering council of senior developers, who had made significant contributions to Python, was formed to make final decisions. Guido later returned to the project as a core developer, contributing without the burden of leadership.

Understanding the Walrus Operator

Syntax and Usage

The Walrus Operator := was introduced in Python 3.8 and allows assigning a value to a variable and returning that value simultaneously. The basic syntax is:

variable := expression

First, expression is evaluated, then its value is assigned to variable, and finally, this value is returned. The operator is called "walrus" because it resembles a walrus's eyes and tusks.

Differences from the Assignment Operator =

The main difference between := and the classic assignment operator = is that := allows assigning values within expressions. For example:

num = 7
print(num)

Using the Walrus Operator, this can be condensed to one line:

print(num := 7)

This assigns 7 to num and then returns it as an argument for print(). Using = in a similar way would result in a syntax error.

Practical Examples of the Walrus Operator

Example 1: Filtering and Printing Keyword Lengths

Consider the task of printing Python keywords longer than five characters:

from keyword import kwlist

for word in kwlist:
    if len(word) > 5:
        print(f'{word} has {len(word)} characters.')

This code calculates len(word) twice. Using :=, we can optimize it:

from keyword import kwlist

for word in kwlist:
    if (n := len(word)) > 5:
        print(f'{word} has {n} characters.')

Example 2: Input Loop Until "stop"

The task is to collect words until "stop" is entered:

words = []
word = input()

while word != 'stop':
    words.append(word)
    word = input()

With :=, this can be simplified:

words = []
while (word := input()) != 'stop':
    words.append(word)

Example 3: Reading File Lines

Reading lines from a file until an empty line is encountered:

with open('input.txt', 'r') as file:
    line = file.readline().rstrip()
    while line:
        print(line)
        line = file.readline().rstrip()

With :=, it becomes:

with open('input.txt', 'r') as file:
    while line := file.readline().rstrip():
        print(line)

Example 4: Filtering Factorials

Generating a list of factorials less than 1000:

from math import factorial

data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
new_data = [factorial(x) for x in data if factorial(x) <= 1000]
print(new_data)

With :=, the factorial is computed only once:

from math import factorial

data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
new_data = [fact for num in data if (fact := factorial(num)) <= 1000]
print(new_data)

Example 5: Extracting Non-None Names

Filtering a list of dictionaries to print names and occupations:

users = [
    {'name': 'Timur Guev', 'occupation': 'python generation guru'},
    {'name': None, 'occupation': 'driver'},
    {'name': 'Anastasiya Korotkova', 'occupation': 'python generation bee'},
    {'name': None, 'occupation': 'driver'},
    {'name': 'Valeriy Svetkin', 'occupation': 'python generation bee'}
]

for user in users:
    name = user.get('name')
    if name is not None:
        print(f'{name} is a {user.get("occupation")}.')

With :=, it is simplified:

users = [
    {'name': 'Timur Guev', 'occupation': 'python generation guru'},
    {'name': None, 'occupation': 'driver'},
    {'name': 'Anastasiya Korotkova', 'occupation': 'python generation bee'},
    {'name': None, 'occupation': 'driver'},
    {'name': 'Valeriy Svetkin', 'occupation': 'python generation bee'}
]

for user in users:
    if (name := user.get('name')) is not None:
        print(f'{name} is a {user.get("occupation")}.')

Example 6: Pattern Matching

Matching patterns in a text:

import re

text = 'Python is a powerful programming language.'
pattern1 = r'beegeek'
pattern2 = r'Python'

m = re.search(pattern1, text)
if m:
    print(f'Match found: {m.group()}')
else:
    m = re.search(pattern2, text)
    if m:
        print(f'Match found: {m.group()}')
    else:
        print('No matches')

With :=, it can be streamlined:

import re

text = 'Python is a powerful programming language.'
pattern1 = r'beegeek'
pattern2 = r'Python'

if m := re.search(pattern1, text):
    print(f'Match found: {m.group()}')
else:
    if m := re.search(pattern2, text):
        print(f'Match found: {m.group()}')
    else:
        print('No matches')

Example 7: Checking Conditions on a List

Determining if any number in a list is greater than 10 and if all numbers are less than 10:

numbers = [1, 4, 6, 2, 12, 4, 15]

print(any(number > 10 for number in numbers))
print(all(number < 10 for number in numbers))

With :=, you can capture the last checked value:

numbers = [1, 4, 6, 2, 12, 4, 15]

print(any((value := number) > 10 for number in numbers))
print(value)

print(all((value := number) < 10 for number in numbers))
print(value)

Caveats and Best Practices

While the Walrus Operator can make code more concise and efficient, it can also introduce pitfalls, such as incorrect precedence and unintended variable scope changes. For instance:

from math import factorial

fact = 0
data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
factorial_data = [fact for num in data if (fact := factorial(num)) <= 1000]
print(fact)

This code overwrites fact in the outer scope.

Another potential issue arises with empty iterables in functions like any() or all(), where the variable might not be created:

numbers = []

print(any((value := number) > 10 for number in numbers))
print(value)

This leads to a NameError.

Using := in conditional expressions also requires caution to ensure variables are properly defined:

for i in range(1, 101):
    if (two := i % 2 == 0) and (three := i % 3 == 0):
        print(f"{i} is divisible by 6.")
    elif two:
        print(f"{i} is divisible by 2.")
    elif three:
        print(f"{i} is divisible by 3.")

This will raise a NameError if (two := i % 2 == 0) is false, as three won't be defined.

Conclusion

The Walrus Operator can make your code more concise and efficient, but it should be used judiciously. Avoid overusing it, and ensure that

In case you have found a mistake in the text, please send a message to the author by selecting the mistake and pressing Ctrl-Enter.
Den W. 2K
I'm a passionate tech enthusiast who loves diving into the world of software, programming, and tech reviews.
Comments (2)
You must be logged in to comment.

Sign In