Annotating Functions and Classes
15 min · Intermediate
Need help? Ask a tutor on WhatsApp (opens in a new tab)A type hint is a note that says what kind of value a name should hold. Python ignores the note when the program runs. Editors and checkers read it and warn you about mistakes before you run the code.
Annotating a function
Type hint is a note in the code that says which type a value should have, such as price: float. Python stores the note but does not enforce it when the program runs.
A function says what it takes and what it gives back. Write a colon and a type after each parameter, and -> and a type after the brackets.
def total_price(price: float, quantity: int) -> float: return price * quantityExample 1 · A function with hints
def discounted(price: float, percent: int) -> float: return price - price * percent / 100
print(discounted(2000.0, 15))print(discounted(499.0, 10))1700.0 449.1
What this code does
- The two hints say a float and an int go in, and a float comes out.
- The hints change nothing at runtime. They are notes for readers and tools.
price: float says what goes in. -> float says what comes out. A function that returns nothing is marked -> None.
Hints are not enforced
Annotation is the syntax for a hint: name: type on a variable or parameter, and -> type for a return value. Python saves annotations in __annotations__ and does nothing else with them.
Python reads the hint and keeps it, then ignores it. A wrong call still runs. The hint helps people and tools, not the interpreter.
def total_price(price: float, quantity: int) -> float: return price * quantity
print(total_price("ab", 3))print(total_price.__annotations__)ababab
{'price': <class 'float'>, 'quantity': <class 'int'>, 'return': <class 'float'>}Example 2 · Hints do not stop a wrong call
def double(n: int) -> int: return n * 2
print(double(21))print(double("ab"))try: double(None)except TypeError as error: print(error)42 abab unsupported operand type(s) for *: 'NoneType' and 'int'
What this code does
double("ab")runs and printsabab, because*also repeats strings.- The
TypeErrorcomes from the*operator, not from the hint.
The string "ab" is not a float, yet "ab" * 3 is valid Python, so a wrong answer appears with no error. A checker such as mypy would have warned you before running.
Annotating variables and attributes
Variables take the same name: type form. You can add a value or leave it out. Inside a class, the same line lists the attributes an object will have.
count: int = 0names: list[str] = []
class Account: owner: str balance: float = 0.0Example 3 · Annotations saved on a class
class Account: owner: str balance: float = 0.0
print(Account.__annotations__)print(Account.balance){'owner': <class 'str'>, 'balance': <class 'float'>}
0.0What this code does
__annotations__is a plain dictionary from name to type.- Only
balancehas a value.ownerwas declared and never assigned.
A line like owner: str only declares the type. No value exists until you assign one. Most variables need no hint, because a checker works out the type from the first value.
Annotating methods and constructors
Self type is the hint -> Self. It means "an object of the same class as the one this method was called on", so it stays correct in subclasses.
self needs no hint. Annotate every other parameter and the return value. __init__ always returns nothing, so it is marked -> None.
class Account: 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.balanceExample 4 · A method that returns Self
from typing import Self
class Cart: def __init__(self) -> None: self.items: list[str] = []
def add(self, item: str) -> Self: self.items.append(item) return self
cart = Cart().add("Pen").add("Book")print(cart.items)['Pen', 'Book']
What this code does
addreturns the same cart, so calls can be chained.-> Selfkeeps that true for a subclass ofCartas well.
A checker reads __init__ and learns that owner is a str and balance is a float. A method that returns its own object can be marked -> Self, imported from typing.
Next step
Check your understanding
11 quick questions