Python is dynamically typed by default, but you can add typing discipline at three levels: type hints, static checking, and runtime enforcement.
Annotations document intent. They are not enforced at runtime on their own, but tools read them.
def greet(name: str) -> str:
return "Hello " + name
age: int = 30
A static checker analyses your annotations before the code runs and flags mismatches.
# run in the terminal:
mypy myscript.py
# flags this as an error:
greet(42) # Argument 1 has incompatible type "int"
To actually reject wrong types while the program runs, check them yourself or use a library.
# manual guard
def greet(name: str) -> str:
if not isinstance(name, str):
raise TypeError("name must be a str")
return "Hello " + name
Libraries such as pydantic validate data against types automatically at runtime.
from pydantic import BaseModel
class User(BaseModel):
name: str
age: int
User(name="Ada", age="oops") # raises ValidationError
In short: hints describe, mypy checks before running, and isinstance or pydantic enforce while running.