Reputation: 955
I'm trying to Type Hint the function bar
, but I got the Too few arguments
error when I run mypy.
from typing import Callable, Optional
def foo(arg: int = 123) -> float:
return arg+0.1
def bar(foo: Callable[[int], float], arg: Optional[int] = None) -> float:
if arg:
return foo(arg)
return foo()
print(bar(foo))
print(bar(foo, 90))
I have also tried:
Callable[[], float]
(got Too many arguments
error)Callable[[Optional[int]], float]
(got another error)So, how should I do the Type Hinting of the bar
function?
Upvotes: 47
Views: 25081
Reputation: 23381
Since an integer can be optionally passed to foo()
, we can use the @overload
decorator here. Since there are two possibilities, we need two overloads. The first overload type-checks bar(foo, arg)
for the case where some integer is passed to foo()
. The second overload is type-checking bar(foo, arg)
for the case where nothing is passed to foo()
. After the overloads, we can define bar()
without type hints.
from typing import Callable, Optional, overload
def foo(arg: int = 123) -> float:
return arg+0.1
@overload
def bar(foo: Callable[[int], float], arg: Optional[int] = None) -> float: ...
@overload
def bar(foo: Callable[[], float], arg: Optional[int] = None) -> float: ...
def bar(foo, arg=None):
if arg:
return foo(arg)
return foo()
print(bar(foo))
print(bar(foo, 90))
Then, for example on VS Code, you can see the hint for bar()
function as follows. You can see that foo
is a callable to which you can pass an integer or not.
Tested on mypy==1.8.0.
Upvotes: 1
Reputation: 5907
Define this:
class Foo(Protocol):
def __call__(self, x: int = ..., /) -> float:
...
then type hint foo
as Foo
instead of Callable[[int], float]
. Callback protocols allow you to:
define flexible callback types that are hard (or even impossible) to express using the
Callable[...]
syntax
and optional arguments are one of those impossible things to express with a normal Callable
. The /
at the end of __call__
's signature makes x
a positional-only parameter, which allows any passed function to bar
to have a parameter name that is not x
(your specific example of foo
calls it arg
instead). If you removed /
, then not only would the types have to line up as expected, but the names would have to line up too because you would be implying that Foo
could be called with a keyword argument. Because bar
doesn't call foo
with keyword arguments, opting into that behavior by omitting the /
imposes inflexibility on the user of bar
(and would make your current example still fail because "arg" != "x"
).
Upvotes: 58