Skip to main content

PEP 20

· 7 min read
Keith Tan
Software Engineer • Geek • Potato

The Zen of Python

In this post, let’s talk about the 19 aphorisms that define Python’s philosophy, along with simple definitions and practical examples

import-this

Easter Egg

Know this

The Zen of Python, authored by Tim Peters, is both a set of guiding principles for writing Pythonic code and a fun Easter egg hidden within Python itself.

To see Zen of Python, run either:

import this

The Zen of Python, by Tim Peters

Beautiful is better than ugly. Explicit is better than implicit. Simple is better than complex. Complex is better than complicated. Flat is better than nested. Sparse is better than dense. Readability counts. Special cases aren't special enough to break the rules. Although practicality beats purity. Errors should never pass silently. Unless explicitly silenced. In the face of ambiguity, refuse the temptation to guess. There should be one-- and preferably only one --obvious way to do it. Although that way may not be obvious at first unless you're Dutch. Now is better than never. Although never is often better than right now. If the implementation is hard to explain, it's a bad idea. If the implementation is easy to explain, it may be a good idea. Namespaces are one honking great idea -- let's do more of those!

important

If you run into issues, it's likely that Python is not installed on your machine. Download it here.


import this

Beautiful is better than ugly

Code for humans.

Clean, readable code is easier to understand and maintain.

  • ⭕ Good
    def f(x):
    return x*x + 2*x + 1
  • ✅ Better
    def quadratic(x):
    return x * x + 2 * x + 1

Explicit is better than implicit

Make it obvious.

Avoid hidden meanings and ensure that the purpose of the code is obvious. Clear code is easier to understand than clever shortcuts.

  • ⭕ Good
    from math import *  # What is being imported?
  • ✅ Good
    from math import sqrt, pi  # Explicit and clear

Simple is better than complex

Avoid unnecessary complexity.

Complex solutions should only be used when absolutely necessary. Simplicity leads to better code.

  • ❌ Complicated
    def add(a, b): 
    return (lambda x, y: x + y)(a, b)
  • ✅ Simplified
    def add(a, b):
    return a + b

Complex is better than complicated

If complexity is necessary, structure it well.

Code should be well-structured and understandable, not convoluted or confusing.

  • ❌ Complicated
    def factorial(n):
    return 1 if n == 0 else n * factorial(n - 1)
  • ✅ Complex but better
    def factorial(n):
    result = 1
    for i in range(1, n + 1):
    result *= i
    return result

Flat is better than nested

Avoid deep nesting that makes code hard to follow.

Flat code is easier to read and manage.

  • ⭕ Good
    if user:
    if user.is_active:
    if user.has_permission():
    print("Access granted")
  • ✅ Good
    if user and user.is_active and user.has_permission():
    print("Access granted")

Sparse is better than dense

Whitespace improves readability.

Dense code can be hard to read and understand. Spread out code with appropriate spacing.

  • ⭕ Good
    x=[1,2,3];y=4;z=x+y
  • ✅ Good
    x = [1, 2, 3]
    y = 4
    z = x + y

Readability counts

Code is read more often than it is written.

Prioritize readability to make it easier for others (and yourself) to understand and maintain.

  • ❌ Less Readable
    def d(a,b,c):
    return (a+b)*c
  • ✅ Readable
    def calculate(a, b, c):
    return (a + b) * c

Special cases aren't special enough to break the rules

Consistency is key.

Stick to the rules and conventions of the language, even for special cases.

  • Following the rules
    def divide(a, b):
    if b == 0:
    raise ValueError("Cannot divide by zero")
    return a / b
  • Breaking the rules
    def divide(a, b):
    if b == 0:
    return None
    return a / b

Although practicality beats purity

Sometimes, breaking a rule is okay for practical reasons.

While following best practices is important, practical solutions should take precedence when necessary. Using try-except instead of checking every possible edge case:

try:
value = int(user_input)
except ValueError:
print("Invalid number")

Comparison

  • Practical
    def read_file(file_path):
    try:
    with open(file_path, 'r') as file:
    return file.read()
    except FileNotFoundError:
    return "File not found"
  • Pure
    def read_file(file_path):
    with open(file_path, 'r') as file:
    return file.read()

Errors should never pass silently

Handle errors explicitly.

Ignoring errors can lead to unexpected behavior and bugs.

  • ⭕ Good
    try:
    risky_function()
    except:
    pass # Silently ignores all errors
  • ✅ Good
    try:
    risky_function()
    except Exception as e:
    print(f"Error: {e}")

Unless explicitly silenced

If ignoring an error is intentional, document it.

If an error must be ignored, it should be done explicitly and with good reason.

try:
os.remove("foobar.txt")
except FileNotFoundError:
pass # It's okay if the file doesn't exist

In the face of ambiguity, refuse the temptation to guess

Do not make assumptions.

Write clear logic rather than making assumptions. When code behavior is unclear, seek clarity and ensure the code's intent is explicit.

  • ❌ Guessing
    def get_user_age(user):
    return user.get('age', 0) # Assuming age is 0 if not found
  • ✅ Seeking clarity
    def get_user_age(user):
    if 'age' in user:
    return user['age']
    else:
    raise KeyError("User age not found")

There should be one—and preferably only one—obvious way to do it

Clear and consistent.

Python emphasizes having a single, clear way to accomplish a task, reducing confusion and increasing consistency.

  • ❌ Multiple ways (Confusing)
    import os
    os.system("ls") # One way
    import subprocess
    subprocess.run(["ls"]) # Another way
  • ✅ Preferred
    import pathlib
    print(list(pathlib.Path(".").iterdir()))

Although that way may not be obvious at first unless you're Dutch

Here's why

Python might seem tricky at first, but it makes more sense once you get used to it, especially if you're familiar with its Dutch creator, Guido van Rossum.

Now is better than never

Don’t over-optimize; ship working code.

It's better to take action and write code now rather than procrastinate.

print("Launching Project...")

Although never is often better than right now

Don’t rush bad code—balance speed and quality.

Rushing to write code without proper thought can lead to poor quality. Balance urgency with careful consideration.

If the implementation is hard to explain, it's a bad idea

Code should be simple enough to explain easily.

If it's too complex to explain, it likely needs to be simplified.

  • ❌ Difficult to explain
    def mystery(x):
    return (x & (x - 1)) == 0
  • ✅ Better
    def is_power_of_two(x):
    return x > 0 and (x & (x - 1)) == 0

If the implementation is easy to explain, it may be a good idea

The value of simplicity and clarity in coding.

When an implementation is easy to explain, it usually means that the code is straightforward, understandable, and maintainable. Simple and clear implementations are often the best solutions.

def factorial(n):
if n == 0:
return 1
else:
return n * factorial(n-1)

Namespaces are one honking great idea—let's do more of those!

Using namespaces

Namespaces help organize code and avoid naming conflicts. Use them liberally to keep code clean and modular.

import math
print(math.sqrt(16)) # Using the math namespace to access sqrt function

Learn more!


Happy coding! 🚀