Imagine that we are programming a game. Our game features several three-dimensional objects, including cardboard boxes. A way to represent that in code would be like this:
class Figura3D():
pass
class Cubo(Figura3D):
pass
class CaixaDePapelao(Cubo):
pass
There is a base class to represent all three-dimensional figures, then a class to represent all three-dimensional cubic figures, then a class to represent specifically a cardboard box, which is obviously a three-dimensional cubic figure.
Our game needs a class responsible for rendering things on the screen, which would look something like this:
T = TypeVar('T')
class Renderizador(Generic[T]):
def __init__(self, x: T):
pass
Notice that the class Renderizador
has a generic argument of type T
, because he should be able to render anything.
The code of our game still has a function that specifically serves to render cubes.
def executar_render(render: Renderizador[Cubo]):
pass
It receives a cube renderer and uses it to draw the cube on the screen.
Now, knowing that an object of the class CaixaDePapelao
is also a Cubo
(due to inheritance), it makes sense to pass a Renderizador[CaixaDePapelao]
for the above function, correct?
Not!
The following code is refused by mypy:
render_caixa_de_papelao = Renderizador(CaixaDePapelao())
executar_render(render_caixa_de_papelao)
error: Argument 1 to "executar_render" has incompatible type "Renderer[Caixadepapelao]"; expected "Renderer[Cubo]"
This happens because the TypeVar('T')
by default is invariant. That means your subclasses and superclasses are not compatible with it. However, if we change the statement to:
T = TypeVar('T', covariant=True)
The error disappears and mypy accepts that cardboard box renderers are used instead of cube renderers.
In short:
- A guy invariant does not accept subclasses or superclasses
- A guy covariant accepts subclasses, but not superclasses
- A guy countervariant accepts superclasses, but not subclasses
Now the question remains:
What are the names of the guys who accept both subclasses and superclasses?
Are called bivariate and are not supported by Python.