Skip to main content
A generic class in Python is a class that can be parameterized with one or more type variables, allowing it to operate on different data types while maintaining strict static type safety. By defining a class as generic, developers defer the specification of internal types until the class is instantiated, enabling static type checkers (like mypy or pyright) to enforce consistency across attributes, method arguments, and return values.

Syntax and Implementation

Python provides two distinct syntaxes for defining generic classes, depending on the version.

Modern Syntax (Python 3.12+)

Introduced in PEP 695, Python 3.12 natively supports type parameter syntax using square brackets directly in the class definition. This eliminates the need to import TypeVar or Generic.

Legacy Syntax (Python 3.5 - 3.11)

In older versions, generic classes are constructed by instantiating typing.TypeVar and inheriting from typing.Generic.

Instantiation and Type Binding

When a generic class is instantiated, the type variable is bound to a concrete type. This binding can be explicit (using square brackets during instantiation) or implicit (inferred by the type checker based on the arguments passed to the constructor).

Multiple Type Variables

Generic classes can be parameterized with multiple type variables to represent complex internal structures.

Type Bounds and Constraints

Type variables within generic classes can be restricted to accept only specific types or subclasses of a specific type. Constrained Types: The type variable must be exactly one of the specified types.
Bound Types: The type variable must be a subclass of a specific class.

Variance

Variance dictates how subtyping between generic classes relates to subtyping between their type arguments.
  • Invariance: Container[int] is not a subtype of Container[float]. This is the default behavior.
  • Covariance: Subtype relationships are preserved. If Dog is a subtype of Animal, Producer[Dog] is a subtype of Producer[Animal].
  • Contravariance: Subtype relationships are reversed. Consumer[Animal] is a subtype of Consumer[Dog].
Python 3.12+ Syntax: Under PEP 695, variance is automatically inferred by static type checkers based on how the type parameter is utilized within the class (e.g., as a return type vs. an argument type).
Legacy Syntax: In older versions, variance must be explicitly declared using the covariant or contravariant keyword arguments in TypeVar.

Subclassing Generic Classes

When inheriting from a generic class, the subclass can retain the generic nature, concretize the type variable, or introduce entirely new type variables. Retaining Generics:
Concretizing the Type:
Introducing New Type Variables: A subclass can inherit a concretized type while introducing its own new type variables. In legacy syntax, this requires explicitly inheriting from Generic alongside the concretized base class.
Tired of Poor Python Skills? Fix That With Deep Grasping!Learn More