Python Type Hints
Type hints are small labels that say what type a variable or function expects, so that editors and tools can catch mistakes early.
Why Type Hints?
Python does not force you to declare types. That makes it quick to write, but in a big project it is easy to pass the wrong kind of value. Type hints help in three ways:
- Your editor (like VS Code) can autocomplete and warn you about mistakes as you type
- Checkers like
mypyorpyrightcan find bugs before you run the program - Other people (and you, six months later) can read the code more easily
Type hints do not change how Python runs your code. Python ignores them at run time. They are notes for humans and tools.
Hints on Variables
Write a colon and the type after the name:
Example
name: str = "Ayesha"
age: int = 21
fee_paid: bool = True
cgpa: float = 3.6
print(name, age, fee_paid, cgpa)Ayesha 21 True 3.6
Hints on Functions
Put the type after each parameter, and the return type after ->.
Example
def total_bill(price: float, qty: int, discount: float = 0) -> float:
return price * qty - discount
def greet(name: str) -> None:
print(f"Hello, {name}")
print(total_bill(250.0, 4, 100))
greet("Bilal")900.0 Hello, Bilal
-> None means the function returns nothing useful.
Hints Are Not Checked at Run Time
To prove that Python ignores hints, pass the wrong type:
Example
def double(n: int) -> int:
return n * 2
print(double(5))
print(double("ha"))
print(double.__annotations__)10
haha
{'n': <class 'int'>, 'return': <class 'int'>}No error. Python happily doubles the string. A type checker, however, would flag the second call before you even run it.
Collections
From Python 3.9 you can write the item type in square brackets on the normal built-in types.
| Hint | Meaning |
|---|---|
list[int] | A list of whole numbers |
tuple[str, int] | A tuple with exactly a string and then an int |
tuple[int, ...] | A tuple of any length, all ints |
dict[str, float] | Keys are strings, values are floats |
set[str] | A set of strings |
Example
def average(marks: list[int]) -> float:
return sum(marks) / len(marks)
def price_list(menu: dict[str, int]) -> list[str]:
return [f"{item}: Rs {price}" for item, price in menu.items()]
print(average([70, 85, 90]))
print(price_list({"tea": 60, "paratha": 80}))81.66666666666667 ['tea: Rs 60', 'paratha: Rs 80']
Optional and Union Types
Use | when a value can be one of several types (Python 3.10+). A very common case is "a value or None".
Example
def find_phone(name: str, book: dict[str, str]) -> str | None:
return book.get(name)
def to_number(value: int | str) -> int:
return int(value)
contacts = {"Ali": "0300-1234567"}
print(find_phone("Ali", contacts))
print(find_phone("Zara", contacts))
print(to_number("42") + to_number(8))0300-1234567 None 50
In older code you will see Optional[str] and Union[int, str] from the typing module. They mean the same thing.
Dataclasses Use Type Hints
A dataclass reads the type hints in a class and writes __init__, __repr__ and __eq__ for you.
Example
from dataclasses import dataclass, field
@dataclass
class Student:
name: str
roll_no: int
courses: list[str] = field(default_factory=list)
s = Student("Hina", 1042)
s.courses.append("Python")
print(s)
print(s == Student("Hina", 1042, ["Python"]))Student(name='Hina', roll_no=1042, courses=['Python']) True
That is a full class with no __init__ written by hand.
Checking With mypy
Install a checker with pip and run it on your file:
Example
pip install mypy
mypy shop.pyIf shop.py contains total_bill("250", 4), mypy reports something like:
shop.py:5: error: Argument 1 to "total_bill" has incompatible type "str"; expected "float" [arg-type] Found 1 error in 1 file (checked 1 source file)
Start small. Add hints to function parameters and return values first. That gives you most of the benefit with very little extra typing.
Exercise
Exercise
What happens if you call a function with the hint n: int using a string instead?
