Python Type Hints Explained: Do They Change Runtime Behaviour or Performance?
Type hints aren't enforced and don't change speed. See the timeit proof, where hints are stored, and which tools actually use them.
Python lets you write this:
def total(price: float, quantity: int) -> float:
return price * quantityThe annotations say price should be a float, quantity an int, and the result a float. So what happens if you break the rules?
print(total("ab", 3))abababNo error. Python multiplied a string by 3, exactly as it would without the hints. So what are hints actually for, and do they cost anything?
The simple idea: a label, not a lock
A type hint is a label on a box. "Fragile" on a parcel doesn't make the box stronger; it tells the people handling it what to expect. Python's interpreter is the delivery van: it carries the box and ignores the label.
The people who read the label are your tools: your editor, a type checker like mypy or Pyright, and a few libraries that choose to look.
Where hints go
Python doesn't throw hints away. It stores them on the function:
print(total.__annotations__){'price': <class 'float'>, 'quantity': <class 'int'>, 'return': <class 'float'>}The recommended way to read them is typing.get_type_hints(total), which also resolves hints written as strings. Tools use exactly this information.
Do hints slow things down? Let's measure
Talk is cheap, so let's time a million calls of two identical functions, one with hints and one without:
import timeit
def plain(price, quantity):
return price * quantity
def hinted(price: float, quantity: int) -> float:
return price * quantity
for f in (plain, hinted):
t = min(timeit.repeat(lambda: f(9.5, 3), number=1_000_000, repeat=5))
print(f.__name__, round(t, 3))plain 0.053
hinted 0.05Your numbers will differ by machine, but the two will always be within noise of each other. A call doesn't look at annotations at all, so there's nothing to slow down.
The only cost is a one-off: when Python defines the function, it builds the annotations dictionary. That happens once per function, at import time, and it's tiny.
Python 3.14 removes even that. Annotations are now evaluated lazily (the change is known as PEP 649): Python stores how to compute them and only does it if something asks, such as get_type_hints(). On older versions, from __future__ import annotations gives a similar effect by storing hints as strings.
Do hints make code faster?
In normal CPython, no. The interpreter doesn't use them to optimise anything.
Separate compilers can, though. mypyc (the compiler mypy itself is built with) and Cython read your hints and generate C code. That can make tight loops much faster, but it's a build step you choose, not something hints do on their own.
So who actually uses them?
Type checkers
This is the main job. Run mypy or Pyright, and it reads the labels without running your code:
error: Argument 1 to "total" has incompatible type "str"; expected "float"That's the bug we saw at the start, caught before anyone ran the program. Your editor runs the same kind of check as you type, which is why autocomplete gets so much smarter with hints.
Dataclasses: they read hints, but don't check them
from dataclasses import dataclass
@dataclass
class Item:
name: str
price: float
print(Item(name=42, price="free"))Item(name=42, price='free')@dataclass uses the annotations to decide which fields exist, then trusts whatever you pass. Many people assume dataclasses validate types. They don't.
Pydantic: the one that enforces them
Pydantic deliberately reads hints at runtime and validates, even converting where it's safe:
from pydantic import BaseModel, ValidationError
class Product(BaseModel):
name: str
price: float
print(Product(name="Mug", price="499"))name='Mug' price=499.0The string "499" became the float 499.0. Pass something that can't be converted and you get a clear error:
try:
Product(name="Mug", price="free")
except ValidationError as e:
print(e.errors()[0]["msg"])Input should be a valid number, unable to parse string as a numberThis is the magic behind FastAPI: the hints on your request model become automatic validation and documentation. See it in action in building a CRUD API with FastAPI and Pydantic.
What goes wrong
- Thinking hints protect you at runtime. They don't. If data comes from a user, a file or an API, validate it (Pydantic is great for this) or check it yourself.
- Hints that lie. A hint that says
-> inton a function that sometimes returnsNoneis worse than no hint, because tools now trust a wrong label. Useint | Nonewhen that's the truth. - Over-engineering. You don't need generics and protocols in a weekend script. Hint the function signatures, and let the checker infer the rest.
When to use them
For small scripts, hints are optional but cheap: annotate the parameters of your main functions and you'll get better autocomplete for free. For anything shared, long-lived or bigger than a few files, use them everywhere and run a checker in CI. The bugs they catch are exactly the boring kind that wake you up at night.
Related: Python also has no traditional overloading, which hints sometimes get confused with. Here's how Python handles method overloading instead.
In one line: type hints are labels for your tools, not rules for the interpreter. They don't change speed or behaviour unless a library like Pydantic, or a compiler like mypyc, chooses to read them.
Keep reading
New games, tools and tutorials
About one short email a week. No spam, one-click unsubscribe.