Write Python like plain English

You will read your code many more times than you write it. So the real skill is not making Python work. It is making Python that someone can understand six months later, at speed, without asking you.

Python is unusually good at this. It reads close to English already. The trick is to lean into that instead of fighting it.

Name things the way you would say them

The name is the comment you never have to update. If you can say what a variable holds in a few words, use those words.

# hard to read
d = {}
for x in l:
    if x[2] > 0:
        d[x[0]] = x[2]

# easy to read
unpaid_by_client = {}
for invoice in invoices:
    if invoice.amount_due > 0:
        unpaid_by_client[invoice.client] = invoice.amount_due

Both do the same work. Only one tells you what the code is about.

Two rules cover most cases. Booleans read as questions: is_paid, has_expired, needs_review. Functions read as instructions: send_invoice, parse_date, find_overdue.

Let a function do one thing

If you need the word “and” to describe a function, it is probably two functions. Split it. Short functions are easier to name, easier to test, and easier to skim past when you are looking for something else.

A useful check: can you read the function body top to bottom and follow it without scrolling? If not, pull a chunk out and give it a name.

Say what you mean, not how the computer does it

Python has direct ways to express common ideas. Use them.

# how the computer does it
found = False
for user in users:
    if user.email == wanted:
        found = True
        break

# what you mean
found = any(user.email == wanted for user in users)

The second version says “is there any user with this email”. That is the actual question you were asking.

The same goes for enumerate instead of counting by hand, zip instead of indexing two lists, and a list comprehension instead of building a list in a loop. These are not clever tricks. They are the plain wording.

But stop before it gets dense. A comprehension nested three levels deep is not plain any more. When it stops reading like a sentence, go back to the loop.

Handle the errors you actually expect

Wrapping everything in a bare except hides real bugs. Catch the specific thing that can go wrong, and let anything else crash loudly where you can see it.

try:
    config = json.loads(path.read_text())
except FileNotFoundError:
    config = default_config()
except json.JSONDecodeError as err:
    raise SystemExit(f"{path} is not valid JSON: {err}")

A crash with a clear message beats silence. Silence is how a small problem becomes a long evening.

Return early

Deep nesting is hard to hold in your head. Deal with the odd cases first, then write the main path flat at the bottom.

def send_invoice(invoice):
    if invoice.is_draft:
        return
    if not invoice.client.email:
        raise ValueError("client has no email address")

    pdf = render(invoice)
    mail(invoice.client.email, pdf)
    invoice.mark_sent()

The important part of the function is now the last three lines, at the left margin, where your eye lands.

Comment the why, not the what

Do not repeat the code in English. The code already says what it does. Use comments for the thing the code cannot say: why it is like that.

# The API rejects more than 20 IDs per call, undocumented but consistent.
for batch in chunked(ids, 20):
    fetch(batch)

That comment saves the next person an hour. # loop over batches saves them nothing.

Type hints as documentation

You do not need a type checker to get value from hints. They tell the reader what goes in and what comes out, and your editor will use them.

def overdue_total(invoices: list[Invoice], today: date) -> Decimal:
    ...

That one line answers most questions someone would have about the function.

Where to start

If this feels like a lot, pick one habit. Renaming variables is the highest return for the least effort, and you can do it while you read code you already have.

Good Python is not clever Python. It is Python that a tired person can read on a Friday afternoon and get right the first time.

← All posts