dshin
dshin

Reputation: 2398

python3 typing: extract argument list from Callable

Consider a function which wraps another function and puts the output into a singleton list:

def listify(func):
    return lambda *kargs, **kwargs: [func(*kargs, **kwargs)]

How would you type-hint this function in python3? This is my best attempt:

from typing import Callable, TypeVar
T = TypeVar('T')

def listify(func: Callable[..., T]) -> Callable[..., List[T]]:
    return lambda *kargs, **kwargs: [func(*kargs, **kwargs)]

But I'm not happy that the returned Callable does not inherit the signature of the argument types of the input Callable. Is there any way to make that happen without making assumptions on the number of arguments of func?

Upvotes: 4

Views: 1686

Answers (2)

Martijn Pieters
Martijn Pieters

Reputation: 1121524

With the release of Python 3.10, and, for older Python versions, the typing-extensions package version 3.10.0 and up, you can use a ParamSpec() construct.

A ParamSpec() object acts just like a TypeVar() object, but instead of acting as a placeholder for a single parameter, it models the combination of arguments that a callable accepts.

Use such an object here as the placeholder for the arguments:

from typing import Callable, List, TypeVar
try:
    from typing import ParamSpec
except ImportError:
    from typing_extensions import ParamSpec

P = ParamSpec("P")
T = TypeVar("T")

def listify(func: Callable[P, T]) -> Callable[P, List[T]]:
    return lambda *kargs, **kwargs: [func(*kargs, **kwargs)]

If you are stuck with Python 3.4 or older, then you can't do what you want with type hinting, as type_extensions 3.10.0 doesn’t support that Python version any more and there is no other syntax to capture the arguments part in a TypeVar generic. Callable was originally kept simple because it was really only meant to denote callback functions.

Upvotes: 1

dshin
dshin

Reputation: 2398

PEP 612 adds ParamSpec to python-3.10, which allows for:

from typing import Callable, ParamSpec, TypeVar
P = ParamSpec('P')
T = TypeVar('T')

def listify(func: Callable[P, T]) -> Callable[P, List[T]]:
    return lambda *kargs, **kwargs: [func(*kargs, **kwargs)]

Upvotes: 0

Related Questions