python typing signature (typing.Callable) for function with kwargs
Question:
I heavily use python typing support from python 3.
Recently I was trying to pass a function as an argument and I do not find any help for using kwargs
in typing.Callable
signature.
Please check the code below and the comments.
import typing
# some function with singnature typing
def fn1_as_arg_with_kwargs(a: int, b: float) -> float:
return a + b
# some function with singnature typing
def fn2_as_arg_with_kwargs(a: int, b: float) -> float:
return a * b
# function that get callables as arg
# this works with typing
def function_executor(
a: int,
b: float,
fn: typing.Callable[[int, float], float]):
return fn(a, b)
# But what if I want to name my kwargs
# (something like below which does not work)
# ... this will help me more complex scenarios
# ... or am I expecting a lot from python3 ;)
def function_executor(
a: int,
b: float,
fn: typing.Callable[["a": int, "b": float], float]):
return fn(a=a, b=b)
Answers:
You are probably looking for Callback protocols.
In short, when you want to express a callable with a complex signature, what you’ll want to do is to create a custom Protocol that defines a __call__
method with the precise signature you want.
For example, in your case:
from typing import Protocol
# Or, if you want to support Python 3.7 and below, install the typing_extensions
# module via pip and do the below:
from typing_extensions import Protocol
class MyCallable(Protocol):
def __call__(self, a: int, b: float) -> float: ...
def good(a: int, b: float) -> float: ...
def bad(x: int, y: float) -> float: ...
def function_executor(a: int, b: float, fn: MyCallable) -> float:
return fn(a=a, b=b)
function_executor(1, 2.3, good) # Ok!
function_executor(1, 2.3, bad) # Errors
If you try type-checking this program using mypy, you’ll get the following (admittedly cryptic) error on the last line:
Argument 3 to "function_executor" has incompatible type "Callable[[int, float], float]"; expected "MyCallable"
(Callback protocols are somewhat new, so hopefully the quality of the error messages will improve over time.)
I found the example with the typing callback a bit complicated. For anyone looking for a simple example of typing a function with kwargs:
from typing import Protocol
class MyCallable(Protocol):
# Define types here, as if __call__ were a function (ignore self).
def __call__(self, a: int, b: int) -> int:
...
# Generic function- types correspond to MyCallable.__call__ args.
def func_add(a: int, b: int) -> int:
return a + b
# Assign the function to a variable called my_function, and add the type.
my_function: MyCallable = func_add
my_function(a=1, b=2) # This is OK.
my_function(a=1, b="x") # This is NOK.
I heavily use python typing support from python 3.
Recently I was trying to pass a function as an argument and I do not find any help for using kwargs
in typing.Callable
signature.
Please check the code below and the comments.
import typing
# some function with singnature typing
def fn1_as_arg_with_kwargs(a: int, b: float) -> float:
return a + b
# some function with singnature typing
def fn2_as_arg_with_kwargs(a: int, b: float) -> float:
return a * b
# function that get callables as arg
# this works with typing
def function_executor(
a: int,
b: float,
fn: typing.Callable[[int, float], float]):
return fn(a, b)
# But what if I want to name my kwargs
# (something like below which does not work)
# ... this will help me more complex scenarios
# ... or am I expecting a lot from python3 ;)
def function_executor(
a: int,
b: float,
fn: typing.Callable[["a": int, "b": float], float]):
return fn(a=a, b=b)
You are probably looking for Callback protocols.
In short, when you want to express a callable with a complex signature, what you’ll want to do is to create a custom Protocol that defines a __call__
method with the precise signature you want.
For example, in your case:
from typing import Protocol
# Or, if you want to support Python 3.7 and below, install the typing_extensions
# module via pip and do the below:
from typing_extensions import Protocol
class MyCallable(Protocol):
def __call__(self, a: int, b: float) -> float: ...
def good(a: int, b: float) -> float: ...
def bad(x: int, y: float) -> float: ...
def function_executor(a: int, b: float, fn: MyCallable) -> float:
return fn(a=a, b=b)
function_executor(1, 2.3, good) # Ok!
function_executor(1, 2.3, bad) # Errors
If you try type-checking this program using mypy, you’ll get the following (admittedly cryptic) error on the last line:
Argument 3 to "function_executor" has incompatible type "Callable[[int, float], float]"; expected "MyCallable"
(Callback protocols are somewhat new, so hopefully the quality of the error messages will improve over time.)
I found the example with the typing callback a bit complicated. For anyone looking for a simple example of typing a function with kwargs:
from typing import Protocol
class MyCallable(Protocol):
# Define types here, as if __call__ were a function (ignore self).
def __call__(self, a: int, b: int) -> int:
...
# Generic function- types correspond to MyCallable.__call__ args.
def func_add(a: int, b: int) -> int:
return a + b
# Assign the function to a variable called my_function, and add the type.
my_function: MyCallable = func_add
my_function(a=1, b=2) # This is OK.
my_function(a=1, b="x") # This is NOK.