Writing it cleanly

INPUT · Slides

Writing down what kind of value it is

01 / 07

No way of telling what to pass in

When you met functions in Chapter 3, a name was all you could put on an argument. Look at the line def add(a, b): and there is nothing to tell you what belongs in a until you read the body.

And this function really will take numbers or text. Whoever wrote it may have meant numbers, but that intention is written down nowhere.

def add(a, b):    return a + bprint(add(2, 3))print(add("cat", "dog"))

Result

5
catdog

02 / 07

Write the kinds on the arguments and the return

Put : kind after the name of an argument, and -> kind after the ). That is a type hint.

  • a: int … an integer arrives in a
  • -> int … this function returns an integer

The arrow is a - with a > next to it. It is not =>, so take care.

def add(a: int, b: int) -> int:    return a + bprint(add(2, 3))

Result

5

03 / 07

Ordinary variables take them too

This is not only about functions. On a variable you put : kind after the name, then = the value in.

Four kinds will carry you a long way.

  • int … a whole number
  • float … a number with a decimal point
  • str … text
  • bool … True or False
name: str = "Yui"age: int = 15member: bool = Trueprint(f"{name} {age} {member}")

Result

Yui 15 True

04 / 07

Containers say what is inside them

A list or a dictionary can say what it holds as well. You line the kinds of the contents up inside square brackets.

  • list[int] … a list of integers
  • dict[str, int] … a dictionary whose keys are strings and whose values are integers

That tells the reader far more than a bare list. Whether it is a list of scores or a list of names is plain without reading the body.

scores: list[int] = [90, 60, 75]scores_by_name: dict[str, int] = {"Yui": 90, "Haruto": 60}print(len(scores))print(scores_by_name["Yui"])

Result

3
90

05 / 07

A value that might not be there

"The score if it is found, None if it is not" — sometimes a value comes out as one of two kinds. For that you join them with a | (a vertical bar).

int | None means "an integer, or else None". The "no value" of Chapter 1 can now be said from the side of the kinds as well.

def find(name: str) -> int | None:    scores = {"Yui": 90, "Haruto": 60}    if name in scores:        return scores[name]    return Noneprint(find("Yui"))print(find("Minato"))

Result

90
None

06 / 07

Nothing is checked while it runs

This is the part that matters most. Python does not look at type hints while it runs.

Hand a string to an argument written a: int and there is no error — it simply runs. A type hint is a note written alongside, not a guard on the door.

def add(a: int, b: int) -> int:    return a + bprint(add("cat", "dog"))

Result

catdog

07 / 07

Why write them anyway

Not one thing about the behaviour changes, so why bother. Two reasons.

  • The reader knows what you meant. The single line def add(a: int, b: int) -> int: says how to use it without any prose
  • Your editor catches mistakes. With the hints there, a string handed to a number argument gets underlined

Like the docstring in the last group, this is written for the reader rather than for the machine. Next lesson you will see type hints cut down how much you have to write as well.