On the morning of July 12, 2018, members of the Python community woke up, opened their laptops, and found a message on the python-committers mailing list that would change the trajectory of one of the world’s most popular programming languages. The subject line was brief and devastating: “Transfer of Power.”
The author was Guido van Rossum — the man who invented Python in 1989, who had led it for nearly three decades, who held the half-joking, half-serious title of “Benevolent Dictator for Life.” And he was done.
Not dead. Not retiring. Not moving on to start a blockchain company. He was quitting — because of a fight over two characters: :=
This is the story of how a tiny operator brought down a king, split a community, and somehow — years later — turned out to be pretty useful.
The Itch Nobody Could Scratch
Before Python 3.8, there was a pattern that every Python developer had bumped into and silently accepted, like a creak in the floorboard of an otherwise beautiful house. You’d write something like this:
# The classic read-loop pattern — works fine, just feels like déjà vu
line = input("Enter something: ")
while line != "quit":
print(f"You said: {line}")
line = input("Enter something: ") # wait, didn't I just write this?
See that line = input(...) appearing twice? Once to prime the loop, once at the bottom to keep it going. It’s not wrong. It works. But it has a certain smell to it — like copy-pasting your own code two lines apart and calling it architecture.
Or consider this common regex pattern:
import re
match = re.search(r'\d+', some_string)
if match:
print(match.group())
Two lines. One to compute the result, one to check it. The assignment and the condition live in separate postal codes when they really belong in the same neighborhood.
And then there’s the list comprehension problem — the one that really stung:
# You want to filter AND use the result of an expensive function
results = []
for x in data:
value = slow_transform(x)
if value > threshold:
results.append(value)
You could try to write this as a comprehension, but then you’d call slow_transform(x) twice:
# Elegant? Sure. Efficient? You're literally doing the work twice.
results = [slow_transform(x) for x in data if slow_transform(x) > threshold]
This wasn’t a screaming emergency. Nobody’s production server was going down because of it. But it was one of those things that nagged at you — especially if you’d spent any time in C, Go, or Ruby, where assignment-as-expression was just… normal.
Python, by design, had always drawn a hard line: assignment is a statement, not an expression. You cannot do if x = 5: in Python. This was intentional. Guido van Rossum had seen the if (x = 5) bug in C — one of the most infamous sources of subtle errors in programming history — and said “not in my language.”
And he was right. For thirty years, he was right.
But being right and being complete are two different things.
Enter PEP 572: The Proposal That Lit the Fuse
In February 2018, Chris Angelico — a prolific contributor on the python-ideas mailing list — formally submitted PEP 572, titled “Assignment Expressions.” The proposal was later co-authored with Tim Peters and Guido van Rossum himself. (Christoph Groth also made significant contributions to the proposal’s direction during discussion.)
The core idea was simple: introduce a new operator, :=, that allows you to assign a value to a variable within an expression. Not replacing =, but supplementing it. A different tool for a different job.
The syntax looked like this:
if (n := len(my_list)) > 10:
print(f"List is too long ({n} elements)")
The := became informally known as “the walrus operator” — because if you tilt your head and squint, the colon looks like the eyes and the equals sign looks like the tusks of a walrus. := → a sideways walrus face. (Programmers are nothing if not imaginative namers.)
The rationale was practical. The PEP presented clear cases where assignment expressions would eliminate redundancy, reduce boilerplate, and make certain patterns more idiomatic. Tim Peters, legendary Pythonista and the author of the Zen of Python itself, contributed an essay to the PEP advocating for the feature.
On paper, it seemed reasonable. A targeted addition. A sharp tool for specific situations.
What happened next was anything but reasonable.
The Great War on the Mailing List
The python-dev and python-ideas mailing lists — where Python’s future has been debated for decades — erupted. The discussion around PEP 572 spanned multiple enormous threads across two mailing lists, spawned separate polls (neither of which favored the feature), and seemed, at times, like it would never end.
The arguments against := were philosophically grounded and genuinely thoughtful:
“It violates the Zen of Python.” Specifically, “There should be one — and preferably only one — obvious way to do it.” Now there would be two ways to assign: = and :=. The distinction between them? Subtle. The potential for confusion? High.
“Explicit is better than implicit.” Assignment expressions do two things at once — they assign and return a value. That’s implicit behavior hiding inside what looks like a simple operation. Python had always been a language that wore its intentions on its sleeve.
“It makes Python look like C.” This was the emotional core of the opposition. Python’s beauty was its readability. Its lack of C-style footguns was a feature, not a limitation. Adding := felt like the beginning of a slippery slope toward if (x := y) and (z := w) or (v := u) — the kind of “clever” code that makes Perl famous and maintenance engineers miserable.
But the pragmatists had their own case:
“The redundancy is real.” The double-call-in-comprehensions problem wasn’t theoretical. People hit it constantly. The workarounds were uglier than the disease.
“Other mature languages handle this fine.” Go uses := for short variable declarations. Ruby allows assignment in conditions. The sky hadn’t fallen.
“Python is a practical language, not a theology.” The Zen of Python is a set of guidelines, not commandments. Tim Peters — the man who wrote the Zen — was co-authoring this PEP. If the Zen’s own author thought := was acceptable, maybe the Zen wasn’t quite as black-and-white as people were treating it.
The debate raged for months. The same arguments surfaced over and over. People who hadn’t read the PEP showed up to loudly proclaim their opposition. People who had read it disagreed about what it actually said. It was, by all accounts, one of the most contentious discussions in Python’s history.
And then Guido accepted the PEP.
“I’m Tired, and I Need a Very Long Break”
On July 12, 2018, six days after accepting PEP 572, Guido van Rossum posted to the python-committers mailing list. The message was short, direct, and tinged with exhaustion.
“Now that PEP 572 is done, I don’t ever want to have to fight so hard for a PEP and find that so many people despise my decisions. I would like to remove myself entirely from the decision process.”
He went on:
“I’ll still be there for a while as an ordinary core dev, and I’ll still be available to mentor people — possibly more available. But I’m basically giving myself a permanent vacation from being BDFL, and you all will be on your own.”
And then, with the kind of understated gravity that only Guido could pull off:
“I’m tired, and need a very long break.”
He didn’t appoint a successor. He didn’t lay out a transition plan. He left a question hanging like an open brace with no closing match: “So what are you all going to do? Create a democracy? Anarchy? A dictatorship? A federation?”
In a later interview, he was more specific about what pushed him over the edge. It wasn’t just the technical debate — it was the personal attacks that followed his acceptance of the PEP. People took to Twitter and social media to say things that, in his words, “really hurt me personally.” And some of those people were core Python developers. The people he had trusted, mentored, and collaborated with for years.
That’s the part that matters beyond syntax debates. A man gave three decades of his life to a language used by millions, and the reward for making a decision some people didn’t like was public cruelty from the community he’d built. If that doesn’t make you think about how open source treats its maintainers, nothing will.
So What Does This := Thing Actually Do?
Alright. The drama is real and important, but you’re also here because you write Python and you want to know: is this operator actually good?
Let’s walk through it, starting from “mildly useful” and working up to “okay, I get it now.”
The While Loop Cleanup
Before:
# Prime the loop, then repeat yourself at the bottom
chunk = file.read(8192)
while chunk:
process(chunk)
chunk = file.read(8192) # the dreaded duplicate line
After:
# One line. One read. No repetition.
while chunk := file.read(8192):
process(chunk)
This is the walrus operator’s bread and butter. The “loop-and-a-half” pattern — where you need to do something before you can test the loop condition — becomes a one-liner. The assignment and the truthiness check happen in the same breath.
The Regex Pattern
Before:
import re
match = re.search(r'(\d{4})-(\d{2})-(\d{2})', log_entry)
if match:
year, month, day = match.groups()
print(f"Found date: {year}-{month}-{day}")
After:
import re
if match := re.search(r'(\d{4})-(\d{2})-(\d{2})', log_entry):
year, month, day = match.groups()
print(f"Found date: {year}-{month}-{day}")
One fewer line, and the intent is clearer: “search for this pattern, and if you find it, do something with the match.” The variable’s scope and purpose are obvious at a glance.
The Comprehension Problem — Solved
This is where := genuinely shines. Remember the expensive-function-in-a-comprehension problem?
Before (inefficient):
# Calling slow_transform TWICE per element — once to check, once to keep
results = [slow_transform(x) for x in data if slow_transform(x) is not None]
Before (efficient but verbose):
results = []
for x in data:
value = slow_transform(x)
if value is not None:
results.append(value)
After:
# Compute once, test once, keep the result. That's it.
results = [y for x in data if (y := slow_transform(x)) is not None]
One line. One call per element. No wasted computation. This is arguably the strongest use case for the walrus operator — it unlocks a pattern that was genuinely impossible to express cleanly in a comprehension before Python 3.8.
The Subprocess / IO Pattern
Before:
command = input("$ ")
while command != "exit":
subprocess.run(command, shell=True)
command = input("$ ")
After:
while (command := input("$ ")) != "exit":
subprocess.run(command, shell=True)
Clean, tight, and the intent is crystal clear.
The Genuinely Surprising One
Here’s a pattern that might make you do a double-take:
# Accumulate a running total, but only keep values that push it over a threshold
running = 0
filtered = [
running
for x in measurements
if (running := running + x) > min_threshold
]
Wait — := in a comprehension binds in the containing scope. That means running leaks out of the comprehension and persists. This is by design (and documented), but it’s the kind of thing that can surprise you if you’re not paying attention. Which brings us to…
When NOT to Use the Walrus
The PEP itself offers this guidance: “Try to limit use of the walrus operator to clean cases that reduce complexity and improve readability.”
Here’s what abuse looks like:
# Please, for the love of Guido, don't do this
if (a := f(x)) and (b := g(a)) and (c := h(b)):
do_something(a, b, c)
This is “clever.” Clever code is code that the author will struggle to understand in six months. If you’re chaining walrus operators like some kind of assignment ninja, you’ve crossed the line from “elegant” to “showing off.”
Rules of thumb:
- If you can just use a regular assignment on the previous line and the code reads fine, do that
- Don’t use
:=in top-level expression statements — it’s syntactically forbidden anyway - Don’t nest walrus operators inside other walrus operators
- If your reviewer has to tilt their head to understand the line, simplify it
The Aftermath: Democracy, Not Anarchy
Guido’s departure left Python without a governance model for the first time in its history. What followed was genuinely impressive: the core developer community proposed, debated, and voted on no fewer than seven different governance PEPs (PEP 8010 through PEP 8016).
In December 2018, PEP 8016 — “The Steering Council Model” — won. Authored by Nathaniel J. Smith and Donald Stufft, it established a five-person steering council elected by core developers. The design philosophy was explicit: “Be boring. We’re not experts in governance, and we don’t think Python is a good place to experiment with new and untried governance models.”
The first steering council was elected in January 2019. Among the five members? Guido van Rossum himself — now serving as one voice among five rather than the sole voice of the language. He remained on the council through 2019 before withdrawing from nominations for the 2020 election.
The walrus operator shipped in Python 3.8, released in October 2019. The implementation was contributed by Emily Morehouse, a Python core developer. Over time, the community’s temperature cooled. CPython’s own source code began using := in several places. Linting tools added support. Style guides incorporated guidance.
Did the community make peace with it? Mostly, yes. The operator found its niche — it didn’t replace =, it didn’t make Python look like C, and the predicted readability apocalypse never materialized. As Jake Edge of LWN.net observed, it’s “actually a fairly small change for all of the uproar it caused.”
The steering council model, meanwhile, has proven remarkably stable. A new council is elected after each feature release, and the system has guided Python through multiple major releases without another BDFL-level crisis. It turns out that distributing authority across five people, with clear processes and term limits, is more sustainable than concentrating it in one person — no matter how brilliant that person is.
The Weight of Two Characters
Here’s what I keep coming back to. The walrus operator is, technically, a minor syntactic convenience. It saves you a line here, a redundant call there. It’s nice. It’s useful. It’s not revolutionary.
But the story around it? That’s about something much larger.
It’s about what happens when a community outgrows its founder. When the person who gave you the language — who shaped its philosophy, defended its readability, and made the hard calls for thirty years — makes a decision you disagree with, and you respond not with respectful dissent but with public contempt.
It’s about the BDFL model itself: beautiful when the dictator is benevolent and correct, fragile the moment the community decides they know better. Python thrived under Guido’s taste for decades. But taste is personal, and communities are not.
It’s about how open source governance is still, in many ways, an unsolved problem. The steering council works — for now. But every open source project of sufficient size will eventually face its own PEP 572 moment: a decision that exposes the gap between “how we think we make decisions” and “how we actually make decisions when we disagree.”
The Zen of Python says, “There should be one — and preferably only one — obvious way to do it.” But for governance? There isn’t one obvious way. There isn’t even one good way. There’s only the way that breaks the fewest things.
Python lost its king over a walrus. And perhaps, in the long run, that’s exactly how it was supposed to happen — because the best languages, like the best communities, eventually learn to govern themselves.
Sources: