Assignment Expressions: The Walrus Operator

Christopher Bailey

In this lesson, you’ll learn about the biggest change in Python 3.8: the introduction of assignment expressions. Assignment expression are written with a new notation (:=) .This operator is often called the walrus operator as it resembles the eyes and tusks of a walrus on its side.

Assignment expressions allow you to assign and return a value in the same expression. For example, if you want to assign to a variable and print its value, then you typically do something like this:

>>> walrus = False >>> print(walrus) False 

In Python 3.8, you’re allowed to combine these two statements into one, using the walrus operator:

>>> print(walrus := True) True >>> type(walrus) 

The assignment expression allows you to assign True to walrus , and immediately print the value. But keep in mind that the walrus operator does not do anything that isn’t possible without it. It only makes certain constructs more convenient, and can sometimes communicate the intent of your code more clearly.

One pattern that shows some of the strengths of the walrus operator is while loops where you need to initialize and update a variable. For example, the following code asks the user for input until they type quit :

# write_something.py inputs = list() current = input("Write something: ") while current != "quit": inputs.append(current) current = input("Write something: ") 

This code is less than ideal. You’re repeating the input() statement, and somehow you need to add current to the list before asking the user for it. A better solution is to set up an infinite while loop, and use break to stop the loop:

# write_something.py inputs = list() while True: current = input("Write something: ") if current == "quit": break inputs.append(current) 

This code is equivalent to the code above, but avoids the repetition and somehow keeps the lines in a more logical order. If you use an assignment expression, then you can simplify this loop further:

# write_something.py inputs = list() while (current := input("Write something: ")) != "quit": inputs.append(current) 

This moves the test back to the while line, where it should be. However, there are now several things happening at that line, so it takes a bit more effort to read it properly. Use your best judgement about when the walrus operator helps make your code more readable.

PEP 572 describes all the details of assignment expressions, including some of the rationale for introducing them into the language, as well as several examples of how the walrus operator can be used. The Python 3.8 documentation also includes some good examples of assignment expressions.

Here are a few resources for more info on using bpython, the REPL (Read–Eval–Print Loop) tool used in most of these videos:

rajeshboyalla on Dec. 4, 2019

Why do you use list() to initialize a list rather than using [] ?

>>> timeit("i = list()", globals=globals()) 0.07558319200001051 >>> timeit("i = []", globals=globals()) 0.02262609000001703 

Avatar image for Geir Arne Hjelle

Geir Arne Hjelle RP Team on Dec. 4, 2019

My two cents about list() vs [] (I wrote the original article this video series is based on):

That said, if I’m initializing a list with existing elements, I usually use [elem1, elem2, . ] , since list(. ) has different–and sometimes surprising–semantics.

Jason on April 3, 2020

Sorry for my ignorance, did the the standard assignment = operator work in this way? I don’t understand what has been gained from adding the := operator. If anything I think it will allow people to write more obfuscated code. But minds better than mine have been working on this, so I’ll have to take their word it is an improvement.

Jason on April 3, 2020

As for the discussion on whether [] is more readable than list(). I’d never seen list() before, so to me [] is better. I’ve only just come over from the dark 2.7 side so maybe it’s an old python programmer thing?

Oh I checked the operation on the assignment operator. I was obviously wrong. lol Still I think the existing operator could’ve been tweaked to do the same thing as := … I’m still on the fence about that one.

gedece on April 3, 2020

you are right in that the existing operator could have worked, but it can lead to something unexpected.

if you do something like

if (newvar = somevar): it gives a traceback, because you are supposed to use == for comparations.

So if you use the normal operator for this, then that expression is valid and you’ll be hard pressed to realize the mistake.

It then makes complete sense to use a different operator as that helps to clarify intent in code.

Jason on April 6, 2020

Yes, I’ve accidentaly done that in other languages before and it can be a difficult to “see” bug.

varelaautumn on Sept. 26, 2020

I watched this earlier today and now tonight I just can’t stop myself from throwing these walrus operators everywhere.

(I’m new and learning so these are just personal fooling around programs)

For example I have this function which cycles through a bunch of other very simple parsing functions that check if my input string is valid in the context of the game state. If the string doesn’t pass one of these parsers it returns a string with an error message such as “Input must be less than 5 characters”. And then the parse_input function returns that error string.

I mean it’s not a huge change, but it saves an extra call of the function, and I feel like it makes it much more readable.

def parse_input(string: str, parsers: Tuple[Callable]) -> str or None: for parser in parsers: if parser(string): return parser(string) 
def parse_input(string: str, parsers: Tuple[Callable]) -> str or None: for parser in parsers: if error := parser(string): return error 

I’m not sure if this other case might be considered abuse of the walrus operator, but I decided to use it twice in one line.

This function repeatedly asks for input. If the input does not pass the parser functions, then the error will be returned and printed out in the while loop. Otherwise the input was valid and it gets returned.

def user_input(parsers: Tuple[Callable]) -> str: while error := parse_input(input_string := input('>>> '), parsers): print(f"Error: 'input_string>' is not a valid input. error>") return input_string ### Here's an example of how it'd look in action: # > "What is your name?" # > >>> a # > "Error: 'a' is not a valid input. Input must be at least 2 characters long." # > >>> 55334 # > "Error: '55334' is not a valid input. Input must be alphabetical." # > >>> Autumn # > Autumn 

I’m able to pass my input into a function and check the result of that function all while retaining my input and the return of the function as their own variables to be used in the next line.

I think the walrus operator helped me put all the relevant details on the three lines. Like if you just read the first words of each line, it basically says “while error, print error, else return input_string.” I don’t see how I could have done that without this cool walrus operator so I’m really appreciative for this video you made! I’ve been converted to a strong believer in the walrus operator.

Avatar image for Geir Arne Hjelle

Geir Arne Hjelle RP Team on Sept. 26, 2020

@varelaautumn Nice examples, thanks for sharing!

I agree that the walrus operator will not revolutionize your code, but it can bring these sorts of small improvements that add up in the long run.

Become a Member to join the conversation.