Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Understanding bound TypeVars with generic parameters

I'm trying to understand how bound variables work in TypeVars. I understand that any subclass of the bound class is allowed, but once I make the bound class a generic, things that I would expect to work don't:

from typing import Generic, TypeVar

class X:
    pass
class Y(X):
    pass

T = TypeVar("T", bound=X)

class A(Generic[T]):
    def __init__(self, param: T):
        self.param = param
class B(A[T]):
    pass

S = TypeVar("S", bound=A[X])

def foo(bar: S) -> S:
    return bar

foo(B(Y())) # Type "B[Y]" cannot be assigned to type "A[X]"

Could someone explain why this doesn't work, and if any workarounds are known?

like image 432
Glenn Sun Avatar asked Aug 04 '26 22:08

Glenn Sun


1 Answers

The other answer explains a solution and links you to the correct mathematical keyword, so I will only answer the question "Why doesn't this work?"

Is a list of Dogs not a list of animals? Is List[Dog] a subtype of List[Animal]? You might think it is. But what does it mean for a type in a programming language to be a subtype of another type? The subtype has to be usable everywhere the supertype can be used, that is the Liskov Substitution principle (the L of SOLID). When a function takes a List[Animal], can you put a List[Dog] in it? No, not necessarily! Consider the scenario that the function adds a cat to the List[Animal], that will not work for a List[Dog].

Covariance means that the type variable behaves in such a way that a List[Dog] can be considered a subtype of List[Animal]. For lists, that is the case if you only perform read-operations on the list. The code Animal a = dogList[0] is valid, only dogList[0] = someAnimal is problematic.[Note 1]

For more in-depth explanations of this topic, read this non-python question (you will be able to follow without knowing any Java): Is List<Dog> a subclass of List<Animal>? Why are Java generics not implicitly polymorphic?

For understanding covariance/contravariance, look at this excellent answer with its beautiful picture which lets you skip some mathsy texts.

[Note 1] 'covariant list = readonly list' is a bit of a simplification. There are modifying operations that work on covariant lists, such as sorting, deleting or copying elements. So while in practice it often means readonly, do not inflate this with immutability/constness

like image 192
julaine Avatar answered Aug 07 '26 13:08

julaine