While Jeremy Banks' answer works just fine, it's not what most would call "pythonic". Since this question comes up quite easily through search engines, here's an alternative answer that attemps to use the best conventions from my experience:
class Event:
def __init__(self):
self.listeners = []
def __iadd__(self, listener):
"""Shortcut for using += to add a listener."""
self.listeners.append(listener)
return self
def notify(self, *args, **kwargs):
for listener in self.listeners:
listener(*args, **kwargs)
To use it you simply create an Event
object and then register listener callbacks by either manipulating the listeners
list directly, or using the +=
shortcut. You then use the notify()
method to call all the listeners. Any arguments and keyword arguments passed to the notify()
method will be forwarded to the listeners.
Here's a full example:
>>> my_event = Event()
>>> def print_person_info(name, age, sex):
... print("Hello! I am {}, I'm a {}-year-old {}".format(name, age, sex))
...
>>> my_event += print_person_info
>>> my_event.notify('Markus', 23, 'male')
Hello! I am Markus, I'm a 23-year-old male
These event objects can easily be added to a class or an instance as well:
class Soldier:
# An event on a class level.
# Listening to just this will notify you of *any* person dying.
e_death = Event()
def __init__(self, name, health):
self.name = name
self.health = health
# Instance level event.
# Using this you need to listen to each person separately.
self.e_eat = Event()
def eat(self, amount):
self.health += amount
self.e_eat.notify(self, amount=amount)
def hurt(self, damage):
self.health -= damage
if self.health <= 0:
Soldier.e_death.notify(self)
Of course it's usually a bad idea to mix class and instance level events like this, I've only done if for demonstration purposes. If unsure, use the instance level events.