# Construct an empty Stack[int] instancestack = Stack[int]()stack.push(2)stack.pop()stack.push('x') # Error: Argument 1 has incompatible type "str"; expected "int"stack2: Stack[str] = Stack()stack2.push('x') # OK
Construction of generic instances is type checked:
class Box[T]: def __init__(self, content: T) -> None: self.content = contentBox(1) # OK, inferred type is Box[int]Box[int](1) # Also OKBox[int]('some string') # Error: Argument 1 has incompatible type "str"
from collections.abc import Sequencedef first[T](seq: Sequence[T]) -> T: return seq[0]reveal_type(first([1, 2, 3])) # Revealed type is "builtins.int"reveal_type(first(('a', 'b'))) # Revealed type is "builtins.str"
from typing import TypeVar, SequenceT = TypeVar('T')def first(seq: Sequence[T]) -> T: return seq[0]reveal_type(first([1, 2, 3])) # Revealed type is "builtins.int"reveal_type(first(('a', 'b'))) # Revealed type is "builtins.str"
You cannot explicitly pass type parameter values when calling generic functions. Type parameters are always inferred.
You can restrict a type variable to having values that are subtypes of a specific type:
Python 3.12+
Python 3.11 and earlier
from typing import SupportsAbsdef max_by_abs[T: SupportsAbs[float]](*xs: T) -> T: return max(xs, key=abs)max_by_abs(-3.5, 2) # OK, has type 'float'max_by_abs(5+6j, 7) # OK, has type 'complex'max_by_abs('a', 'b') # Error: 'str' is not a subtype of SupportsAbs[float]
Python 3.11+ provides a simpler syntax using Self:
from typing import Selfclass Friend: other: Self | None = None @classmethod def make_pair(cls) -> tuple[Self, Self]: a, b = cls(), cls() a.other = b b.other = a return a, bclass SuperFriend(Friend): passa, b = SuperFriend.make_pair() # Type is tuple[SuperFriend, SuperFriend]
If B is a subtype of A, then MyCovGen[B] is a subtype of MyCovGen[A].
from collections.abc import Sequenceclass Shape: ...class Triangle(Shape): ...def count_sides(shapes: Sequence[Shape]) -> int: return sum(s.num_sides for s in shapes)triangles: Sequence[Triangle]count_sides(triangles) # OK - Sequence is covariant
Contravariant
If B is a subtype of A, then MyContraGen[A] is a subtype of MyContraGen[B].
from collections.abc import Callabledef cost_of_paint_required( triangle: Triangle, area_calculator: Callable[[Triangle], float]) -> float: return area_calculator(triangle) * DOLLAR_PER_SQ_FT# This works!def area_of_any_shape(shape: Shape) -> float: ...cost_of_paint_required(triangle, area_of_any_shape) # OK
Invariant
Neither covariant nor contravariant.
def add_one(things: list[Shape]) -> None: things.append(Shape())my_circles: list[Circle] = []add_one(my_circles) # Error: list is invariant