-
-
Save Midnighter/e0051e00e3467f447f487a7f399334d7 to your computer and use it in GitHub Desktop.
Add C# += / -= event subscriptions to Python using the Events package
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import functools | |
from events import Events # Note you must pip install events | |
class set_event: | |
"""Define a class-based setter method decorator.""" | |
def __init__(self, func, event): | |
super().__init__() | |
functools.update_wrapper(self, func) | |
self._func = func | |
self._event = event | |
self._last = None | |
def __call__(self, *args, **kwargs): | |
if len(args) == 2: | |
attr = args[1] | |
elif "value" in kwargs: | |
attr = kwargs["value"] | |
else: | |
raise RuntimeError("Not a simple property assignment.") | |
self._func(*args, **kwargs) | |
if attr != self._last: | |
self._event(args[0]) | |
self._last = attr | |
def add_events(fields: dict): | |
"""Define a class decorator with arguments.""" | |
def decorator(klass): | |
original_init = klass.__init__ | |
@functools.wraps(original_init) | |
def init(self, *args, **kwargs): | |
original_init(self, *args, **kwargs) | |
self.__dict__["_events"] = events = Events(tuple(fields.values())) | |
for event_name in fields.values(): | |
self.__dict__[event_name] = getattr(events, event_name) | |
for field, event_name in fields.items(): | |
class_field = type(self).__dict__[field] | |
# Attempt to decorate the instance property... | |
self.__dict__[field] = property( | |
class_field.fget, | |
set_event(class_field.fset, getattr(events, event_name)), | |
class_field.fdel, | |
) | |
klass.__init__ = init | |
return klass | |
return decorator | |
@add_events( | |
{"age": "on_age_changed", "name": "on_name_changed", "city": "on_location_changed"} | |
) | |
class Person: | |
def __init__(self, name: str, age: int, city: str): | |
self.__name = name | |
self.__age = age | |
self.__city = city | |
@property | |
def age(self): | |
return self.__age | |
@age.setter | |
def age(self, value): | |
self.__age = value | |
@property | |
def name(self): | |
return self.__name | |
@name.setter | |
def name(self, value): | |
self.__name = value | |
@property | |
def city(self): | |
return self.__city | |
@city.setter | |
def city(self, value): | |
self.__city = value | |
def main(): | |
p1 = Person("Michael", 47, "Portland") | |
p2 = Person("Zoe", 50, "Orlando") | |
p1.on_name_changed += lambda p: print(f"Person changed name to {p.name}.") | |
p1.on_location_changed += location_changed | |
p2.on_location_changed += location_changed | |
# Two moves for Zoe | |
p2.city = "Seattle" | |
p2.city = "LA" | |
# One move for Michael | |
p1.city = "Vancouver" | |
# One name change for Michael to Mike | |
p1.name = "Mike" | |
# No longer watching for moves for Michael | |
p1.on_location_changed -= location_changed | |
p1.city = "Portland" | |
def location_changed(person: Person): | |
print(f"Looks like {person.name} has moved to {person.city}") | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment