I am using ParamSpec
and Concatenate
to type hint callables.
Here's the code
from collections.abc import Callable
from typing import Concatenate, Generic, ParamSpec, TypeVar
T0 = TypeVar("T0")
P1 = ParamSpec("P1")
T1 = TypeVar("T1")
class TestClass(Generic[T0]):
def __init__(self, obj: T0, method: Callable[Concatenate[T0, P1], T1]) -> None:
self.obj = obj
self.method = method
P2 = ParamSpec("P2")
T2 = TypeVar("T2")
def test_function(func: Callable[P2, T2], *args: P2.args, **kwargs: P2.kwargs) -> T2:
return func(*args, **kwargs)
t = TestClass(1, lambda i: i)
print(test_function(t.method, t.obj))
And the mypy errors:
file.py:26: error: Argument 1 to "test_function" has incompatible type "Callable[[int, **P1], T1]"; expected "Callable[[int, **P1], T1]"
file.py:26: error: Argument 2 to "test_function" has incompatible type "int"; expected "[int, **P1.args]"
What I'm trying to do is storing a function that must accept T0
as first arg and passing it to a function that accepts anything
CodePudding user response:
The problem is that you've lost P1
and T1
information after __init__
, it is not attached to your class. Using them in Generic
solves the issue:
from collections.abc import Callable
from typing import Concatenate, Generic, ParamSpec, TypeVar
T0 = TypeVar("T0")
P1 = ParamSpec("P1")
T1 = TypeVar("T1")
class TestClass(Generic[T0, P1, T1]):
def __init__(self, obj: T0, method: Callable[Concatenate[T0, P1], T1]) -> None:
self.obj = obj
self.method = method
P2 = ParamSpec("P2")
T2 = TypeVar("T2")
def test_function(func: Callable[P2, T2], *args: P2.args, **kwargs: P2.kwargs) -> T2:
return func(*args, **kwargs)
def mth(i: int) -> int: # Instead of lambda, to avoid unexpected Any
return i
t = TestClass(1, mth)
print(test_function(t.method, t.obj))
(playground).
Strictly speaking, you could expect mypy
to type method
after resolving type vars, but it does never happen (in any context). It would lead to ambiguous resolution: should it be Callable[[int], int]
? Callable[[T0], int]
? Callable[[T0], T0]
? Something else? (I cannot even say which of these I'd personally prefer, it is very hard to define non-ambiguous, you'll have to keep in mind Generic/Protocol, bound typevars in Callable
itself and in outer method, etc.)