I'm working on my first Python project, and I'm already missing events in my classes. Perhaps it's not even called events in Python, but I would like to create "groups" in my classes to which function references can be added. At some point in my class all function references in my group would execute.
Is this built into Python? (I'm using 2.7 at the moment)
Python doesn't have any sort of event system built-in, but it's could be implemented pretty simply. For example:
class ObjectWithEvents(object):
callbacks = None
def on(self, event_name, callback):
if self.callbacks is None:
self.callbacks = {}
if event_name not in self.callbacks:
self.callbacks[event_name] = [callback]
else:
self.callbacks[event_name].append(callback)
def trigger(self, event_name):
if self.callbacks is not None and event_name in self.callbacks:
for callback in self.callbacks[event_name]:
callback(self)
class MyClass(ObjectWithEvents):
def __init__(self, contents):
self.contents = contents
def __str__(self):
return "MyClass containing " + repr(self.contents)
def echo(value): # because "print" isn't a function...
print value
o = MyClass("hello world")
o.on("example_event", echo)
o.on("example_event", echo)
o.trigger("example_event") # prints "MyClass containing \"Hello World\"" twice
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With