69

PEP 572 introduces assignment expressions (colloquially known as the Walrus Operator), implemented for Python 3.8. This seems like a really substantial new feature since it will allow this form of assignment within comprehensions and lambda functions.

What exactly are the syntax, semantics, and grammar specification of assignment expressions?

Why is this new (and seemingly quite radical concept) being introduced, when a similar idea in PEP 379 on "Adding an assignment expression" was rejected before?

MendelG
  • 4,464
  • 1
  • 5
  • 22
Chris_Rands
  • 30,797
  • 12
  • 66
  • 100
  • 1
    Are there organically-asked questions on this topic that can be closed with a link to this reference question? A question that might otherwise be approaching "too broad" can certainly be justifiable when it addresses what is otherwise a source of common duplicates. – Charles Duffy May 11 '18 at 17:53
  • 7
    This should be reopened. This is definitely not "too broad". It's a very specific subject and a very good reference question. – Panagiotis Kanavos Oct 05 '18 at 16:27
  • 1
    While it shouldn't be taken too literally because I'm sure Python may diverge in some ways, this is one of Go's best features and there are [examples](https://golang.org/ref/spec#Short_variable_declarations) throughout the Go docs – Brad Solomon Apr 07 '19 at 12:54
  • 7
    Just to give you a historical perspective: A long and heated discussion among Python developers preceded the approval of PEP 572. And it appears to be [one of the reasons](https://mail.python.org/pipermail/python-committers/2018-July/005664.html) why Guido resigned as BDFL. The assignment expressions have a number of valid usecases but can also be easily misused to make code less readable. [Try to limit use of the walrus operator to clean cases that reduce complexity and improve readability.](https://docs.python.org/3/whatsnew/3.8.html#assignment-expressions) – Jeyekomon Oct 16 '19 at 11:10

3 Answers3

59

PEP 572 contains many of the details, especially for the first question. I'll try to summarise/quote concisely arguably some of the most important parts of the PEP:

Rationale

Allowing this form of assignment within comprehensions, such as list comprehensions, and lambda functions where traditional assignments are forbidden. This can also facilitate interactive debugging without the need for code refactoring.

Recommended use-case examples

a) Getting conditional values

for example (in Python 3):

command = input("> ")
while command != "quit":
    print("You entered:", command)
    command = input("> ")

can become:

while (command := input("> ")) != "quit":
    print("You entered:", command)

Similarly, from the docs:

In this example, the assignment expression helps avoid calling len() twice:

if (n := len(a)) > 10:
    print(f"List is too long ({n} elements, expected <= 10)")

b) Simplifying list comprehensions

for example:

stuff = [(lambda y: [y,x/y])(f(x)) for x in range(5)]

can become:

stuff = [[y := f(x), x/y] for x in range(5)]

Syntax and semantics

In any context where arbitrary Python expressions can be used, a named expression can appear. This is of the form name := expr where expr is any valid Python expression, and name is an identifier.

The value of such a named expression is the same as the incorporated expression, with the additional side-effect that the target is assigned that value

Differences from regular assignment statements

In addition to being an expression rather than statement, there are several differences mentioned in the PEP: expression assignments go right-to-left, have different priority around commas, and do not support:

  • Multiple targets
x = y = z = 0  # Equivalent: (z := (y := (x := 0)))
  • Assignments not to a single name:
# No equivalent
a[i] = x
self.rest = []
  • Iterable packing/unpacking
# Equivalent needs extra parentheses

loc = x, y  # Use (loc := (x, y))
info = name, phone, *rest  # Use (info := (name, phone, *rest))

# No equivalent

px, py, pz = position
name, phone, email, *other_info = contact
  • Inline type annotations:
# Closest equivalent is "p: Optional[int]" as a separate declaration
p: Optional[int] = None
  • Augmented assignment is not supported:
total += tax  # Equivalent: (total := total + tax)
Chris_Rands
  • 30,797
  • 12
  • 66
  • 100
  • I'm testing this right now and it's not the only difference – Adelin Jul 19 '18 at 08:29
  • There's [a whole list of differences between `=` and `:=`](https://www.python.org/dev/peps/pep-0572/#differences-between-assignment-expressions-and-assignment-statements), out of which only one is listed here – Adelin Jul 19 '18 at 08:38
  • 1
    My point is, this could be a really good canonical q&a but it needs a serious polish – Adelin Jul 19 '18 at 08:39
  • @Adelin I wonder how you are testing it if alpha version will be released in 2019. –  Aug 30 '18 at 16:48
  • 1
    @JC - One can build from the [source](https://github.com/python/cpython) any time to test the bleeding-edge version of Python. This is how Python developers do it, but anybody who's curious could also do the same – Nathan May 21 '19 at 17:46
40

A couple of my favorite examples of where assignment expressions can make code more concise and easier to read:

if statement

Before:

match = pattern.match(line)
if match:
    return match.group(1)

After:

if match := pattern.match(line):
    return match.group(1)

Infinite while statement

Before:

while True:
    data = f.read(1024)
    if not data:
        break
    use(data)

After:

while data := f.read(1024):
    use(data)

There are other good examples in the PEP.

Jonathon Reinhart
  • 116,671
  • 27
  • 221
  • 298
  • 5
    Especially good examples: they show an aspect under which _C_ sometimes managed to be clearer and more elegant than Python, I thought they were never going to fix this – AquilaIrreale Aug 15 '19 at 19:21
7

A few more examples and rationales now that 3.8 has been officially released.

Naming the result of an expression is an important part of programming, allowing a descriptive name to be used in place of a longer expression, and permitting reuse. Currently, this feature is available only in statement form, making it unavailable in list comprehensions and other expression contexts.

Source: LicensedProfessional's reddit comment

Handle a matched regex

if (match := pattern.search(data)) is not None:
    # Do something with match

A loop that can't be trivially rewritten using 2-arg iter()

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

Reuse a value that's expensive to compute

[y := f(x), y**2, y**3]

Share a subexpression between a comprehension filter clause and its output

filtered_data = [y for x in data if (y := f(x)) is not None]
Charles Clayton
  • 13,212
  • 10
  • 73
  • 114