tamaño - stringvar python
Devoluciones de llamada y generadores asíncronos de Python (3)
ACTUALIZACIÓN : tome esto con un grano de sal, ya que estoy fuera de contacto con los desarrollos asincrónicos modernos de Python, incluyendo gevent y asyncio y en realidad no tengo una experiencia seria con el código asíncrono.
Hay 3 enfoques comunes para la codificación asincrónica sin hilos en Python:
Devolución de llamada: feo pero viable, Twisted lo hace bien.
Generadores: agradable, pero requieren todo su código para seguir el estilo.
Utilice la implementación de Python con tasklets reales: Stackless (RIP) y Greenlet .
Desafortunadamente, idealmente todo el programa debería usar un estilo o las cosas se vuelven complicadas. Si está de acuerdo con que su biblioteca exponga una interfaz totalmente síncrona, probablemente esté bien, pero si quiere que varias llamadas a su biblioteca trabajen en paralelo, especialmente en paralelo con otro código asíncrono, entonces necesita un "reactor" de evento común que puede trabajar con todo el código.
Entonces, si tiene (o espera que el usuario tenga) otro código asíncrono en la aplicación, probablemente sea inteligente adoptar el mismo modelo.
Si no quieres entender todo el lío, considera usar malos viejos hilos. También son feos, pero trabajan con todo lo demás.
Si quieres entender cómo las corutinas pueden ayudarte, y cómo pueden complicarte, el "Curso curioso sobre corutinas y concurrencia" de David Beazley es algo bueno.
Greenlets podría ser la forma más limpia si puedes usar la extensión. No tengo ninguna experiencia con ellos, así que no puedo decir mucho.
Estoy intentando convertir una biblioteca síncrona para usar un marco IO asíncrono interno. Tengo varios métodos que se ven así:
def foo:
....
sync_call_1() # synchronous blocking call
....
sync_call_2() # synchronous blocking call
....
return bar
Para cada una de las funciones sincrónicas ( sync_call_*
), he escrito una función asíncrona correspondiente que toma una devolución de llamada. P.ej
def async_call_1(callback=none):
# do the I/O
callback()
Ahora, para la pregunta de novato de python: ¿cuál es la forma más fácil de traducir los métodos existentes para usar estos nuevos métodos asíncronos? Es decir, el método foo()
anterior ahora debe ser:
def async_foo(callback):
# Do the foo() stuff using async_call_*
callback()
Una opción obvia es pasar una devolución de llamada a cada método asíncrono que efectivamente "reanude" la función de llamada "foo", y luego llame a la devolución de llamada global al final del método. Sin embargo, eso hace que el código sea frágil, feo y necesitaría agregar una nueva devolución de llamada para cada llamada a un método async_call_*
.
¿Hay una manera fácil de hacerlo utilizando un modismo de Python, como un generador o coroutine?
Debes hacer que la función foo
también sea asincrónica. ¿Qué hay de este enfoque?
@make_async
def foo(somearg, callback):
# This function is now async. Expect a callback argument.
...
# change
# x = sync_call1(somearg, some_other_arg)
# to the following:
x = yield async_call1, somearg, some_other_arg
...
# same transformation again
y = yield async_call2, x
...
# change
# return bar
# to a callback call
callback(bar)
Y make_async
se puede definir así:
def make_async(f):
"""Decorator to convert sync function to async
using the above mentioned transformations"""
def g(*a, **kw):
async_call(f(*a, **kw))
return g
def async_call(it, value=None):
# This function is the core of async transformation.
try:
# send the current value to the iterator and
# expect function to call and args to pass to it
x = it.send(value)
except StopIteration:
return
func = x[0]
args = list(x[1:])
# define callback and append it to args
# (assuming that callback is always the last argument)
callback = lambda new_value: async_call(it, new_value)
args.append(callback)
func(*args)
PRECAUCIÓN: No he probado esto
Hay varias formas de tareas de multiplexación. No podemos decir qué es lo mejor para su caso sin un conocimiento más profundo de lo que está haciendo. Probablemente la forma más fácil / universal es usar hilos. Eche un vistazo a esta pregunta para algunas ideas.