I'm wondering if it is possible to type-hint a Callable
with positional- / keyword-only argument without using typing.Protocol
.
For example in this snippet - how would I type-hint x
correctly so that add
is assignable to it:
from typing import Callable
def add(arg: int, /, *, other: str) -> str:
return f"{arg}{other}"
x: Callable[[int, str], str] = add
# Type Error:
# `(arg: int, /, *, other: str) -> str` is not assignable to `(int, str) -> str`
In short no. A combination is (currently) not possible, as keyword only parameters are not possible with Callable
, as it describes positional-only parameters - you need a Protocol
for more specific typing. To quote the specs:
Parameters specified using
Callable
are assumed to be positional-only. The Callable form provides no way to specify keyword-only parameters, variadic parameters, or default argument values. For these use cases, see the section on Callback protocols.
A bit more on assignability; a function with standard parameters (keyword or positional) can be assigned to a function with any parameter type.
The reverse, if you have a function that is keyword-only / positional-only it can only be assigned to a matching type, i.e. in your case you need a Protocol
that reflects these parameter types exactly.
from typing import Callable, Protocol
class Standard(Protocol):
def __call__(self, a: int) -> None: ...
class KwOnly(Protocol):
def __call__(self, *, a: int) -> None: ...
class PosOnly(Protocol):
def __call__(self, a: int, /) -> None: ...
CallableType = Callable[[int], None]
def func(standard: Standard, kw_only: KwOnly, callable: CallableType, pos_only: PosOnly):
# Standard assignable to all
f1a: KwOnly = standard # OK
f1b: CallableType = standard # OK
f1c: PosOnly = standard # OK
# Keyword-Only assignable to keyword-only
f2a: Standard = kw_only # error
f2b: CallableType = kw_only # error
f2c: PosOnly = kw_only # error
# CallableType and PosOnly are equivalent; only assignable to position-only/Callable
f3a: Standard = callable # error
f3b: KwOnly = callable # error
f3c: PosOnly = callable # OK - as equivalent
f4a: Standard = pos_only # error
f4b: KwOnly = pos_only # error
f4c: CallableType = pos_only # OK - as equivalent
I am not aware that there are any plans to change extend Callable
in its behavior, e.g. accept keyword-only via TypedDict
s.