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