“Pythonic” means writing code the way the Python community expects it. It’s not just about working code — it’s about code that reads naturally and uses the language’s strengths.
The Zen of Python
Run import this in any Python shell and we get 19 guiding principles. The ones that matter most day-to-day:
- Beautiful is better than ugly — readability counts
- Explicit is better than implicit — don’t be clever, be clear
- Simple is better than complex — if there’s a straightforward way, use it
- There should be one obvious way to do it — Python prefers one right path
Key PEP 8 Rules
PEP 8 is the official style guide. Here’s the cheat sheet:
- Indentation: 4 spaces (never tabs)
- Line length: 79 characters max (120 is common in practice)
- Naming:
snake_casefor functions/variables,PascalCasefor classes,UPPER_SNAKEfor constants - Blank lines: 2 before top-level definitions, 1 between methods
- Imports: one per line, grouped (stdlib → third-party → local), at the top of the file
Pythonic Patterns
These are the idioms that separate Python beginners from experienced developers.
Use enumerate instead of range(len(...)):
# Not Pythonic
for i in range(len(names)):
print(i, names[i])
# Pythonic
for i, name in enumerate(names):
print(i, name)
Unpack instead of indexing:
# Not Pythonic
point = (3, 7)
x = point[0]
y = point[1]
# Pythonic
x, y = (3, 7)
EAFP over LBYL: Python prefers “Easier to Ask Forgiveness than Permission.” In simple language, try it first and handle the error, rather than checking every condition upfront.
# LBYL (Look Before You Leap) — not Pythonic
if key in dictionary:
value = dictionary[key]
# EAFP — Pythonic
try:
value = dictionary[key]
except KeyError:
value = default
Use join for string concatenation:
# Slow — creates a new string each iteration
result = ""
for word in words:
result += word + " "
# Fast and Pythonic
result = " ".join(words)
Truthiness checks — keep them simple:
# Not Pythonic
if len(my_list) > 0:
if active == True:
# Pythonic — empty collections are falsy, non-empty are truthy
if my_list:
if active:
List comprehensions over manual loops:
# Verbose
squares = []
for x in range(10):
squares.append(x ** 2)
# Pythonic
squares = [x ** 2 for x in range(10)]
Common Anti-Patterns
- Using
type(x) == intinstead ofisinstance(x, int) - Bare
except:that catches everything (includingKeyboardInterrupt) - Mutable default arguments like
def f(lst=[])(we’ll cover this gotcha later) - Using
globalwhen we could pass arguments or use a class - Writing Java-style getters/setters instead of using
@property
In simple language, Pythonic code is about using Python the way Python wants to be used. Read PEP 8 once, use a linter like ruff or flake8, and it becomes second nature.