Более чистый способ обработки событий (и также намного более быстрый, но, возможно, потребляющий немного больше памяти) состоит в том, чтобы в вашем коде было несколько функций-обработчиков событий.Что-то вроде этого:
Желаемый интерфейс
class KeyboardEvent:
pass
class MouseEvent:
pass
class NotifyThisClass:
def __init__(self, event_dispatcher):
self.ed = event_dispatcher
self.ed.add(KeyboardEvent, self.on_keyboard_event)
self.ed.add(MouseEvent, self.on_mouse_event)
def __del__(self):
self.ed.remove(KeyboardEvent, self.on_keyboard_event)
self.ed.remove(MouseEvent, self.on_mouse_event)
def on_keyboard_event(self, event):
pass
def on_mouse_event(self, event):
pass
Здесь метод __init__
получает EventDispatcher
в качестве аргумента.Функция EventDispatcher.add
теперь принимает тип интересующего вас события и слушателя.
Это имеет преимущества для эффективности, так как слушатель только когда-либо вызывается для событий, в которых он заинтересован.в более общем коде внутри самого EventDispatcher
:
EventDispatcher
Реализация
class EventDispatcher:
def __init__(self):
# Dict that maps event types to lists of listeners
self._listeners = dict()
def add(self, eventcls, listener):
self._listeners.setdefault(eventcls, list()).append(listener)
def post(self, event):
try:
for listener in self._listeners[event.__class__]:
listener(event)
except KeyError:
pass # No listener interested in this event
Но есть проблема с этой реализацией.Внутри NotifyThisClass
вы делаете это:
self.ed.add(KeyboardEvent, self.on_keyboard_event)
Проблема с self.on_keyboard_event
: это связанный метод , который вы передали EventDispatcher
.Связанные методы содержат ссылку на self
;это означает, что пока EventDispatcher
имеет связанный метод, self
не будет удален.
WeakBoundMethod
Вам нужно будет создать класс WeakBoundMethod
, который содержит толькослабая ссылка на self
(я вижу, вы уже знаете о слабых ссылках), так что EventDispatcher
не предотвращает удаление self
.
Альтернативой может быть функция NotifyThisClass.remove_listeners
, котораяВы вызываете перед удалением объекта, но на самом деле это не самое чистое решение, и я нахожу его очень подверженным ошибкам (легко забыть сделать).
Реализация WeakBoundMethod
будет выглядеть примерно так:
class WeakBoundMethod:
def __init__(self, meth):
self._self = weakref.ref(meth.__self__)
self._func = meth.__func__
def __call__(self, *args, **kwargs):
self._func(self._self(), *args, **kwargs)
Вот более надежная реализация Я разместил в CodeReview и вот пример того, как вы будете использовать класс:
from weak_bound_method import WeakBoundMethod as Wbm
class NotifyThisClass:
def __init__(self, event_dispatcher):
self.ed = event_dispatcher
self.ed.add(KeyboardEvent, Wbm(self.on_keyboard_event))
self.ed.add(MouseEvent, Wbm(self.on_mouse_event))
Connection
Objects (Необязательно))
При удалении слушателей из диспетчера / диспетчера вместо выполнения EventDispatcher
ненужного поиска среди слушателей, пока он не найдет нужный тип события, затем выполните поиск в списке, пока не найдетНапример, слушатель, у вас может быть что-то вроде этого:
class NotifyThisClass:
def __init__(self, event_dispatcher):
self.ed = event_dispatcher
self._connections = [
self.ed.add(KeyboardEvent, Wbm(self.on_keyboard_event)),
self.ed.add(MouseEvent, Wbm(self.on_mouse_event))
]
Здесь EventDispatcher.add
возвращает объект Connection
, который знает, где в списке EventDispatcher
он находится.Когда объект NotifyThisClass
удален, то же самое происходит и с self._connections
, который вызовет Connection.__del__
, что приведет к удалению слушателя из EventDispatcher
.
. Это может сделать ваш код более быстрым и простым в использовании.поскольку вам нужно только явно добавить функции, они удаляются автоматически, но вам решать, хотите ли вы это сделать.Если вы сделаете это, обратите внимание, что EventDispatcher.remove
больше не должно существовать.