'How can decorators be used for callback recognition?
I am implementing a system that uses callbacks for event handling. I currently register the callbacks in a dictionary in each module. For example, see below:
class Module(ABC):
topics = {}
_eventHandler = None
def __init__(self):
pass
def onNotify(self, event):
self.topics[event.topic](event.data)
class Reporter(Module):
def __init__(self):
self.topics = {'time':self.onMsg,
'data':self.onMsg}
def onMsg(self, msg):
print(f'[reporter] {msg}')
def update(self):
pass
A collection of Modules is registered in an EventHandler, as seen below:
class EventHandler(queue.Queue):
def __init__(self):
super().__init__()
self._modules = []
@property
def modules(self):
return self._modules
def attach(self, module):
self._modules.append(module)
module._eventHandler = self
def detach(self, module):
self._modules.append(module)
module._eventHandler = None
def notify(self, event):
for module in self.modules:
if event.topic in module.topics.keys():
module.onNotify(event)
def run(self):
idx = 0
while True:
while not self.empty():
event = self.get_nowait()
self.notify(event)
else:
self.modules[idx].update()
idx = (idx + 1) % len(self._modules)
I do not like that there is a separate list to denote callbacks. It seems that the Python developers agree, as PEP 318 states the following in support of decorators: "It also seems less than pythonic to name the function three times for what is conceptually a single declaration."
How can I use decorators to register functions as callbacks that are recognized by the EventHandler, without having a separate list to use as a directory. An example of my desired formatting for the modules is below:
class Module(ABC):
topics = {}
_eventHandler = None
def __init__(self):
pass
def onNotify(self, event):
'''Find the correct callback for event.topic, and direct the event'''
class Reporter(Module):
@callback(topic = ['time', 'data'])
def onMsg(self, msg):
print(f'[reporter] {msg}')
def update(self):
pass
and the EventHandler could use a method like:
def notify(self, event):
for module in self.modules:
if event.topic in module.callbacks:
module.onNotify(event)
Solution 1:[1]
I have determined that using decorators for this would add confusion, and is not a pythonic solution. See my new sample code below that shows an alternate solution.
import time
import threading
class Event():
def __init__(self, topic, payload):
self.topic = topic
self.payload = payload
class EventAggregator():
def __init__(self):
self.subscriber = {}
def publish(self, event):
if event.topic in self.subscriber.keys():
filtered_callbacks = self.subscriber[event.topic]
for callback in filtered_callbacks:
callback(event)
def subscribe(self, topic, callback):
if not topic in self.subscriber.keys():
self.subscriber[topic] = [callback]
else:
self.subscriber[topic].append(callback)
class Reporter():
def __init__(self, event_aggregator):
self._event_aggregator = event_aggregator
self._event_aggregator.subscribe('data', self.onData)
self._event_aggregator.subscribe('time', self.onTime)
def onData(self, event):
print(f'[reporter] (data):{event.payload}')
def onTime(self, event):
print(f'[reporter] (time):{event.payload}')
class Clock():
def __init__(self, event_aggregator):
self._event_aggregator = event_aggregator
threading.Thread(target=self.clockProcess).start()
def clockProcess(self):
while True:
t = time.time()
if round(t % 5) == 0:
event = Event('time', t)
self.publish(event)
time.sleep(1)
def publish(self, event):
self._event_aggregator.publish(event)
if __name__ == '__main__':
egg = EventAggregator()
reporter = Reporter(egg)
clock = Clock(egg)
event = Event('data', 5)
egg.publish(event)
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Deemo |
