Estate atento ya que en este tutorial vas a hallar el resultado que buscas.
Solución:
Paquetes de PyPI
A partir de enero de 2021, estos son los paquetes relacionados con eventos disponibles en PyPI, ordenados por fecha de lanzamiento más reciente.
- pymitter
0.3.0
: Noviembre de 2020 - zope.event
4.5.0
: Septiembre de 2020 - Python-dispatch
0.1.31
: Agosto de 2020 - RxPy3
1.0.1
: Junio de 2020 - regordete
0.13.1
: Junio de 2020 (beta) - Louie
2.0
: Septiembre de 2019 - PyPubSub
4.0.3
: Enero de 2019 - pyeventdispatcher
0.2.3a0
: 2018 - Carril de Autobús
0.0.5
: 2018 - PyPyDispatcher
2.1.2
: 2017 - axel
0.0.7
: 2016 - intermitente
1.4
: 2015 - PyDispatcher
2.0.5
: 2015 - despachador
1.0
: 2012 - py-notificar
0.3.1
: 2008
Hay más
Son muchas bibliotecas para elegir, usando terminología muy diferente (eventos, señales, manejadores, despacho de métodos, ganchos, …).
Estoy tratando de mantener una descripción general de los paquetes anteriores, además de las técnicas mencionadas en las respuestas aquí.
Primero, algo de terminología …
Patrón de observador
El estilo más básico de sistema de eventos es la “bolsa de métodos de manejo”, que es una implementación simple del patrón Observer.
Básicamente, los métodos del controlador (llamadas) se almacenan en un array y cada uno se llama cuando el evento “dispara”.
Publicar-Suscribir
La desventaja de los sistemas de eventos Observer es que solo puede registrar los controladores en el objeto de evento real (o lista de controladores). Entonces, en el momento del registro, el evento ya debe existir.
Por eso existe el segundo estilo de sistemas de eventos: el patrón de publicación-suscripción. Aquí, los manejadores no se registran en un objeto de evento (o lista de manejadores), sino en un despachador central. Además, los notificadores solo hablan con el despachador. Qué escuchar o qué publicar está determinado por ‘señal’, que no es más que un nombre (string).
Patrón de mediador
También puede ser de interés: el patrón Mediator.
Manos
Un sistema de ‘gancho’ se usa generalmente en el contexto de complementos de aplicaciones. La aplicación contiene puntos de integración fijos (ganchos) y cada complemento puede conectarse a ese gancho y realizar ciertas acciones.
Otros eventos’
Nota: threading.Event no es un ‘sistema de eventos’ en el sentido anterior. Es un sistema de sincronización de subprocesos en el que un subproceso espera hasta que otro subproceso “señala” el objeto de evento.
Las bibliotecas de mensajería en red también suelen utilizar el término “eventos”; a veces estos son similares en concepto; a veces no. Por supuesto, pueden atravesar los límites de los hilos, los procesos y las computadoras. Véase, por ejemplo, pyzmq, pymq, Twisted, Tornado, gevent, eventlet.
Referencias débiles
En Python, mantener una referencia a un método u objeto asegura que el recolector de basura no lo eliminará. Esto puede ser deseable, pero también puede provocar pérdidas de memoria: los controladores vinculados nunca se limpian.
Algunos sistemas de eventos usan referencias débiles en lugar de las regulares para resolver esto.
Algunas palabras sobre las distintas bibliotecas.
Sistemas de eventos estilo observador:
- zope.event muestra lo básico de cómo funciona esto (consulte la respuesta de Lennart). Nota: este ejemplo ni siquiera admite argumentos de controlador.
- La implementación de la ‘lista de llamadas’ de LongPoke muestra que un sistema de eventos de este tipo se puede implementar de manera muy minimalista mediante subclases
list
. - La variación EventHook de Felk también asegura las firmas de los destinatarios y llamantes.
- EventHook de spassig (Patrón de eventos de Michael Foord) es una implementación sencilla.
- La clase de eventos Valued Lessons de Josip es básicamente la misma, pero utiliza un
set
en lugar de unlist
para guardar la bolsa y los implementos__call__
que son adiciones razonables. - PyNotify es similar en concepto y también proporciona conceptos adicionales de variables y condiciones (‘evento de cambio de variable’). La página de inicio no funciona.
- axel es básicamente una bolsa de manipuladores con más funciones relacionadas con enhebrado, manejo de errores, …
- python-dispatch requiere que las clases fuente pares se deriven de
pydispatch.Dispatcher
. - buslane se basa en clases, admite controladores únicos o múltiples y facilita sugerencias de tipo extensivas.
- Observer / Event de Pithikos es un diseño ligero.
Bibliotecas de publicación y suscripción:
- blinker tiene algunas características ingeniosas, como la desconexión automática y el filtrado según el remitente.
- PyPubSub es un paquete estable y promete “funciones avanzadas que facilitan la depuración y el mantenimiento de temas y mensajes”.
- pymitter es un puerto Python de Node.js EventEmitter2 y ofrece espacios de nombres, comodines y TTL.
- PyDispatcher parece enfatizar la flexibilidad con respecto a la publicación de muchos a muchos, etc. Admite referencias débiles.
- louie es un PyDispatcher reelaborado y debería funcionar “en una amplia variedad de contextos”.
- pypydispatcher se basa en (lo adivinó …) PyDispatcher y también funciona en PyPy.
- django.dispatch es un PyDispatcher reescrito “con una interfaz más limitada, pero de mayor rendimiento”.
- pyeventdispatcher se basa en el despachador de eventos del marco Symfony de PHP.
- dispatcher se extrajo de django.dispatch pero se está volviendo bastante viejo.
- EventManger de Cristian García es una implementación realmente corta.
Otros:
- pluggy contiene un sistema de gancho que es utilizado por
pytest
complementos. - RxPy3 implementa el patrón Observable y permite fusionar eventos, reintentar, etc.
- Las señales y ranuras de Qt están disponibles en PyQt o PySide2. Funcionan como devolución de llamada cuando se usan en el mismo hilo, o como eventos (usando un bucle de eventos) entre dos hilos diferentes. Signals y Slots tienen la limitación de que solo funcionan en objetos de clases que derivan de
QObject
.
Lo he estado haciendo de esta manera:
class Event(list):
"""Event subscription.
A list of callable objects. Calling an instance of this will cause a
call to each item in the list in ascending order by index.
Example Usage:
>>> def f(x):
... print 'f(%s)' % x
>>> def g(x):
... print 'g(%s)' % x
>>> e = Event()
>>> e()
>>> e.append(f)
>>> e(123)
f(123)
>>> e.remove(f)
>>> e()
>>> e += (f, g)
>>> e(10)
f(10)
g(10)
>>> del e[0]
>>> e(2)
g(2)
"""
def __call__(self, *args, **kwargs):
for f in self:
f(*args, **kwargs)
def __repr__(self):
return "Event(%s)" % list.__repr__(self)
Sin embargo, como con todo lo demás que he visto, no hay un pydoc generado automáticamente para esto, ni firmas, lo que realmente apesta.
Usamos un EventHook como lo sugiere Michael Foord en su Event Pattern:
Simplemente agregue EventHooks a sus clases con:
class MyBroadcaster()
def __init__():
self.onChange = EventHook()
theBroadcaster = MyBroadcaster()
# add a listener to the event
theBroadcaster.onChange += myFunction
# remove listener from the event
theBroadcaster.onChange -= myFunction
# fire event
theBroadcaster.onChange.fire()
Agregamos la funcionalidad para eliminar todos los oyentes de un objeto a la clase Michaels y terminamos con esto:
class EventHook(object):
def __init__(self):
self.__handlers = []
def __iadd__(self, handler):
self.__handlers.append(handler)
return self
def __isub__(self, handler):
self.__handlers.remove(handler)
return self
def fire(self, *args, **keywargs):
for handler in self.__handlers:
handler(*args, **keywargs)
def clearObjectHandlers(self, inObject):
for theHandler in self.__handlers:
if theHandler.im_self == inObject:
self -= theHandler