Writing it cleanly

INPUT · Slides

Finishing with code that gets read

01 / 06

The four tools side by side

Four things came out of this chapter, and not one of them changes the behaviour.

  • PEP 8 … makes the writing uniform. Four spaces to indent, lowercase names with underscores, one space either side of an operator
  • a docstring … a description in """...""" placed just below a function or class
  • a type hint … a note of what goes in and what comes back
  • a dataclass … deletes the lines nobody was in doubt about from a class that only holds data

Why do any of it, when nothing changes? To take time off the reader. And that reader is usually you, three days later.

02 / 06

Names decide half of it

Nothing works harder than a name. Turning a and x into price and count often does away with a whole sentence of explanation.

  • variables and functions are lowercase with underscores (yearly_fee / total_fees)
  • classes start with a capital (Member / Product)
  • something holding several things is plural (members / prices)

Before you add an explanation, ask whether the name could say it instead.

unit_price = 250count = 4print(f"total ${unit_price * count}")

Result

total $1000

03 / 06

A type hint and a docstring have different jobs

Both are tools for the reader, but they answer different questions.

  • a type hint … what you pass, and what comes back
  • a docstring … what it actually does, and any conditions on what you pass

There is no need to say again in the docstring what the type hint has already said. Writing things that do not overlap is the trick.

def discount(price: int, rate: float) -> int:    """Return the price after the discount. Pass rate as a proportion, such as 0.2."""    return int(price * (1 - rate))print(discount(1000, 0.2))

Result

800

04 / 06

Where a dataclass earns the most

@dataclass pays off most when you replace data you were keeping in dictionaries.

With a dictionary you write item["price"], quotes and all, and a mistyped key stays hidden until the program runs. With @dataclass you write product.price, and the belongings are declared, with their types, in one place.

from dataclasses import dataclass@dataclassclass Product:    name: str    price: intproducts = [Product("apple", 150), Product("orange", 80)]for product in products:    print(f"{product.name} ${product.price}")

Result

apple $150
orange $80

05 / 06

The order to tidy in

Trying to clean everything at once stops you dead. From the top, one at a time is enough.

  • change the names into words you can read
  • split long stretches of work into units that have names
  • put type hints and docstrings on the functions used from outside
  • turn classes that only hold data into a @dataclass

Every one of these is changing the shape while leaving the behaviour alone. Run it after each change and check the output has not moved. That is how you tidy up safely.

06 / 06

The last ten

What follows is the last ten questions of the Python course. There is no new grammar left to come.

The first half takes the tools one at a time. The second half builds up, and the last question is a small but finished program made of a class, a list and functions.

Get here and the Python course is done. You have been through the grammar, you can read other people's code, and you can write code others can read. What you build next is up to you.