Twisted contiene large number of examples. Uno en particular, el "evolution of Finger" tutorial, contiene una explicación detallada de cómo un programa asíncrono crece desde un kernel muy pequeño hasta un sistema complejo con muchas partes móviles. Otro que podría interesarle es el tutorial sobre simplemente writing servers.
La clave a tener en cuenta sobre Twisted, o incluso otras bibliotecas de redes asíncronas (tales como asyncore, MINA o ACE), es que su código sólo se invoca cuando ocurre algo. La parte que he escuchado más a menudo suena como "vudú" es la gestión de devoluciones de llamadas: por ejemplo, Deferred
. Si está acostumbrado a escribir código que se ejecuta en línea recta y solo llama a funciones que regresan inmediatamente con resultados, la idea de esperar que algo le devuelva la llamada podría ser confusa. Pero no hay nada mágico, no hay "vudú" sobre devoluciones de llamadas. En el nivel más bajo, el reactor es simplemente sentarse alrededor y esperar a que uno de un pequeño número de cosas que suceden:
- Los datos llegan en una conexión (se llamará
dataReceived
en un Protocolo)
- El tiempo ha pasado (llamará a una función registrada con
callLater
).
- Se ha aceptado una conexión (llamará a
buildProtocol
en una fábrica registrada con una función listenXXX
o connectXXX
).
- Una conexión se ha caído (se llamará
connectionLost
en el protocolo correspondiente)
Cada programa comienza asíncrono mediante la conexión de algunos de estos eventos y luego dar comienzo al reactor para esperar a que sucedan. Por supuesto, los eventos que suceden llevan a más eventos que se conectan o desconectan, por lo que su programa sigue su camino feliz. Más allá de eso, no hay nada especial acerca de la estructura del programa asíncrono que sea interesante o especial; los controladores de eventos y las devoluciones de llamada son solo objetos, y su código se ejecuta de la manera habitual.
Aquí hay un simple "motor de eventos" que muestra lo simple que es este proceso.
# Engine
import time
class SimplestReactor(object):
def __init__(self):
self.events = []
self.stopped = False
def do(self, something):
self.events.append(something)
def run(self):
while not self.stopped:
time.sleep(0.1)
if self.events:
thisTurn = self.events.pop(0)
thisTurn()
def stop(self):
self.stopped = True
reactor = SimplestReactor()
# Application
def thing1():
print 'Doing thing 1'
reactor.do(thing2)
reactor.do(thing3)
def thing2():
print 'Doing thing 2'
def thing3():
print 'Doing thing 3: and stopping'
reactor.stop()
reactor.do(thing1)
print 'Running'
reactor.run()
print 'Done!'
En el núcleo de bibliotecas como Twisted, la función en el bucle principal no es sleep
, sino una llamada de sistema operativo como select()
o poll()
, como expuesto por un módulo como the Python select module. Digo "me gusta" select
, porque esta es una API que varía mucho entre las plataformas, y casi todas las herramientas GUI tienen su propia versión. Twisted actualmente proporciona una interfaz abstracta para 14 variaciones diferentes sobre este tema. Lo común que ofrece una API así es proporcionar una forma de decir "Aquí hay una lista de eventos que estoy esperando. Dormir hasta que uno de ellos suceda, luego despertar y decirme cuál de ellos fue."
Espero que estés teniendo mejor suerte con Twisted. Actualmente es uno de mis frameworks favoritos. – Dustin