Skip to content

Python Type Hints

Updated 8 min read Aimerscrew

Python type hints are small labels you add to your code to say what kind of value a variable or function expects, such as int, str or list[float]. They don't change how your program runs, but they make code easier to read and let your editor spot mistakes early. In this lesson, you'll learn how to write type hints for variables, functions and collections, and why they're so popular in modern Python.

🌍 Real-life example

Think of the labels on the storage containers in a kitchen: "Sugar", "Rice", "Coffee beans". The labels don't physically stop anyone from pouring salt into the sugar jar. But they tell everyone what belongs inside, and a careful cook notices right away when something's wrong. Type hints are labels for your code: Python won't stop a wrong value, but you, your teammates and your tools will notice.

What are type hints in Python?

A type hint (also called a type annotation) is a note in your code that says which type a value should have. Python is dynamically typed, as you learned in Python variables, so you never have to declare types. Type hints are an optional extra that make your intentions clear.

How do you add type hints to variables?

Write a colon and the type after the variable name, then assign the value as usual:

player: str = "Jordan"
level: int = 3
health: float = 87.5
is_online: bool = True

print(player, level, health, is_online)
Jordan 3 87.5 True

Read level: int = 3 as "level is an int, and it starts at 3". For simple variables like these, the type is obvious from the value, so many programmers skip the hint. Variable hints are most useful when the value is empty at first, like orders: list[str] = [].

How do you add type hints to functions?

Function hints are where type hints really shine. Put a type after each parameter name, and put -> plus the return type before the colon at the end of the def line:

def add_tax(price: float, rate: float) -> float:
    return round(price * (1 + rate), 2)

def greet(name: str) -> str:
    return f"Welcome back, {name}!"

print(add_tax(20.0, 0.1))
print(greet("Sofia"))
22.0
Welcome back, Sofia!

Just by reading the first line of add_tax, you know it takes two decimal numbers and gives one back. You don't need to read the body or guess.

Diagram of the line def add_tax(price: float, rate: float) -> float: with labels pointing to the parameter name, the type hint and the return type; below, editors and mypy read the hints while Python itself ignores them at runtime
The parts of a type-hinted function. Tools read the hints; Python itself ignores them when running.

A function that doesn't return anything useful gets the return type None:

def print_receipt(total: float) -> None:
    print(f"You paid ${total:.2f}")

print_receipt(18.5)
You paid $18.50

Are type hints enforced in Python?

No. This surprises a lot of beginners. Python stores the hints but doesn't check them when your program runs. Watch what happens when we pass the "wrong" types:

def double(number: int) -> int:
    return number * 2

print(double(4))
print(double("ha"))
print(double([1, 2]))
8
haha
[1, 2, 1, 2]

No error at all. The hint says int, but Python happily doubles a string and a list. The hints are only stored as information, which you can see in the function's __annotations__ attribute:

def double(number: int) -> int:
    return number * 2

print(double.__annotations__)
{'number': <class 'int'>, 'return': <class 'int'>}

📝 Note

If you need to really stop wrong values, you still check them yourself (for example with isinstance()) and raise an error. Type hints are documentation plus tool support, not a security guard.

Type hints for lists, dictionaries and other collections

For collections, put the type of the items in square brackets. Since Python 3.9, you can use the built-in names directly:

HintMeaningExample value
list[int]A list of integers[90, 75, 88]
dict[str, float]Keys are strings, values are floats{"latte": 4.5}
tuple[str, int]Exactly a string, then an int("Lions", 3)
set[str]A set of strings{"red", "blue"}
int | floatEither an int or a float7 or 7.5
str | NoneA string, or nothing"SAVE10" or None
def average(scores: list[int]) -> float:
    return sum(scores) / len(scores)

def cheapest(menu: dict[str, float]) -> str:
    return min(menu, key=menu.get)

print(average([90, 75, 88, 97]))
print(cheapest({"latte": 4.5, "tea": 3.0, "mocha": 5.25}))
87.5
tea

How do you hint a value that might be None?

Many functions return a value or None when nothing is found. Write that with the | symbol, which means "or":

def find_price(menu: dict[str, float], item: str) -> float | None:
    if item in menu:
        return menu[item]
    return None

menu = {"latte": 4.5, "tea": 3.0}
print(find_price(menu, "tea"))
print(find_price(menu, "smoothie"))
3.0
None

In older code (before Python 3.10) you'll see the same idea written with the typing module: Optional[float] means exactly float | None.

from typing import Optional

def find_seat(row: str) -> Optional[int]:
    seats = {"A": 12, "B": 7}
    return seats.get(row)

print(find_seat("A"))
print(find_seat("Z"))
12
None

Type hints in classes

Methods take hints just like functions. The self parameter doesn't need one, and __init__ returns None. You've already seen this style in Python dataclasses, which use type hints to define their fields.

