Every Python installation on the planet ships with a hidden manifesto. Type import this into any Python interpreter and nineteen lines appear — the guiding philosophy of an entire programming language, compressed into a format that reads like a set of proverbs. This is PEP 20, known as the Zen of Python, and it is far more useful than it first appears. It has shaped language design debates, influenced how features like f-strings and structural pattern matching were evaluated, and according to peer-reviewed research, it measurably changes the way developers write and reason about code.
Origins: The Mailing List, the Joke, and the Easter Egg
The Zen of Python did not start as an official document. It started as a joke on a mailing list. On June 4, 1999, Tim Peters posted nineteen aphorisms to the python-list mailing list in a thread called "The Python Way." The community had been debating proposed changes to the language, and Peters — a pre-1.0 CPython contributor, the inventor of the Timsort sorting algorithm, and the author of the doctest and timeit standard library modules — was responding to frustration that newcomers from other languages kept trying to reshape Python in ways that clashed with its design intent. Patrick Phalen had suggested that someone write a short document capturing Python's design aesthetic, something like a concise style manual for the language's philosophy. Peters responded with twenty theses, only nineteen of which he wrote down, and signed them off with a playful bit of self-censorship referencing old terminal editors where ^H represented a backspace character. (Source: python-list mailing list archive, June 1999; Real Python, realpython.com)
The aphorisms were Peters' attempt to distill Guido van Rossum's design instincts into plain English. Van Rossum — Python's creator, known in the community as the BDFL (Benevolent Dictator For Life) — had built an entire language around intuitions that were difficult to articulate. Peters, who had worked at Kendall Square Research on supercomputers and at Dragon Systems writing speech-to-text software, had a unique ability to translate deep technical intuition into clear language. Van Rossum later called Peters a mentor, describing him as someone who combines incredible technical skills with insight into what others are missing. (Source: Python Software Foundation Distinguished Service Award, pyfound.blogspot.com)
Five years later, in August 2004, Peters formalized the aphorisms as PEP 20 (Python Enhancement Proposal 20). The number itself may be an intentional wink — twenty, to match the claimed count of aphorisms, though only nineteen are documented. The text was released into the public domain, which means anyone can reproduce, adapt, or reference it freely. (Source: peps.python.org/pep-0020)
But the Zen had already become an Easter egg before the PEP existed. In the fall of 2001, Barry Warsaw — another core developer and creator of GNU Mailman — was helping organize the International Python Conference 10 (IPC 10). The organizers wanted a motto for the conference t-shirt and solicited submissions from the community. According to Warsaw's account, the team received roughly 500 submissions. Peters did the initial cut, then he and Warsaw traded the remaining list back and forth, each cutting it in half, until two finalists remained. One of them was "import this." Warsaw immediately realized that the phrase had to be implemented literally. He proposed sneaking a this.py module into CPython that would print the Zen when imported. Peters or Van Rossum suggested encoding the text in ROT13 — a simple letter-substitution cipher — for extra obfuscation. They committed it to what became Python 2.2.1 with check-in notifications turned off, told nobody outside their small group, and according to Warsaw it took a long time for anyone to discover the Easter egg. (Source: Barry Warsaw, wefearchange.org)
Open any Python interpreter and run import this. All nineteen aphorisms will print to your terminal. It takes about thirty seconds to read. This works in every standard Python installation — no packages to install, no configuration needed.
Beauty, Simplicity, and Readability
The first several aphorisms form a coherent argument for writing code that humans can follow — not just machines.
Beautiful is better than ugly.
This is not about aesthetics for its own sake. It is a reminder that code is read far more often than it is written. A developer six months from now — possibly you — will thank you for choosing clarity over cleverness. Ugly code is usually a symptom of a deeper problem: unclear intent, muddled logic, or rushed thinking. When your code looks wrong, it often is. Van Rossum has spoken about how Python deliberately minimizes punctuation, keeping it in line with common written English and high-school algebra. The visual cleanliness of Python is not an accident; it is a design decision that flows directly from this principle.
Explicit is better than implicit.
Python can do a lot of things quietly behind the scenes. That does not mean you should let it. If a function modifies a list in place, say so — in the function name, in a docstring, in a comment if necessary. If a variable holds a specific type, name it accordingly. Implicit behavior creates bugs that are notoriously difficult to trace because the code does something you did not see it doing. This is why Python requires self as an explicit parameter in method definitions rather than using an implicit keyword like this in other languages. It is also the philosophical foundation for type hints (PEP 484), which let you make variable types visible without the language enforcing them.
# Implicit: what does this function return? What does it change?
def process(data):
data.sort()
return data[:10]
# Explicit: the name, type hints, and docstring tell the full story
def get_top_ten_sorted(data: list[float]) -> list[float]:
"""Return the ten smallest values from data in ascending order.
Note: sorts the input list in place.
"""
data.sort()
return data[:10]
Simple is better than complex.
Resist the temptation to engineer a solution before you understand the problem. A straightforward loop that everyone on your team can read immediately is worth more than an elegant one-liner that requires three re-reads and a Stack Overflow visit. Reach for complexity only when simplicity genuinely cannot do the job.
Complex is better than complicated.
This pairs with the previous aphorism in a way that trips people up. Complex means having many parts that work together coherently — think of a well-designed class hierarchy or a pipeline with clearly separated stages. Complicated means tangled, hard to follow, difficult to reason about — think of a single function with twelve nested conditionals and four early returns. Complex systems can be understood one piece at a time; complicated ones resist understanding at every level. The distinction matters when you are deciding whether to refactor: the goal is not always fewer moving parts. Sometimes the goal is parts that move in predictable ways.
Flat is better than nested.
Deeply nested code — callbacks inside callbacks, loops inside loops, conditionals stacked three layers deep — makes it very hard to track state and very easy to introduce bugs. If you find yourself indenting past three or four levels, that is almost always a signal to refactor. Pull logic into named functions. Flatten your data structures. Use guard clauses (early returns) to eliminate unnecessary nesting. Use itertools or list comprehensions to collapse loops. Your future self will notice the difference.
# Deeply nested - hard to follow
def process_orders(orders):
results = []
for order in orders:
if order.is_valid():
if order.has_inventory():
if order.payment_cleared():
results.append(fulfill(order))
return results
# Flat with guard clauses - same logic, easier to read
def process_orders(orders):
results = []
for order in orders:
if not order.is_valid():
continue
if not order.has_inventory():
continue
if not order.payment_cleared():
continue
results.append(fulfill(order))
return results
# Even flatter with a comprehension
def process_orders(orders):
ready = [o for o in orders
if o.is_valid() and o.has_inventory() and o.payment_cleared()]
return [fulfill(o) for o in ready]
Sparse is better than dense.
Whitespace is not wasted space. Blank lines between logical sections of code help a reader's eye and mind process structure. A function that tries to do twelve things in forty lines with no breathing room is a liability. Breaking it apart with meaningful spacing — or better yet, meaningful functions — turns a wall of text into something navigable. Consider the difference between a paragraph of prose with no line breaks and a well-organized document with sections. Code works the same way.
Readability counts.
This is the one that ties all the others together. Python was designed for humans, not just machines. Code that runs correctly but is impossible to read creates a maintenance burden that compounds over time. Variable names matter. Function names matter. Comment quality matters. If someone else cannot understand your code without asking you, readability has failed. Van Rossum has described Python's goal as code that reads almost like English — and this aphorism is the reason. (Source: docs.python.org)
When you are choosing between two implementations that both work, read each one aloud. The one that sounds more like a clear description of what it does is almost always the better choice. If you cannot explain what a function does in one sentence without using the word "and," it is probably doing too many things.
Errors, Ambiguity, and Consistency
The middle section of PEP 20 deals with the tensions every developer encounters: when to follow the rules, when to break them, and what to do when things go wrong.
Special cases aren't special enough to break the rules.
This is a warning against the habit of carving out exceptions every time a rule feels inconvenient. Consistency in a codebase is enormously valuable. When every module follows the same patterns, new contributors can onboard faster, bugs are easier to spot, and refactoring is less dangerous. Fight the urge to write a special-case handler "just this once." That single exception tends to attract more exceptions over time until the rule no longer functions as a rule.
Although practicality beats purity.
This is the counterweight to the previous aphorism — and it is essential. Python is a pragmatic language. If a technically impure solution genuinely makes life easier without causing harm down the line, take it. Real software exists in the real world, with deadlines and constraints. The Zen of Python is not dogma; it is guidance. As Van Rossum has said, you can use the Zen to motivate a design choice, but it cannot be the sole justification — you still need to think carefully. (Source: Python Software Foundation, pyfound.blogspot.com)
Errors should never pass silently.
Swallowing exceptions is one of the most dangerous habits in software development. A bare except: pass block might keep your program running, but it also means problems are disappearing without a trace. When something goes wrong, you need to know about it. Log it, raise it, handle it deliberately — but never just make it disappear. This principle extends beyond exception handling. It also applies to return values that silently indicate failure (returning None when a function should raise an error), to configuration files that ignore unknown keys, and to APIs that accept invalid input without complaint.
A naked except: pass is one of the hardest bugs to diagnose. It silences everything — including errors you did not anticipate and genuinely need to know about. Always catch a specific exception type and document why you are suppressing it.
# Dangerous: silences ALL errors, including ones you need to know about
try:
result = fetch_data()
except:
pass
# Better: catches a specific error with a deliberate fallback
try:
result = fetch_data()
except TimeoutError:
# Service is temporarily unavailable; use cached result instead
result = get_cached_data()
# Best: logs the failure even when falling back
try:
result = fetch_data()
except TimeoutError:
logger.warning("fetch_data timed out, falling back to cache")
result = get_cached_data()
except ConnectionError as e:
logger.error("Connection failed: %s", e)
raise ServiceUnavailableError("Data source unreachable") from e
Unless explicitly silenced.
There are legitimate cases where you want to suppress an error: file cleanup code, optional feature detection, graceful degradation in a user-facing application. The difference is intent. Use except SomeSpecificError: pass with a comment explaining why, not a naked exception handler that catches everything and says nothing. The word "explicitly" is doing heavy lifting here. An explicit silencing is a documented decision. An implicit silencing is a time bomb.
# Explicit silencing with clear intent
import shutil
def safe_cleanup(path: str) -> None:
"""Remove a temporary directory if it exists. Errors are non-fatal."""
try:
shutil.rmtree(path)
except FileNotFoundError:
pass # Already cleaned up; nothing to do
In the face of ambiguity, refuse the temptation to guess.
When you do not know what something should do in an edge case, do not pick a behavior at random and hope for the best. Raise an exception. Write a test that documents the expected behavior. Ask someone. Guessing creates hidden assumptions that quietly corrupt your logic in situations you did not anticipate. This principle directly influenced Python's refusal to auto-convert between incompatible types. In many other languages, adding a string and an integer silently produces a result. In Python, it raises a TypeError — because guessing what the developer intended would be worse than stopping and asking.
There should be one — and preferably only one — obvious way to do it.
Python deliberately avoids offering a dozen ways to accomplish the same task. This is in direct contrast to languages like Perl, whose community motto TIMTOWTDI ("There's More Than One Way To Do It," pronounced "Tim Toady") celebrates offering developers multiple approaches to the same problem. During the 1990s and early 2000s, Perl and Python had a friendly rivalry, and this aphorism was a deliberate philosophical counterpoint. When there is one obvious way, teams write consistent code without needing to argue about style. It also makes reading other people's Python more predictable — if you know the idiom, you can follow the logic. (Source: Real Python, realpython.com)
Peters embedded a joke in the formatting of this line that is easy to miss. In the original text, the em dashes around "and preferably only one" use an unusual spacing: one-- and preferably only one --obvious. As Peters later explained, there is no consensus on whether spaces should appear around em dashes. By writing a line about having only one way to do something, he deliberately used a typographic device for which multiple conventions exist — and then chose a third style to drive the point home.
Although that way may not be obvious at first unless you're Dutch.
This is the joke aphorism — Guido van Rossum is Dutch — but there is a real point underneath the humor. Knowing the "Pythonic" way to do something takes time and practice. You learn it by reading other people's code, studying the standard library, and accumulating experience. The obvious way is obvious to those who know the language well. For everyone else, it becomes obvious through learning. That is not a flaw; it is an honest acknowledgment that fluency requires investment.
Timing, Explanation, and Namespaces
The final cluster of aphorisms addresses shipping, architecture, and the structural features that make Python's module system worth using.
Now is better than never.
Shipped software beats perfect software that never ships. This applies to refactoring and bug fixes too. Making a meaningful improvement today is better than waiting until you have time for the comprehensive rewrite you will never actually do. Perfectionism is often procrastination with better posture.
Although never is often better than right now.
The mirror aphorism. Moving fast without thinking causes different but equally real problems. A hasty API design you release today might haunt you for years. A quick fix deployed to production at 4pm on a Friday deserves extra scrutiny. The tension between these two aphorisms captures real engineering judgment — sometimes you move, sometimes you wait. Notice that the original text emphasizes "right" (rendered as *right* in the plain-text convention for emphasis), drawing attention to the distinction between "now" and "right now." The problem is not urgency itself; it is recklessness disguised as urgency.
If the implementation is hard to explain, it's a bad idea.
This is a filter for architectural decisions. If you cannot describe how your solution works to another developer in plain terms, that is a signal worth heeding. Either the design is too complicated, or you do not understand it well enough yet. Good ideas, properly understood, can be explained simply. The core Python developers apply this test during PEP debates. Core developer Carol Willing has summarized the Zen's overall message as being about meeting constraints in ways that prioritize common sense, maintainability, and comprehensibility. (Source: Python Software Foundation, pyfound.blogspot.com)
If the implementation is easy to explain, it may be a good idea.
"May be" is doing important work here. Simplicity of explanation is a necessary condition for a good implementation, not a sufficient one. Plenty of bad ideas are easy to explain. Use this as a sanity check, not a guarantee. The asymmetry between this and the previous aphorism is deliberate: difficulty of explanation is a strong negative signal, but ease of explanation is only a weak positive signal.
Namespaces are one honking great idea — let's do more of those!
This is the most enthusiastic line in a document that is otherwise measured in tone. Python's module system, class namespaces, and package structure all exist to prevent the naming collisions and polluted global scopes that plague other languages. Use them. Keep your imports clean, your module interfaces deliberate, and your global namespace as empty as possible. Every time you write from some_module import *, you are working against this principle — because you can no longer tell where any given name came from.
# Polluted global namespace - avoid
from os.path import *
from json import *
# Now if you call basename() or dumps(), a reader has no idea
# which module they came from without checking every import
# Clean and deliberate - prefer
import os
import json
# Now it's always clear where basename() and dumps() come from
path = os.path.basename(filepath)
output = json.dumps(data, indent=2)
The Missing Twentieth Aphorism
Peters' original post declared twenty aphorisms, only nineteen of which he wrote down. The twentieth was explicitly left for Van Rossum to fill in — and Van Rossum never did. Van Rossum has reportedly described the missing entry as a Tim Peters inside joke. (Source: DataCamp, datacamp.com)
The vacancy has taken on its own meaning over time. Some interpret it as a meta-joke: a document about how to think about Python leaves room for you to form your own conclusion, which is itself very Pythonic. Others see it as a deliberate demonstration of the "sparse is better than dense" principle — saying less when more is unnecessary. And some consider it an invitation: after absorbing the other nineteen, you should be equipped to derive the twentieth from your own experience. It is a fitting end for a document that values readability and good judgment — it leaves something to the human.
The Irony Inside the Module
There is a layer of irony in the Zen of Python that is easy to miss if you only ever run import this and read the output. The source code of the this.py module — the very module that delivers these principles about readability and explicitness — deliberately violates them.
The text of the Zen is not stored as readable English in the module's source. It is stored as a ROT13-encoded string — a simple Caesar cipher that shifts each letter thirteen positions through the alphabet. The module builds a decoding dictionary and deciphers the string at import time. The code uses single-letter variable names, no comments, and no docstrings. It is dense, implicit, and not particularly beautiful. As one Python community member put it: even the Zen of Python does something not beautiful, not explicit, and not simple. (Source: github.com/OrkoHunter/python-easter-eggs)
# Simplified version of what this.py actually does internally
s = "Gur Mra bs Clguba, ol Gvz Crgref..." # ROT13-encoded Zen text
d = {}
for c in (65, 97): # 65 = 'A', 97 = 'a'
for i in range(26):
d[chr(i + c)] = chr((i + 13) % 26 + c)
print(''.join([d.get(c, c) for c in s]))
This self-contradiction is intentional, and it teaches one of the Zen's own lessons: the aphorisms are guidance, not law. Even the module that preaches readability chose obfuscation for a good reason — to make the Easter egg more surprising when someone finally found it. Sometimes practicality (or in this case, playfulness) really does beat purity. It is also a test of sorts: if you take the Zen so literally that you cannot appreciate the joke in its own delivery mechanism, you may be missing the point of "although practicality beats purity."
How the Zen Connects to PEP 8
A common point of confusion for Python learners is the relationship between PEP 20 (the Zen) and PEP 8 (the style guide). They are not the same thing, but they are not independent either. PEP 20 is the abstract philosophy. PEP 8 is the concrete implementation of that philosophy as specific formatting rules.
When PEP 8 says to limit lines to 79 characters, that is a practical expression of "readability counts" and "sparse is better than dense." When PEP 8 prescribes naming conventions — snake_case for functions, PascalCase for classes — that serves "explicit is better than implicit," because the naming style signals what kind of thing the identifier represents. When PEP 8 warns against wildcard imports, that is "namespaces are one honking great idea" put into practice.
Understanding the Zen makes the PEP 8 rules feel less arbitrary. They are not just conventions someone chose at random. They are the downstream consequences of a coherent design philosophy. If you find yourself arguing about a style question that PEP 8 does not explicitly cover, the Zen often points you toward the answer.
Tools like black (the code formatter), ruff (the linter), and mypy (the type checker) are all practical applications of the Zen's principles. black embodies "one obvious way" by removing style debates entirely. ruff catches violations of "errors should never pass silently." mypy enforces "explicit is better than implicit" at the type level.
The Zen in Modern Python Design Decisions
The Zen of Python is not a historical artifact. It is actively used in PEP debates to evaluate whether a proposed feature belongs in the language. Several major additions to Python 3 were directly shaped by these principles.
F-strings (PEP 498, Python 3.6) became the preferred way to interpolate strings because they satisfy multiple aphorisms at once: the variable name appears inline (explicit), the syntax is clean (beautiful), and they replaced a situation where Python had three competing approaches — % formatting, str.format(), and string.Template — with one obvious way to do it.
# Three ways existed before f-strings
name, version = "Python", 3.12
msg = "Welcome to %s %.2f" % (name, version) # % formatting
msg = "Welcome to {} {:.2f}".format(name, version) # str.format()
msg = f"Welcome to {name} {version:.2f}" # f-string: the obvious way
The walrus operator (PEP 572, Python 3.8) was one of the most contentious additions in Python's history, and the debate explicitly invoked the Zen. Opponents argued it was implicit and dense; proponents argued it reduced nesting and eliminated repeated computation. The proposal passed, but with very narrow scope — a compromise that honors both "flat is better than nested" and "simple is better than complex."
# Without walrus: compute the value twice or use a temporary variable
data = get_response()
if data:
process(data)
# With walrus: flat and efficient, but use sparingly
if data := get_response():
process(data)
# Where it really shines: eliminating nesting in loops
while chunk := file.read(8192):
process(chunk)
Structural pattern matching (PEP 634, Python 3.10) faced similar scrutiny. Critics saw it as adding complexity to a language that valued simplicity. The counterargument, which ultimately prevailed, was that the patterns it replaced — chains of if/elif with isinstance() checks — were already complicated, and a dedicated syntax could make the intent more explicit and the code more readable.
# Before pattern matching: nested isinstance checks
def handle_event(event):
if isinstance(event, Click):
if event.button == "left":
handle_left_click(event.position)
elif event.button == "right":
show_context_menu(event.position)
elif isinstance(event, KeyPress):
if event.key == "enter":
submit_form()
# With pattern matching: flat, explicit, readable
def handle_event(event):
match event:
case Click(button="left", position=pos):
handle_left_click(pos)
case Click(button="right", position=pos):
show_context_menu(pos)
case KeyPress(key="enter"):
submit_form()
What the Research Says
The Zen of Python is more than folklore. Academic researchers have studied its actual impact on developer behavior. A 2021 study by Farooq and Zaytsev interviewed thirteen Python programmers of varying skill levels and found that, despite differences in how beginners and experts interpreted the aphorisms, the Zen had a measurable positive effect on how developers write and discuss code. A separate 2018 study by Alexandru et al., published at ACM SIGPLAN's Onward! symposium, analyzed Python idiom usage across GitHub repositories and found that the adoption of Pythonic idioms grew over time — suggesting that the community is collectively converging on the patterns the Zen encourages. A 2022 study at IEEE's ICPC conference by Leelaprute et al. explored whether writing code in alignment with the Zen's principles affected runtime performance and memory use, with preliminary results suggesting that Pythonic idioms can offer performance advantages alongside their readability benefits. (Sources: Alexandru et al., ACM SIGPLAN Onward! 2018; Leelaprute et al., IEEE ICPC 2022; Farooq and Zaytsev, 2021 — all cited in the Zen of Python Wikipedia article)
This is unusual for a set of guiding principles. Many programming communities have style guides and philosophical statements, but few have generated peer-reviewed evidence that their philosophy actually changes behavior at scale. The Zen's influence is not limited to how code looks. It appears to shape how developers reason about trade-offs, which is a much deeper effect.
Putting It Into Practice
The Zen of Python is useful not as a checklist but as a set of questions you ask yourself during code review, design discussions, or when you are stuck choosing between two approaches. When something feels off about a piece of code you have written, there is a reasonable chance one of these nineteen lines tells you why.
A few practical ways to bring PEP 20 into your workflow:
- Code review lens: Before approving or submitting a pull request, run through the aphorisms that feel relevant to the changes. You will catch issues that linters miss. Ask: "Is there a simpler version of this? Can I explain what this function does in one sentence? Would a reader know where every imported name comes from?"
- Design discussions: When a team is debating two architectural approaches, the Zen can serve as a neutral third party. "Which option is easier to explain?" is a question anyone can answer, regardless of seniority.
- Naming decisions: Stuck on what to call a variable or function? The readability and explicitness aphorisms usually point you toward a name that communicates intent rather than implementation.
get_active_users()tells you what it does;gau()does not. - Exception handling audits: Search your codebase for bare
exceptblocks periodically. PEP 20 will remind you why each one is worth a second look. Replace blanket catches with specific exception types and document why each suppression exists. - Refactoring compass: When a module feels unwieldy but you cannot articulate why, read through the Zen line by line against the code. You will often find that two or three specific aphorisms identify the exact problem — too nested, too dense, too implicit — and point toward a concrete solution.
- Onboarding tool: Ask new team members to run
import thison their first day and discuss which aphorism surprises them. Their reaction will tell you something about the expectations they bring from other languages — and gives you an opening to talk about what makes Python different.
The Zen of Python was posted to the python-list mailing list on June 4, 1999, by Tim Peters. Its full text is available at peps.python.org/pep-0020 and is in the public domain.
Run import this the next time you open a Python shell. Read the text that appears. Then look at the source code of the this module itself and notice how thoroughly it violates its own advice. That tension — between the ideal and the practical, between the principle and the exception — is the whole point. The Zen is not a set of commandments. It is a way of thinking, and the best evidence of its value is that after more than twenty-five years, Python developers are still arguing about what it means. That is exactly what Tim Peters intended.