From the official docs: The introduction of explicit syntax for generic classes in Python 3.12 eliminates the need for variance to be specified for type parameters. Instead, type checkers will infer the variance of type parameters based on their usage within a class. Type parameters are inferred to be invariant, covariant, or contravariant depending on how they are used. The problem is, type checkers include __init__ when infering variance: they check if __init__ accepts the typevar as init parameter. class MyContainer[T]: # This is covariant for all intended usecases # Inferred as invariant because of `val: T`, but I need to pass the value somehow... def __init__(self, val: T) -> None: self.val = val def get_val(self) -> T: return self.val def print_float_val(container: MyContainer[float]) -> None: print(container) int_container = MyContainer(1) # reveal_type: MyContainer[int] print_float_val(int_container) # main.py:15: error: Argument 1 to "print_float_val" has incompatible type "MyContainer[int]"; expected "MyContainer[float]" [arg-type] I cannot imagine this behavior to be very useful because: My understanding of covariant generics is that they are mostly (perhaps all) read-only containers. Pretty much all my covariant container types that I can think of receive the values it contains during initialization: tuple, namedtuple, frozenset. We only call __init__ when instantiating objects, and it is irrelevant afterward. For instance, print_float_val accepts an instance of MyContainer[float], so the object would have been instantiated before being passed to this function. Continue reading...