Definition
Beyond basic hints, Python's typing system supports optional values (str | None), generics (a list of a certain type), Protocols (a required shape), TypedDict and more. A static checker such as mypy or Pyright uses these to find bugs without running the code.
In simple words
Plain hints say "this is a list". Advanced ones say "this is a list of students, and this function returns the same type it was given". The more exact the description, the more mistakes a checker can catch.
Why it matters
Large codebases rely on this to stay safe to change. LLM libraries also use it: Pydantic turns type hints into validation and into schemas for tool calling.
How it works
| Hint | Means |
|---|---|
| str | None | A string or None |
| list[int] | A list of ints |
| dict[str, float] | String keys, float values |
| Callable[[int], str] | A function from int to str |
| Literal['a', 'b'] | Only these values |
| Protocol | Anything with this shape |
| TypedDict | A dict with known keys |
In code
from typing import Literal, Protocol, TypedDict, TypeVar
T = TypeVar("T")
def first(items: list[T]) -> T | None:
return items[0] if items else None
class Student(TypedDict):
name: str
mark: int
class Predictor(Protocol):
def predict(self, x: float) -> float: ...
class Doubler: # no inheritance is needed
def predict(self, x: float) -> float:
return x * 2
def run(model: Predictor, mode: Literal["fast", "slow"]) -> float:
return model.predict(2.0)
print(first([1, 2]), first([]))
riya: Student = {"name": "Riya", "mark": 90}
print(riya["mark"])
print(run(Doubler(), "fast"))Output
1 None 90 4.0
Common mistakes
- Overusing Any. It switches off checking wherever it spreads.
- Assuming hints validate at run time. Use Pydantic when you need that.
Check yourself
Summary
- Precise hints catch more bugs before you run the code.
- mypy or Pyright do the checking.
Finished reading?
Sources: Python docs: typing