from typing import Callable, TypeVar
T = TypeVar('T')
IndicatorFunction = Callable[[T], bool]
# mypy accepts this annotation.
s1: Callable[[T], bool] = lambda x: False
# mypy rejects this annotation, but it's just an alias of the first one.
s2: IndicatorFunction[T] = lambda x: False
I am trying to understand how to make mypy consider T
to be bound in the type annotation for s2
in the above code snippet. I get the following error:
main.py:9: error: Type variable "__main__.T" is unbound
main.py:9: note: (Hint: Use "Generic[T]" or "Protocol[T]" base class to bind "T" inside a class)
main.py:9: note: (Hint: Use "T" in function signature to bind "T" inside a function)
Found 1 error in 1 file (checked 1 source file)
The hints helpfully tell me how to bind "T" inside a class and in a function signature, but not how to do so for the type annotation of a lambda expression.
Subquestions:
- How do I bind T in the type annotation of
s2
? - Why do I not get the same error in the type annotation of
s1
? SinceIndicatorFunction
is an alias fors1
's type, I expected boths1
ands2
to be accepted bymypy
.
CodePudding user response:
After reading a lot of PEPs and mypy
's issue tracker, I believe I understand what's happening here.
How do I bind
T
in the type annotation ofs2
?
You can't. There is no syntax defined for it in PEP 526, which introduced variable annotations.
As far as I can tell, and as implied by mypy's error message, there are only two ways to quantify (bind) a type variable T
in Python as it stands today:
- In a class definition, by including either
Generic[T]
orProtocol[T]
in the inheritance list (or another class that inherits from these two). In other words,Generic
andProtocol
are "magical" implicit type variable binders. - In a function definition's parameter list, by annotating a parameter with a generic type that contains
T
as a type parameter.
There is no way to introduce a type variable binding in a PEP 526 variable annotation. PEP 526 makes almost no mention of type parameters and generics.
Will Python add a way to bind type variables in variable annotations in the future? It doesn't look like it. PEP 695, if accepted, will greatly improve the clarity of type variable binding syntax, but, again, it only treats the above two cases: introducing bindings in either class or function definitions, not in variable annotations.
The next question implied by all this is why does mypy treat s1
as valid and implicitly binds T
? You can see that mypy does implicitly bind T
if you try to give it this code:
T = TypeVar('T')
s3: Callable[[T], bool] = lambda x: x False
mypy
gives these errors:
main.py:5: error: Incompatible types in assignment (expression has type "Callable[[T], int]", variable has type "Callable[[T], bool]")
main.py:5: error: Unsupported operand types for ("T" and "bool")
main.py:5: error: Incompatible return value type (got "int", expected "bool")
The second one, Unsupported operand types for ("T" and "bool")
, shows that mypy
did bind T
and infer that x
is of type T
.
This appears to be a mypy
bug. In fact, one of the first posted comments on that bug is a question about Callable
and aliases of Callable
being treated inconsistently by mypy
.
This answers the second question:
Why do I not get the same error in the type annotation of
s1
? SinceIndicatorFunction
is an alias fors1
's type, I expected boths1
ands2
to be accepted bymypy
.
It's the above mypy
bug.
In contrast, Pyright correctly rejects s1
, complaining that Type variable "T" has no meaning in this context
-- in other words, T
is unbound.