Is the walrus Operator antipattern?

I recently discovered the walrus operator (:=) and have mixed feelings about it.
It feels like an odd exception to Python’s usual emphasis on readability.

What puzzled me:

  • Python famously rejects ++/-- operators for being “unpythonic,” yet := exists.
  • Most cases I’ve seen (e.g., while (line := file.readline())) could just use multiple lines.

Am I missing something?

  • Are there legitimate cases where := is the cleanest solution?
  • Or is it a niche feature that encourages bad habits?

(Source: Walrus Operator)

Please try to turn this exact example into multiple lines without repeating file.readline(). Yes, it’s possible, but it doesn’t read as well.

Any kind of syntax sugar can be dismissed as “just use more lines instead”, but that is missing the point.

You can search back on the mailing lists from around the time this operator was added. You will find dozens of people making the exact same arguments you are making and many people making counter arguments.

1 Like

It’s convenient when using the re module:

if m := re.match(first_pattern, text):
    ...
elif m := re.match(second_pattern, text):
    ...
else:
    ...

That’s nicer than:

m = re.match(first_pattern, text)
if m:
    ...
else:
    m = re.match(second_pattern, text)
    if m:
        ...
    else:
        ...

.

Or it isn’t :wink:. Seriously, in some contexts it’s more readable. @MegaIng and @MRAB gave examples. Like:

while buf := some_binary_file.read(BUFSIZE):
    # deal with `buf`

they’re of the very simple form while result := function(). There’s really no gain in clarity (the contrary) by splitting that across multiple lines and typing buf out an additional time.

So stick to that. At the time (about 5 years ago), Guido (the language’s designer) and I were the strongest advocates for adding the walrus, and repeatedly stressed that it was intended to be used only when it actually increased clarity. I went through many thousands of lines of code “by eyeball” at the time, and only found a few dozen cases where it would be a clear improvement. But they were indeed clear (if small) improvements.

So it’s not often! If, e.g., you need to wrap an assignment expression in parentheses, you’re probably pushing the intent.

It’s intended to be a simple feature for use in simple contexts. That’s why, e.g., the binding target is restricted to be a simple identifier (no, e.g. , subscripted or attribute targets, or magical unpacking of an iterable result into multiple targets). An assignment statement can be far more complex than an assignment expression.

Python could certainly have lived without it. But in the relatively few cases where it’s “just right”, it’s very “Pythonic” to my (& Guido’s) eyes: efficiently and clearly communicating intent with a minimum of syntactic noise and/or repetition.

Of course people will, at times, use it in unintended ways. There’s nothing to stop them from, e.g., using 117 blanks per indentation level either - Python never aimed to make it impossible to write ugly code;

5 Likes

I like to use it sometimes when building data structures with reusable elements

{'thing': thing:=do_something(), 'other_thing': OtherThing(thing=thing)}

Shoot me.

1 Like

I use it mainly in the preprocessing steps of my functions.
Before the walrus, I was struggling to choose among many possible ways doing that kind of preprocessing :

def process_mix(data0, data1):
    assert data0.parameter == data1.parameter  # safety check
    parameter = data0.parameter  # parameter extraction
    ...

Now there is an all-at once way

def process_mix(data0, data1):
    assert (parameter := data0.parameter) == data1.parameter
    ...

Just a detail, though.

Don’t do this. Assertions should NEVER have functionality other than the actual assertion.

3 Likes

Though - on second thoughts, it’s not the walrus that’s the problem here, it’s the assertion. This should most likely be a simple if/raise, not an assert.

1 Like

This is a poor choice of example because the alternative without the Walrus operator in this case is awkward and also for line in file is better anyway.

Maybe your experience of the Walrus operator is somewhat like mine in that it appeared and then I saw other people using it enthusiastically but usually in situations where it isn’t needed. In a quick review of one codebase (git grep -C3 :=) I find that about 50% of the time when the Walrus operator is used it could just be an assignment on the line above e.g.:

if x := func():
    do_thing(x)

I don’t mind it if the name being assigned to appears right after if but I also see more complex examples where the assignment can be anywhere:

if func(a, b, x := g()):
   do_thing(x)

There are some while loops but the situations where the alternative without the Walrus would be awkward are almost all if/elif cases like @MRAB’s example:

if one_thing():
    #
elif x := func():
    do_thing(x)
elif ...
1 Like

I know, but these are meant to avoid silent bugs due to inconsistently formatted data in personal codes. It is not meant for production and I use it when I don’t want to spend time and lines on exception handling.

You do realise that entire line will be skipped if someone runs with python -O?

1 Like

I’m not faulting you if you haven’t done so yet, but you should read the PEP which introduced the idea, because it goes into great detail discussing the use cases for which such an operator was desired.

I will note that things like ++ are often used to modify an index during iteration, and Python already has better abstractions for iteration that don’t involve explicit indexing.

3 Likes

Using assert in this way is a bad habit that should be avoided all the time. Just because you happen to not be using -O right now doesn’t mean that it’s safe to abuse assertions in this way; you never know what you’ll do in the future, and the assert will cause yourself unnecessary pain.

1 Like