I never really used Generics in the past and this is my first time using mypy in Python, but I'm trying to type a function that both accept a class and a value of that class using the typing module
So far I made this, but in both uses, mypy
does not raise any error when I use the command mypy file.py
. Am I doing something wrong ?
# file.py
from typing import TypeVar, Type, Any
T = TypeVar("T")
def function(cls: Type[T], inst: T) -> bool:
# Should be true for all values matching the type annotation
return isinstance(inst, cls)
function(int, 5) # Correct
function(str, 5) # Incorrect - mypy should be raising an error here
When determining which type T
is actually bound to, mypy
looks at both arguments and choose a type that will satisfy each, rather than trying to use one to restrict options available for the other. When you call
function(str, 5)
mypy
choose to bind object
to T
: this works because 5
is an instance of object
and str
is a subclass of object
.
I don't think there is a general solution to this. For more specific cases, you can enumerate a list of types that can be bound to T
, which can prevent a too-general superclass from being chosen. For example, given
T = TypeVar('T', str, int)
only str
or int
, not object
, can be bound to T
when typechecking function
. This means the following are OK:
function(str, "5") # T ~ str
function(int, 5) # T ~ int
but this is not:
function(str, 5) # Neither T ~ str nor T ~ int satisfy the type hints.
To force you to think about what restrictions you really want to put on T
to disallow function(str, 5)
, consider the following:
function(int, True)
Do you want that to fail? If so, you are violating a fundamental tenet of type checking, which says you can use a value of type a
anywhere a value of type b
is expected, as long as a
is a subtype of b
.
Do you want that to succeed? Then you need to figure out why binding T ~ int
is acceptable but binding T ~ object
is not. I'm not sure you can do that, again, without violating the fundamental tenet stated above.