website/content/blog/pysubscribepattern.md
2020-04-14 08:42:49 -04:00

2.8 KiB

title date draft tags
Python Patterns: Subscribe 2020-04-14T07:53:46-04:00 false

It is common for larger applications to have modules that publishes and subscribes to events. This post will outline a couple ways to achieve this using decorators.

Single Event

First let us concern ourselves with a single event since that's the easiest. Here we will create an application class that stores callbacks of functions through the subscribe decorator. Calling emit will send a message to all the functions stored in self.callbacks.

class Application:
    def __init__(self):
        self.callbacks = []
    def subscribe(self, func):
        self.callbacks.append(func)
        return func
    def emit(self, message):
        for callback in self.callbacks:
            callback(message)

Here is an example of its usage:

app = Application()

@app.subscribe
def test1(message):
    print("Function 1:", message)

@app.subscribe
def test2(message):
    print("Function 2:", message)

app.emit('Hello World')
Function 1: Hello World
Function 2: Hello World

Multiple Events

Let's say you want the application to handle different types of events. Now self.callbacks is a dictionary of lists, where the key is the event and the list is the same as the last section. There's an additional layered function on top of subscribe this time in order to handle passing an argument into the decorator.

from collections import defaultdict

class Application:
    def __init__(self):
        self.callbacks = defaultdict(list)
    def on(self, event):
        def subscribe(func):
            self.callbacks[event].append(func)
            return func
        return subscribe
    def emit(self, event, message):
        for callback in self.callbacks[event]:
            callback(message)

To show its usage lets first create an instance of Application

app = Application()

Now let's subscribe a couple functions to event1

@app.on('event1')
def test1(message):
    print("Function 1:", message)

@app.on('event1')
def test3(message):
    print("Function 3:", message)

Now to subscribe a couple events to event2

# Subscribed to event 2
@app.on('event2')
def test2(message):
    print("Function 2:", message)

@app.on('event2')
def test4(message):
    print("Function 4:", message)

We can also subscribe to both events

# Subscribed to both events
@app.on('event1')
@app.on('event2')
def test5(message):
    print("Function 5:", message)
app.emit('event1', 'Hello, World!')
Function 1: Hello, World!
Function 3: Hello, World!
Function 5: Hello, World!
app.emit('event2', 'Goodbye, World!')
Function 2: Goodbye, World!
Function 4: Goodbye, World!
Function 5: Goodbye, World!