Today I took a deep dive into Liskov's Substitution Principle and covariance/contravariance.
And I got stuck on the difference between:
T = TypeVar("T", bound=Union[A, B])T = TypeVar("T", A, B, covariant=True)My Understanding of #1
Difference between TypeVar('T', A, B) and TypeVar('T', bound=Union[A, B])
This answer clearly states T can be :
Union[A, B](or a union of any subtypes ofAandBsuch asUnion[A, BChild])A(or any subtype ofA)B(or any subtype ofB)
This makes perfect sense to me.
My Flawed Understanding of #2
MyPy doesn't allow constrained TypeVar's to be covariant? Defining a generic dict with constrained but covariant key-val types
Re-mentions the bound=Union[A, B] case, but does not get at the meaning of option #2, A, B, covariant=True.
I have tried playing around with mypy, and can't seem to figure it out.
Can anyone point out what this means?
I think it means:
A (or any subtype of A)B (or any subtype of B)(aka it excludes the Union case from above)
**Edit**
It was asked in the comments:
Are you sure that they're actually different?
Here's sample code to show the difference. The errors come from mypy==0.770.
from typing import Union, TypeVar, Generic
class A: pass
class ASub(A): pass
class B: pass
# Case 1... Success: no issues found
# T = TypeVar("T", bound=Union[A, B])
# Case 2... error: Value of type variable "T" of "SomeGeneric" cannot be "ASub"
T = TypeVar("T", A, B, covariant=True)
class SomeGeneric(Generic[T]): pass
class SomeGenericASub(SomeGeneric[ASub]): pass
**Edit 2**
I ended up asking about this in python/mypy #8806: Generic[T_co] erroring when T_co = TypeVar("T_co", A, B, covariant=True) and passed subclass of A
This cleared up some misunderstandings I had. Turns out TypeVar("T", A, B, covariant=True) isn't really correct, knowing the value restrictions A and B aren't actually covariant.
Use of covariant=True syntax is only helpful when they're related.
It is a type variable. Type variables exist primarily for the benefit of static type checkers. They serve as the parameters for generic types as well as for generic function definitions.
Python is a dynamically typed language. This means that the Python interpreter does type checking only as code runs, and that the type of a variable is allowed to change over its lifetime.
Covariance and contra-variance are terms that relate to the intersection between object orientation and generics.
Here's the question this concept is trying to answer:
Base and
Derived.List<T>.List<Derived> can be used wherever List<Base> can?List<Base> can be used wherever List<Derived> can?If the answer to (3) is yes, it's called covariance and we'll say declare List as having covariance=True. If the answer to (4) is true, it's called 'contra-variance'. If none is true, it's invariant.
Bounds also come from the intersection of OO and generics. When we define a generic type MyType - does it mean that 'T' can be any type at all? Or, may I impose some limitations on what T might be? Bounds allow me to state that the upper bound of T is, for example, the class Derived. In that case, Base can't be used with 'MyType' - but Derived and all its subclasses can.
The definition of covariance and contravariance can be found in this section of PEP-484.
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