devnotes.

Generics and Advanced Typing

Modern Python type hints can express generic functions and classes — code that works with a type parameter, checked consistently across a single call — using increasingly concise syntax across recent versions.

from typing import TypeVar, Generic

T = TypeVar("T")               # a type variable — pre-3.12 way to write generics

def first_item(items: list[T]) -> T:     # whatever type the list holds, that's the return type
    return items[0]

class Stack(Generic[T]):                   # a generic class — pre-3.12 syntax
    def __init__(self) -> None:
        self._items: list[T] = []
    def push(self, item: T) -> None:
        self._items.append(item)
    def pop(self) -> T:
        return self._items.pop()

int_stack: Stack[int] = Stack()      # a Stack specifically of ints
int_stack.push(5)

# Python 3.12+ new, cleaner generic syntax — no TypeVar import needed:
def first_item_new[T](items: list[T]) -> T:
    return items[0]

class Stack312[T]:
    def __init__(self) -> None:
        self._items: list[T] = []

A generic function like first_item says "I accept a list of some type T, and I return that same type T" — a type checker can then verify that first_item([1, 2, 3]) returns an int, and first_item(["a", "b"]) returns a str, without needing a separate hint-writing pass for every possible type.

Python 3.12 introduced new, built-in syntax for generics (def func[T](...), class Stack[T]:) that eliminates the need to import and declare TypeVar separately — a meaningful readability improvement, though the older TypeVar/Generic style (shown above) remains necessary for code that needs to support Python versions before 3.12, and is still extremely common to encounter when reading existing codebases.

Generics and Advanced Typing in Python | DevNotes