Last modified: Sep 10, 2026
Make Python Statically Typed
Python is a dynamically typed language. This means types are checked at runtime. However, you can add static typing using type hints. Static typing helps catch errors before running the code.
Why Use Static Typing?
Static typing improves code readability. It makes large projects easier to maintain. IDEs can give better autocomplete suggestions. Tools like mypy can check types without executing the code.
Adding Type Hints to Variables
You can annotate variables with their expected types. This does not change how Python runs the code. It only adds metadata.
# Annotating a string variable
name: str = "Alice"
# Annotating an integer
age: int = 30
# Annotating a boolean
is_student: bool = False
No output is shown because these are just annotations. They help developers and tools understand the intended types.
Type Hints in Functions
Functions can also have type hints. You specify the parameter types and the return type.
def greet(name: str) -> str:
return f"Hello, {name}!"
message = greet("Bob")
print(message)
Hello, Bob!
Here, greet takes a string and returns a string. If you pass the wrong type, tools like mypy will warn you.
Using Complex Types
You can use complex types like lists and dictionaries. Import the List and Dict types from the typing module.
from typing import List, Dict
def process_items(items: List[str]) -> Dict[str, int]:
counts = {}
for item in items:
counts[item] = len(item)
return counts
result = process_items(["apple", "banana"])
print(result)
{'apple': 5, 'banana': 6}
This function takes a list of strings and returns a dictionary mapping each string to its length.
Optional and Union Types
Sometimes a variable can have more than one type. Use Union to specify multiple possible types.
from typing import Union
def parse_value(value: Union[str, int]) -> str:
return str(value)
print(parse_value("123"))
print(parse_value(456))
123
456
You can also use Optional when a value might be None.
from typing import Optional
def get_name(name: Optional[str]) -> str:
if name is None:
return "Unknown"
return name
print(get_name(None))
print(get_name("Alice"))
Unknown
Alice
Checking Types with mypy
To enforce static typing, use a type checker like mypy. Install it using pip.
pip install mypy
Run mypy on your Python file to check for type errors.
mypy my_script.py
If there are type mismatches, mypy will report them.
Classes and Static Typing
You can add type hints to class attributes and methods.
class Person:
name: str
age: int
def __init__(self, name: str, age: int) -> None:
self.name = name
self.age = age
def introduce(self) -> str:
return f"My name is {self.name} and I am {self.age} years old."
person = Person("Alice", 30)
print(person.introduce())
My name is Alice and I am 30 years old.
Using Type Aliases
For readability, you can create type aliases for complex types.
from typing import List
# Creating a type alias
Coordinate = List[float]
def distance(point_a: Coordinate, point_b: Coordinate) -> float:
return ((point_a[0] - point_b[0])**2 + (point_a[1] - point_b[1])**2)**0.5
print(distance([0.0, 0.0], [3.0, 4.0]))
5.0
Generics with Type Variables
Use TypeVar for generic functions that work with multiple types.
from typing import TypeVar, List
T = TypeVar('T')
def first_item(items: List[T]) -> T:
return items[0]
print(first_item([1, 2, 3]))
print(first_item(["a", "b", "c"]))
1
a
Conclusion
Adding static typing to Python enhances code quality and developer experience. Type hints provide clarity and enable powerful tooling. Use mypy to catch type errors early. While Python remains dynamically typed at runtime, static typing gives you the best of both worlds.
Start small by annotating function signatures. Gradually add more annotations as you become comfortable. This improves maintainability and reduces bugs in large codebases.