← All posts

Walrus operator in Python 3.8: a primer

The definitive tutorial for the all-new assignment expressions syntax in Python 3.8 with examples.

Header image
By Sanket on 
Share on Twitter Share on LinkedIn Share on Facebook

Assignment expressions (:=), or the “walrus” operator, have been the most talked about feature to be introduced in the latest version of Python. The new addition to the language was proposed in PEP 572. In this post, we look at the rationale behind assignment expressions, and understand how to use it with various examples.

Background

The primary rationale behind introducing assignment expressions, as stated in the PEP, is the observation that programmers want to save lines of code, and would even repeat a subexpression (and thereby slow down the program) in order to do so.

So instead of writing:

match = re.match(data)
group = match.group(1) if match else None

they write:

group = re.match(data).group(1) if re.match(data) else None

Assignment in expressions make it easier to write code constructs where a value calculated is re-used as part of some condition later, and help make the intent of the code’s author clearer. Languages like C and Go, among others, already support a functionality like this.

Where to use the Walrus operator

Let’s have a look at few examples on how to use walrus operator in Python:

Example 1: When a calculated value is used as part of a condition later

match = pattern.search(data)
if match is not None:
    do_something(match)

can be written as:

if (match := pattern.search(data)) is not None:
  do_something(match)

The above piece of code can be read as: if match, which becomes pattern.search of data, is not None, do_something of match.

Example 2: When reading a generator resource that exhausts

chunk = resource.read(8192)
while chunk:
    process(chunk)
    chunk = resource.read(8192)

can be written as much shorter:

while chunk := resource.read(8192):
  process(chunk)

Another example when reading all lines in a file:

line = f.readline()
while line:
    do_something(line)
    line = f.readline()

can be written succinctly as:

while line := f.readline():
    do_something(line)

Example 3: Reusing a value that’s expensive to compute

filtered_data = [
  f(x) for x in data
  if f(x) is not None
]

can be written as:

filtered_data = [
  y for x in data
  if (y := f(x)) is not None
]
This saves re-calculation of f(x) and can improve performance of the program, while still keeping the line count to the same.

The authors of the PEP have also rationalized the feature by showing real-world examples from the Python standard library where using the walrus operator would save lines and make the intent clearer.

Where not to use the Walrus operator

Assignment expressions are not allowed in certain cases to avoid ambiguities — such as when used at the top level of an expression statement without parentheses. This simplifies the choice for the user between an assignment statement (=) and an assignment expression (:=) — there is no syntactic position where both are valid.

y := f(x)  # INVALID
(y := f(x))  # Valid, though not recommended

y0 = y1 := f(x)  # INVALID
y0 = (y1 := f(x))  # Valid, though discouraged


foo(x = y := f(x))  # INVALID
foo(x=(y := f(x)))  # Valid, though probably confusing

Things to keep in mind

  • An assignment expression does not introduce a new scope, and is bound by the current scope. If the current scope contains a nonlocal or global declaration for the target, the assignment expression honors that.

  • There is one special case: an assignment expression occurring in a list, set or dict comprehension or in a generator expression binds the target in the containing scope, honoring a nonlocal or global declaration for the target in that scope, if one exists. So it’s possible to do something like this:

total = 0
partial_sums = [total := total + v for v in values]
print("Total:", total)

However, an assignment expression target name cannot be the same as a for-target name appearing in any comprehension containing the assignment expression.


What are your opinions on the Walrus operator? Do you think it is going to simplify writing code, or rather decrease the readability? Let us know @DeepSourceHQ!

About DeepSource
DeepSource helps you automatically find and fix issues in your code during code reviews, such as bug risks, anti-patterns, performance issues, and security flaws. It takes less than 5 minutes to set up with your Bitbucket, GitHub, or GitLab account. It works for Python, Go, Ruby, and JavaScript.
Get started for free

Keep reading...