Pythonic Code & PEP 8

"Pythonic" code uses the language's features the way they were designed to be used — it's usually shorter, clearer and faster than a direct translation from another language. PEP 8, Python's official style guide, keeps code consistent across projects. Modern tools apply most of it for you.

The Zen of Python

PY zen.py
import this
Output
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!

These aphorisms, built into Python as an Easter egg, summarise its design philosophy.

PEP 8 essentials

RuleExample
Indent with 4 spacesNever tabs
Lines up to 79 characters (many teams use 88–100)Wrap inside brackets
snake_case for functions and variablestotal_price, get_user()
PascalCase for classesShoppingCart
UPPER_CASE for constantsMAX_RETRIES = 3
Spaces around operators and after commasx = a + b, f(a, b)
No spaces around = in keyword argumentsf(size=10)
Two blank lines between top-level functions/classesOne blank line between methods
Imports at the top: standard library, third-party, localOne group per block

Let tools do the formatting

Instead of memorising style rules, use a formatter and a linter. Ruff is a very fast tool that does both:

$ terminal
pip install ruff
ruff format .        # reformat code consistently (Black-compatible style)
ruff check .         # find bugs, unused imports, style problems
ruff check --fix .   # fix what can be fixed automatically

Configure your editor to format on save, and style debates disappear from code review.

Pythonic idioms

Loop over items, not indexes

PY loops.py
names = ["Ana", "Ben", "Cleo"]

# Not Pythonic
for i in range(len(names)):
    print(i, names[i])

# Pythonic
for i, name in enumerate(names):
    print(i, name)
Output
0 Ana
1 Ben
2 Cleo
0 Ana
1 Ben
2 Cleo

Use truthiness and in

PY truthiness.py
items = []
status = "closed"

if not items:
    print("empty")
if status in ("closed", "archived"):
    print("inactive")
Output
empty
inactive

Comprehensions over map/filter with lambdas

PY comprehensions.py
prices = [12.5, 7.0, 30.0]

discounted = [p * 0.9 for p in prices if p > 10]
print(discounted)
Output
[11.25, 27.0]

Unpacking and swapping

PY unpacking.py
point = (3, 4)
x, y = point
a, b = 1, 2
a, b = b, a
first, *rest = [10, 20, 30]
print(x, y, a, b, first, rest)
Output
3 4 2 1 10 [20, 30]

Context managers for resources

PY context.py
from pathlib import Path

with open("data.txt", "w", encoding="utf-8") as f:
    f.write("pythonic")

print(Path("data.txt").read_text(encoding="utf-8"))
Output
pythonic

dict.get, setdefault and Counter

PY dicts.py
from collections import Counter

words = "a b a c b a".split()
print(Counter(words).most_common(1))

config = {}
print(config.get("timeout", 30))
Output
[('a', 3)]
30

f-strings and join

PY strings.py
name, total = "Ana", 13.5
print(f"{name} owes ${total:.2f}")
print(", ".join(["red", "green", "blue"]))
Output
Ana owes $13.50
red, green, blue

Use built-ins

PY builtins.py
scores = [72, 88, 95]
print(sum(scores), max(scores), sorted(scores, reverse=True), any(s > 90 for s in scores))
Output
255 95 [95, 88, 72] True

Habits that prevent bugs

  • Compare to None with is: if value is None.
  • Never use mutable default arguments (lesson 29).
  • Catch specific exceptions, never a bare except: (lesson 41).
  • Don't shadow built-ins like list, id, input.
  • Use pathlib for paths and always pass encoding="utf-8" for text files.
  • Add type hints to function signatures (lesson 33) and run a type checker.

Readable over clever

Pythonic doesn't mean squeezing everything onto one line. A three-level nested comprehension with a walrus operator might be "clever", but a simple loop with a well-named variable is easier to read and debug. When choosing between two ways, pick the one a teammate will understand fastest.

Try it yourself

Rewrite this un-Pythonic code using the idioms above, then compare with the version underneath:

Try it yourself
Console output