class BankAccount:
    def __init__(self, owner: str, balance: float = 0.0) -> None:
        self.owner = owner
        self.balance = balance

    def deposit(self, amount: float) -> float:
        self.balance += amount
        return self.balance

account = BankAccount("Noah")
print(account.deposit(250.0))
250.0

Why use type hints if Python ignores them?

  • Readable code. The function signature tells you what goes in and what comes out.
  • Better editor help. Editors such as VS Code and PyCharm use hints for autocomplete. Type name. and they suggest string methods because they know name is a str.
  • Catch bugs before running. Editors underline mistakes, and a free tool called mypy checks a whole project for type errors.
  • Safer changes. In a big project, hints show you every place that breaks when you change a function.

To try mypy, install it with pip (see pip and virtual environments) and point it at a file. For a file that calls double("ha") on line 4, mypy reports something like this, without ever running your code:

pip install mypy
mypy shop.py
shop.py:4: error: Argument 1 to "double" has incompatible type "str"; expected "int"  [arg-type]
Found 1 error in 1 file (checked 1 source file)

Real-world example: shopping cart functions

Here's a small online-store module with full type hints. Each signature documents exactly what it expects:

def line_total(price: float, quantity: int) -> float:
    return price * quantity

def cart_total(cart: dict[str, tuple[float, int]]) -> float:
    total = 0.0
    for price, quantity in cart.values():
        total += line_total(price, quantity)
    return round(total, 2)

def apply_coupon(total: float, code: str | None = None) -> float:
    if code == "SAVE10":
        return round(total * 0.9, 2)
    return total

cart: dict[str, tuple[float, int]] = {
    "notebook": (3.5, 4),
    "pen": (1.25, 10),
    "backpack": (29.99, 1),
}

total = cart_total(cart)
print("Total:", total)
print("With coupon:", apply_coupon(total, "SAVE10"))
print("No coupon:", apply_coupon(total))
Total: 56.49
With coupon: 50.84
No coupon: 56.49

A teammate who has never seen this code can tell from dict[str, tuple[float, int]] that the cart maps product names to a (price, quantity) pair.

Common mistakes with type hints

1. Expecting Python to block wrong types

As you saw, double("ha") runs without complaint. Use mypy or your editor to check hints, and validate input yourself when it matters.

2. Using = instead of : in a hint

def greet(name=str):
    return "Hi " + name

print(greet("Ana"))
print(greet())
Hi Ana
TypeError: can only concatenate str (not "type") to str

name=str sets a default value (the str type itself), not a hint. The correct form is name: str.

3. Calling the type in a hint

Write list[int], not list(int). Square brackets describe the item type; round brackets would try to call list on int and fail with TypeError: 'type' object is not iterable.

4. Forgetting that a value can be None

If a function can return None, say so with | None. Otherwise anyone reading -> float will assume they always get a number.

Practice exercise

🏋️ Try it yourself

Write a fully type-hinted function longest_song(playlist: dict[str, int]) -> str | None. The dictionary maps song titles to their length in seconds. Return the title of the longest song, or None if the playlist is empty. Test it with a playlist and with {}.

Show solution
def longest_song(playlist: dict[str, int]) -> str | None:
    if not playlist:
        return None
    return max(playlist, key=playlist.get)

road_trip: dict[str, int] = {"Open Road": 214, "Night Drive": 251, "Home": 187}
print(longest_song(road_trip))
print(longest_song({}))
Night Drive
None

What's next?

Type hints make your code clearer and safer. Next, you'll learn a powerful tool for searching and cleaning text: Python regular expressions.

Key takeaways

  • Type hints are optional labels that describe the expected type of a variable, parameter or return value.
  • Annotate parameters with name: type and return values with -> type before the colon.
  • Use list[int], dict[str, float], tuple[str, int] and set[str] to describe collection contents.
  • Write X | None (or Optional[X] in older code) for a value that might be None.
  • Python does not enforce type hints at runtime; wrong types still run.
  • Editors and tools like mypy use hints to give autocomplete and catch bugs before you run the code.

Frequently asked questions

What are type hints in Python?

Type hints are optional annotations that state which type a value should have, such as age: int or def total(prices: list[float]) -> float. They make code easier to read and let tools check it.

Does Python enforce type hints?

No. Python stores type hints but ignores them while running, so passing a string to a parameter hinted as int does not raise an error. Use a type checker like mypy or your editor to find such mistakes.

What is the difference between Optional[int] and int | None?

They mean the same thing: the value is an int or None. Optional comes from the typing module and works in older Python versions; the int | None form needs Python 3.10 or newer.

Do type hints make Python faster?

No. Standard Python ignores hints at runtime, so they do not speed up or slow down your program. Their benefits are readability, editor support and catching bugs early.

What is mypy?

mypy is a free static type checker for Python. You install it with pip and run mypy file.py; it reads your type hints and reports mismatches without running the program.

Should beginners use type hints?

Adding hints to function parameters and return values is a great habit once you are comfortable with functions. You can skip them on obvious variables like count = 0.