Programacion orientada a eventos (un estudio practico)

Chema Cortes py en ch3m4.org
Lun Mar 15 17:47:40 CET 2004


He estado estudiando el paradigma de la "programación orientada a 
eventos" y cómo implementarlo desde python, y os expongo aquí mis 
resultados:

Para los que conozcáis VB (a nivel medio-avanzado) sabréis que se pueden 
crear nuevos "eventos" a los que asociar "oyentes ("Listeners") que los 
escuchan y procesan. Esto es, más o menos, parecido a la programación de 
eventos que tiene cualquier interface gráfico.

La ventaja de este paradigma es que permite desacoplar la parte que 
genera los eventos de la parte que los escucha, de modo que es muy fácil 
hacer trozos de código independientes que funcionen en paralelo 
(normalmente multihilo) que responden simultáneamente a un mismo evento.

El problema que encontraba para implementar este paradigma es el 
conocido en python de que por guardar las referencias de los objetos, el 
objeto siempre estaba "vivo" y nunca era borrado por el recolector de 
basura, con lo que se hacía imposible obtener un desacople completo 
entre "eventos" y "oyentes".

Este problema es bastante corriente en python (posiblemente, muchos de 
los errores "extraños" con toolkits gráficos como el Tkinter provenga de 
estos objetos "siempre-vivos"). Para evitar este problema se 
introdujeron las "referencias débiles" ("weakrefs"). Estas son una 
referencias que, como indica su propio nombre, no fuerzan a que el 
objeto esté siempre presente. Esto se consigue obligando a acceder a él 
a través de una indirección (mirar la documentación del módulo "weakref").

De todo esto he creado el siguiente programilla de ejemplo donde aplico 
la programación orienta a eventos. Cualquier comentario será bienvenido:


#!/usr/bin/env python
# -*- encoding: iso8859-15 -*-

#Demostración de programación orientada a eventos
#
#La técnica emplea "referencias débiles" (weakref)
#con lo que al saltar un evento no hay problema si
#el objeto oyente (listener) ya no existe

import weakref

class Listener(object):
     def DoEvent(self,evt):pass

class Event(object):

     def __init__(self,name,listeners=None):
         self.name=name
         self.listeners=[]
         if listeners:
             self.Register(listeners)

     def Register(self,lst):
         #quitamos morralla (referencias inválidas)
         self.listeners=[l for l in self.listeners if l()!=None]

         if isinstance(lst,Listener):
             self.listeners.append(weakref.ref(lst))
         elif isinstance(lst,(list,tuple)):
             L=[weakref.ref(l) for l in lst if isinstance(l,Listener)]
             self.listeners.extend(L)

     def Cancel(self,lst):
         l=weakref.ref(lst)
         if l in self.listeners:
             self.listeners.remove(l)

     def CancelAll(self):
         self.listeners=[]

     def Raise(self):
         for lst in self.listeners:
             ref=lst()  #weakref handle
             if ref:
                 ref.DoEvent(self)


if __name__=="__main__":

     class MyListener(Listener):
         def __init__(self,name):
             self.name=name
         def DoEvent(self,evt):
             print "\tOyente '%s' escuchando evento '%s'" % \
                   (self.name,evt.name)

     l1=MyListener("Primero")
     l2=MyListener("Segundo")

     alarm=Event("Alarma", (l1,l2))

     #alternativamente podríamos haber puesto
     #~ alarm.Register(l1)
     #~ alarm.Register(l2)

     print "LANZANDO PRIMER EVENTO..."
     alarm.Raise()

     #borramos el segundo escucha.
     #normalmente se suele cancelar con:
     # alarm.Cancel(l2)
     del l2

     print "LANZANDO SEGUNDO EVENTO..."
     alarm.Raise()

     #cancelamos todas las escuchas
     alarm.CancelAll()

     print "LANZANDO TERCER EVENTO..."
     alarm.Raise() #no hace nada, por no existir oyentes


------------ próxima parte ------------
_______________________________________________
Python-es mailing list
Python-es en aditel.org
http://listas.aditel.org/listinfo/python-es


Más información sobre la lista de distribución Python-es