Intrastellar Explorer
Intrastellar Explorer

Reputation: 2411

What does TypeVar(A, B, covariant=True) mean?

Today I took a deep dive into Liskov's Substitution Principle and covariance/contravariance.

And I got stuck on the difference between:

  1. T = TypeVar("T", bound=Union[A, B])
  2. 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 :

  1. Union[A, B] (or a union of any subtypes of A and B such as Union[A, BChild])
  2. A (or any subtype of A)
  3. B (or any subtype of B)

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:

  1. A (or any subtype of A)
  2. B (or any subtype of B)

(aka it excludes the Union case from above)


Here's sample code to show that they are actually different. 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

Upvotes: 59

Views: 30938

Answers (1)

Roy2012
Roy2012

Reputation: 12503

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:

  1. We have a couple of "regular", "object-oriented" classes, Base and Derived.
  2. We also have some generic type - let's say List[T].
  3. We know that Derived can be used anywhere Base can - does that mean that List[Derived] can be used wherever List[Base] can?
  4. Could it be the other way around? Maybe it's the reverse direction and now 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<T> - 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.

Upvotes: 115

Related Questions