Week 2 · Python for real work
Type hints
Label your variables, parameters and return values with the kind of data they hold, so your code is easier to read and tools can catch mistakes.
A type hint is a label that says what kind of data a variable, parameter or return value should hold. A function works fine without hints, but with them you can understand it at a glance:
def short_code(name: str) -> str:
return name.strip().lower().replace(" ", "-")
From the first line alone, you know short_code takes a string and returns a string. You don't have to read the rest of the function.
Hints help the next person who reads your code, and they help tools such as VS Code and mypy find your mistakes. Python itself doesn't check them when your program runs.
How to follow along
Create a separate project for this lesson and the next three. Your learn-python project has practice files with mistakes in them on purpose, and you don't want mypy reporting those:
cd ~/projects/fde-course/week-02
uv init --no-package --python ">=3.14" learn-types
cd learn-types
code .
Create a file called hints.py, type each example into it and run it with:
uv run hints.py
Don't call the file types.py, because Python has a standard library module with that name.
Hints on functions
Write parameter: type for each parameter, and put the return type after ->:
def add_delivery(total: int, charge: int) -> int:
return total + charge
def greet(name: str) -> None:
print(f"Hello, {name}")
def add_tax(total: float, rate: float = 0.18) -> float:
return total + total * rate
print(add_delivery(1000, 350))
greet("Noor Foods")
print(add_tax(1000))
Output:
1350
Hello, Noor Foods
1180.0
-> Nonemeans the function doesn't return anything useful. It's there to do a job, such as printing.- A parameter with a default value has its type first and its default after it:
rate: float = 0.18.
Hints on variables
You can label variables too, but you rarely need to. Here, the value already makes the type obvious:
customer = "Noor Foods"
An empty collection is different, because nothing shows what's meant to go in it. A hint makes that clear:
stock_counts: dict[str, int] = {}
Hints for collections
Square brackets say what a collection holds:
def split_tags(raw: str) -> list[str]:
tags = []
for tag in raw.split(","):
tags.append(tag.strip())
return tags
print(split_tags("rice, oil ,sugar"))
Output:
['rice', 'oil', 'sugar']
| Hint | What it means |
|---|---|
list[str] | A list of strings |
dict[str, int] | A dictionary with string keys and whole number values |
tuple[float, float] | A tuple of exactly two decimal numbers |
set[str] | A set of strings |
Types that take a type in square brackets like this are called generic types.
A value that might be missing
A lookup might not find anything, so a value can be a string or None. Use |, read as "or", to allow more than one type:
def find_phone(phones: dict[str, str], customer: str) -> str | None:
return phones.get(customer)
phones = {"Noor Foods": "0300-1234567"}
print(find_phone(phones, "Noor Foods"))
print(find_phone(phones, "Bilal Traders"))
Output:
0300-1234567
None
You'll often see | None with a default value, for an optional parameter:
def archive_customer(name: str, reason: str | None = None) -> None:
...
Your own classes and enums work as types too, such as def archive_all(customers: list[Customer]) -> None:.
Hints don't check anything by themselves
Python ignores type hints when it runs your code. This breaks the hint, but it runs anyway:
def add_delivery(total: int, charge: int) -> int:
return total + charge
print(add_delivery("noor", "foods"))
Output:
noorfoods
To catch this before the code runs, you need a type checker, which is the next lesson. To check data while the program runs, such as data from a form or an API, you'll use Pydantic.