10.2. Event Programming

  • EN: Callback Design

  • PL:

10.2.1. Metaclass

>>> class EventListener(type):
...     listeners: dict[str, list[callable]] = {}
...
...     @classmethod
...     def register(cls, *clsnames):
...         def wrapper(func):
...             for clsname in clsnames:
...                 if clsname not in cls.listeners:
...                     cls.listeners[clsname] = []
...                 cls.listeners[clsname] += [func]
...         return wrapper
...
...     def __new__(mcs, classname, bases, attrs):
...         for listener in mcs.listeners.get(classname, []):
...             listener.__call__(classname, bases, attrs)
...         return type(classname, bases, attrs)
>>>
>>>
>>> @EventListener.register('Astronaut')
... def hello_class(clsname, bases, attrs):
...     print(f'\n\nHello new class {clsname}\n')
>>>
>>>
>>> @EventListener.register('Astronaut', 'Person')
... def print_name(clsname, bases, attrs):
...     print('\nNew class created')
...     print('Classname:', clsname)
...     print('Bases:', bases)
...     print('Attrs:', attrs)
>>>
>>>
>>> class Person(metaclass=EventListener):
...     pass

New class created
Classname: Person
Bases: ()
Attrs: {'__module__': '__main__', '__qualname__': 'Person'}
>>>
>>> class Astronaut(Person, metaclass=EventListener):
...     pass


Hello new class Astronaut


New class created
Classname: Astronaut
Bases: (<class '__main__.Person'>,)
Attrs: {'__module__': '__main__', '__qualname__': 'Astronaut'}

10.2.2. Solution

class event:
    __slots__ = ('__subscribers', )

    def __init__(self):
        self.__subscribers = set()

    def call(self, *args, **kwargs):
        for subscriber in self.__subscribers:
            subscriber(*args, **kwargs)
    __call__ = call

    def register(self, function):
        self.__subscribers.add(function)
        return self
    __add__ = __iadd__ = register

    def unregister(self, function):
        self.__subscribers.remove(function)
        return self
    __sub__ = __isub__ = unregister

class EventManager:
    @staticmethod
    def register(name):
        if not hasattr(EventManager, name):
            setattr(EventManager, name, event())
        return getattr(EventManager, name).register

Usage:

@EventManager.register('on_foo')
def foo(*args, **kwargs):
    print('Args: ' + str(args), 'Kwargs: ' + str(kwargs))

def call_on_foo():
    EventManager.on_foo()
    EventManager.on_foo(1, 2, 3)
    EventManager.on_foo(a=1, b=2, c=3)
    EventManager.on_foo(1, 2, 3, a=1, b=2, c=3)

@EventManager.register('on_bar')
def bar():
    call_on_foo()

EventManager.on_bar()

Use:

EventManager.on_bar + funkcja
EventManager.on_bar += funkcja

10.2.3. Assignments

Code 10.16. Solution