I wrote a higher-order python function (let's call it parent
) and its parameter function (let's call it child
) is a variadic function.
I don't know how to typehint it.
child
takes as parameter a first argument that always is a str
and a variable number of parameters that can be anything. It returns Any
.
The closest I can get to it is Callable[..., Any]
but then I "lose" the fact that the first argument is a str
.
I would like something as such Callable[[str,...], Any]
but this is not a valid typehint.
Is there a way to typehint my function?
Using a Protocol does not require you to manually wrap values in a "type-hinting wrapper", but unfortunately it doesn't help you here.
If a function can match a protocol, it's signature must exactly match that of the __call__
method in the protocol. However, (if I'm not mistaken) you want to match any function with a string as the first argument, which can be any of the following:
def fn1(x: str) -> Any: ...
def fn2(x: str, arg1: int, arg2: float) -> Any: ...
def fn3(x: str, *args: Any, **kwargs: Any) -> Any: ...
these all have different signatures, and thus cannot be matched by a single protocol: (mypy-play)
from typing import Any, Protocol
# This might be the protocol you might use, but unfortunately it doesn't work.
class StrCallable(Protocol):
def __call__(self, x: str, *args, **kwargs) -> Any:
...
def my_higher_order_fn(fn: StrCallable) -> StrCallable:
return fn # fill this with your actual implementation
my_higher_order_fn(fn1) # fails
my_higher_order_fn(fn2) # fails
my_higher_order_fn(fn3) # this passes, though
PEP 612 introduced ParamSpec
, which is what you'll need here. It's kinda like TypeVar
, but for function signatures. You can put a ParamSpec
where you'd put the first list argument in Callable
:
from typing import Callable, Concatenate, ParamSpec, TypeVar
P = ParamSpec("P")
TRet = TypeVar("TRet")
StrCallable = Callable[Concatenate[str, P], TRet]
where Concatenate
is concatenating types to an existing parameter spec. Concatenate[str, P]
is exactly what you need: any function signature whose first argument is a str
.
Unfortunately, PEP 612 won't be available until Python 3.10, and mypy does not yet fully support it either. Until then, you might have to just use Callable[..., TRet]
.