I am currently writing a system using events and event listeners and am running into a problem correctly typing the function signatures of the listeners. The relevant part of the program looks similar to
from typing import Hashable, Callable
from collections import defaultdict
class System:
def __init__(self):
self.event_listeners = defaultdict(list)
def add_event_listener(self, event: Hashable, listener: Callable):
self.event_listeners[event].append(listener)
def emit_event(self, event: Hashable, *args, **kwargs):
for listener in self.event_listeners[event]:
listener(self, *args, **kwargs)
...
In addition to the instance of System
itself, event related information can be passed to the event listeners as further arguments and the type of information (number and types of arguments) is dependent on the specific event.
Is there a way to type the defaultdict
instance and the function add_event_listener
to reflect that for every event there is a specific function signature Callable[[System, ...], object]
, that is expected? I thought about how this can be achieved using ParamSpec
's but have not found a solution so far.
Give typing.Protocol
a shot.
from __future__ import annotations
from typing import Any, Hashable, Protocol
from collections import defaultdict
class EventListener(Protocol):
def __call__(self, system: System, *args: Any, **kwds: Any) -> Any:
...
class System:
def __init__(self) -> None:
self.event_listeners: defaultdict[Hashable, list[EventListener]] = defaultdict(
list
)
def add_event_listener(self, event: Hashable, listener: EventListener) -> None:
self.event_listeners[event].append(listener)
def emit_event(self, event: Hashable, *args: Any, **kwargs: Any) -> None:
for listener in self.event_listeners[event]:
listener(self, *args, **kwargs)
I'm answering the question in your title Type hinting callback functions
Use Callable
(from typing import Callable
).
Callable[[<PARAMETER TYPES>], <RETURN TYPE>]
def call(callback: Callable[[], int]) -> int:
return callback()
def get1():
return 1
def getA():
return 'A'
call(get1) # ok
call(getA) # not ok
def foo(): pass
has type Callable[[], None]
def foo(): return 1
has type Callable[[], int]
def foo(): return '1'
has type Callable[[], str]
def foo(a: int): return '1'
has type Callable[[int], str]
def foo(a: int, b: str): return '1'
has type Callable[[int, str], str]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With