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?
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With