pythonfunctionpython-typingpython-3.10

In Python, how do you annotate a function that accepts a fixed argument as well as any number of other arguments?


In Python, from my understanding, the ellipsis lets you annotate a function that has any number of arguments (documentation found here). Here's an example of what I'd like to do:

from typing import Callable, Any

def foo(first: str, *rest: Any):
    print(rest)
    return first

def call(f: Callable[[str, ...], str]):
    f("Hello", 1, None, True)

print(call(foo))

Python (or, at least, Pylance) doesn't like the ellipsis ("..." not allowed in this context): Sceenshot of Pylance hint

I've tried to use Python 3.10's ParamSpecs, but the documentation on them (including PEP 612) seems to say they're used for other purposes, and I can't tell what those purposes are. Here's what I've tried:

from typing import Any, Callable, Concatenate, ParamSpec

P = ParamSpec("P")

def foo(first: str, *rest: Any):
    print(rest)
    return first

def call(f: Callable[Concatenate[str, P], str]):
    f("Hello", 1, None, True)

print(call(foo))

Python (or, at least, Pylance) seems to reflect that they aren't meant to be used this way: Screenshot of Pylance hint

How do I annotate a function like this, that knows the type of one or more of its arguments and accepts any number of other arguments anyway?


Solution

  • You can type arbitrary function signatures using __call__ on a Protocol

    class Foo(Protocol):
        def __call__(self, first: str, *rest: Any) -> str:
            ...
    
    def call(f: Foo):
        f("Hello", 1, None, True)