Skip to content

Expert · Advanced Python · topic 43 of 50 · 30 min

Type hints

Explanation

Type hints are notes in your code that say what type a variable, parameter or return value should be, like def total(prices: list[float]) -> float. Python itself does not check them when the program runs. Tools such as mypy and pyright, and your code editor, read them to catch mistakes before you run anything.

What a type hint is

A type is the kind of a value: int, str, list and so on. A type hint (also called a type annotation) is a short label that says which type you expect. You write it after a colon for variables and parameters, and after an arrow -> for what a function returns.

python
city: str = "Chennai"
population: int = 7_000_000

def ticket_price(distance_km: float, is_senior: bool) -> float:
    price = distance_km * 1.5
    if is_senior:
        price *= 0.6
    return round(price, 2)

print(ticket_price(200, True))
Read it as: distance_km should be a float, is_senior a bool, and the function gives back a float.
Think of it like this: Type hints are like the labels on the boxes of a form: "Date of birth (DD/MM/YYYY)", "Mobile number (10 digits)". The label does not stop anyone writing the wrong thing, but it tells everyone what belongs there, and a careful checker can spot a wrong entry before the form is submitted.

For containers, put the type of the items in square brackets. Since Python 3.9 you can use the normal built-in names for this.

HintMeans
list[int]A list of whole numbers, like [90, 75, 62]
dict[str, float]A dict with text keys and decimal values, like {"rice": 52.5}
tuple[str, int]A tuple of exactly two items: a str then an int
tuple[int, ...]A tuple of any length, all ints
set[str]A set of strings

Values that may be missing: Optional and X | None

Many functions return a value or None ("nothing found"). Write that as str | None (Python 3.10+). Older code writes the same thing as Optional[str], imported from the typing module. Both mean "a str, or None".

python
def find_phone(contacts: dict[str, str], name: str) -> str | None:
    return contacts.get(name)

book = {"Anil": "98xxxxxx01"}
print(find_phone(book, "Anil"))
print(find_phone(book, "Zoya"))     # None
The hint warns the caller: check for None before using the result.

Union means "one of these types". int | str (or Union[int, str] in older code) accepts either. A type alias is just a variable that holds a long hint, so you can give it a short, meaningful name and reuse it.

python
from typing import Union

PinCode = Union[int, str]                    # alias, old style
Cart = dict[str, tuple[int, float]]          # item -> (quantity, price)

def cart_total(cart: Cart) -> float:
    return sum(qty * price for qty, price in cart.values())

print(cart_total({"pen": (3, 10.0), "notebook": (2, 45.5)}))
Cart is easier to read than the full dict[...] hint every time.

Hints are not checked when the program runs, and this surprises many learners. Python stores the hints but ignores them while running. Passing a str where an int is hinted gives no error from the hint itself. The code only fails later if the wrong type causes a real problem.

python
def double(n: int) -> int:
    return n * 2

print(double("ha"))    # haha  (no error: hints are not enforced)
Python happily runs this. A type checker would flag it before you run it.

A static type checker is a separate tool that reads your code without running it and reports type mistakes. The two common ones are mypy and pyright. Editors like VS Code (through the Pylance extension, which uses pyright) show these mistakes as red underlines as you type.

bash
pip install mypy
mypy app.py
# app.py:4: error: Argument 1 to "double" has incompatible type "str"; expected "int"
Run a type checker from the terminal, or let your editor do it for you.

TypedDict describes a dict with fixed keys, each with its own type, which is handy for JSON data. Protocol describes "anything that has these methods", so a checker accepts any class that fits, without inheritance. Both come from the typing module; you will meet them in bigger projects.

python
from typing import TypedDict

class Student(TypedDict):
    name: str
    marks: int

def passed(s: Student) -> bool:
    return s["marks"] >= 35

print(passed({"name": "Divya", "marks": 81}))
print(Student.__annotations__)
print(passed.__annotations__)
__annotations__ is the dict where Python stores the hints of a function or class.
Tip: Start by adding hints to function parameters and return values. That is where they help most: anyone calling the function, including you in six months, sees at once what to pass and what comes back.
Careful: A hint like scores: list[int] = [] does not protect the list. Python still lets you append a string to it; only a type checker will complain.

Watch the lesson

Type hints · 1/6

Type hints

This lesson is about type hints, the small labels that say what type each value should be.

Prefer a video by a teacher? Find free videos on “python type hints tutorial mypy” on YouTube ↗

In real life: Clear hints in an exam results service

A state board website calculates results for lakhs of students. Many developers work on the same code. With type hints, a new developer can see that grade() takes a list of marks and returns a grade string, and the team runs mypy in their checks so a mistake like passing a single number instead of a list is caught before the results go live.

python
def average(marks: list[int]) -> float:
    return sum(marks) / len(marks)

def grade(marks: list[int]) -> str:
    avg = average(marks)
    if avg >= 75:
        return "Distinction"
    if avg >= 35:
        return "Pass"
    return "Fail"

print(grade([78, 82, 91]))
print(grade.__annotations__)
The hints document the function, and checkers use them.

Practice (0/3 done)

Task 1 · Code · Python

Write average(marks) with full type hints: marks is a list[float] and the function returns float | None. Return the average, or None for an empty list. (The checker reads average.__annotations__.)

Task 2 · Code · Python

Write hint_names(func) that reads func.__annotations__ and returns a dict from each name to the NAME of its type (use the type's __name__). For def f(name: str, age: int) -> bool, it returns {"name": "str", "age": "int", "return": "bool"}. Use simple types only (no list[...]).

Task 3 · Practice

Install mypy on your computer (pip install mypy). Write a small file with a hinted function, call it with a wrong type on purpose, and run mypy on the file. Then fix the call and run mypy again.

Quick quiz

Question 1

1.What happens when you run double("ha") if double is hinted as (n: int) -> int and returns n * 2?

Question 2

2.Which hint means "a dict whose keys are str and values are float"?

Question 3

3.Which hint means the same as Optional[int]?

Question 4

4.What are mypy and pyright?

Question 5

5.Where does Python store a function's hints?

Read more (free)

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

← Previous: Context managers and the with statement