Difficulty: Advanced
What are TypeVar and Generic in Python type hints? How do you write generic functions and classes?
TypeVar creates a placeholder type variable that is resolved when a function or class is used.
Generic functions: the return type relates to the input type (e.g., first(list[int]) -> int). Generic classes: parameterize the class with a type (Stack[int], Queue[str]).
In Python 3.12+: generic syntax is simplified - def first[T](lst: list[T]) -> T instead of TypeVar.
Bound TypeVar: restricts acceptable types to a base class. Constrained TypeVar: only the listed types are accepted.
from typing import TypeVar, List, Optional, Sequence
T = TypeVar('T') # Unrestricted
Numeric = TypeVar('Numeric', int, float) # Constrained to int or float
# Generic function: return type matches input type
def first(seq: Sequence[T]) -> Optional[T]:
return seq[0] if seq else None
def last(seq: Sequence[T]) -> Optional[T]:
return seq[-1] if seq else None
def repeat(item: T, times: int) -> List[T]:
return [item] * times
# Type checker knows: first([1, 2, 3]) returns int, not Any
result_int = first([1, 2, 3]) # int
result_str = first(['a', 'b']) # str
result_none = first([]) # None
print(first([1, 2, 3])) # 1
print(first(['a', 'b'])) # a
print(repeat('x', 3)) # ['x', 'x', 'x']
# Bound TypeVar
from typing import TypeVar
Comparable = TypeVar('Comparable', bound='Comparable')
# Built-in: Comparable constraint
def maximum(a: Numeric, b: Numeric) -> Numeric:
return a if a > b else b
print(maximum(3, 5)) # 5
print(maximum(3.14, 2.0)) # 3.14
TypeVar T is inferred from the input type. The type checker knows first([1, 2, 3]) returns int, enabling better error detection.
from typing import TypeVar, Generic, Iterator
T = TypeVar('T')
class Stack(Generic[T]):
def __init__(self) -> None:
self._items: list[T] = []
def push(self, item: T) -> None:
self._items.append(item)
def pop(self) -> T:
if not self._items:
raise IndexError('Stack is empty')
return self._items.pop()
def peek(self) -> T:
return self._items[-1]
def __len__(self) -> int:
return len(self._items)
def __iter__(self) -> Iterator[T]:
return reversed(self._items)
# Type-safe usage
int_stack: Stack[int] = Stack()
int_stack.push(1)
int_stack.push(2)
print(int_stack.pop()) # 2 - type checker knows this is int
str_stack: Stack[str] = Stack()
str_stack.push('hello')
print(str_stack.peek()) # hello
# Python 3.12+ simplified syntax
# def first[T](seq: list[T]) -> T: ...
# class Stack[T]:
# def push(self, item: T) -> None: ...
Stack[int] tells the type checker that pop() returns int, not Any. The generic class works for any type - type safety comes from the annotation.
TypeVar, Generic, Generics, Type Safety, mypy