Foreword: this question is probably simpler than the title may suggest
I am trying to implement a simple event handler system, using decorators to 'subscribe' various methods and functions to an event.
1. Simple approach to event handlers
A simple approach would be to create a class for adding and running the event:
# class for adding and running functions, aka an event handler
class Runner:
def __init__(self):
self.functions = []
def add(self, function):
self.functions.append(function)
def run(self):
for function in self.functions:
function()
runner = Runner()
and then adding various functions or methods:
# random example function
def myFunction():
print('myFunction')
# random example class
class MyClass:
def myMethod(self):
print('myMethod')
# getting instance of class & method
myObject = MyClass()
myObjectMethod = myObject.myMethod
runner.add(myFunction)
runner.add(myObjectMethod)
runner.run()
Which leads to:
> py main.py
myFunction
myMethod
Nice! It works as expected
2. Decorator approach
So this is okay, but I've recently learned about decorators and thought I could tidy up the syntax by replacing the somewhat ugly runner.add()
method.
First I change the Runner.add()
method to work as a decorator:
class Runner:
def __init__(self):
self.functions = []
def add(self, function):
self.functions.append(function)
return function # <-- returns function
def run(self):
for function in self.functions:
function()
runner = Runner()
Then I remove the runner.add()
calls and insert the decorator:
@runner.add
def myFunction():
print('myFunction')
class MyClass:
@runner.add
def myMethod(self):
print('myMethod')
# myObject = MyClass()
# myObjectMethod = myObject.myMethod
runner.run()
Which obviously leads to:
> py main.py
myFunction
Traceback (most recent call last):
...
TypeError: myMethod() missing 1 required positional argument: 'self'
Clearly the problem here is that I'm adding the 'static' (or is it 'unbound'?) myMethod
to the event, which isn't associated with an instance and therefore doesn't have any instance of self
.
Answers such as this one refer to implementing decorators to wrap methods as they're called, however my reason for using decorators is the access you get upon initial code execution.
A thought is that something would have to run in the MyClass.__init__()
constructor, since only then has an instance been created and you have access to self
, but how then how would you run a decorator only on instantiation? Let alone to achieve the result we're after here.
The question
What options do I have for implementing behaviour shown in example 1, but with syntax as close as possible to example 2 (or any other 'clean looking' variant perhaps) due to runner.add()
being kinda verbose, ugly, and certainly not as streamlined as decorators.
Cheers!