8.10. Clases awaitable¶
8.10.1. Qué es un awaitable¶
Cuando una corrutina escribe await x, el lenguaje le pregunta a x cómo esperarlo. Cualquier objeto que sepa responder es awaitable. Hay dos tipos:
Los objetos corrutina que devuelven las funciones
async def. Llamar asend_request()produce uno de estos cada vez – el objeto corrutina, no el resultado de la función. Escribirawait send_request()es lo que realmente ejecuta el cuerpo.Los objetos de una clase que define un método
__await__.async defes la forma abreviada del primer tipo;__await__es la forma manual de crear el segundo tipo.
El código de la aplicación usa el primer tipo de forma predeterminada. El segundo tipo existe para el raro caso en que el propio objeto deba ser aquello que el llamador hace await, en lugar del resultado de llamar a un método sobre él.
8.10.2. Las dos formas, lado a lado¶
La misma lógica escrita como corrutina y como clase awaitable:
import asyncio
# async def -- almost always the right choice
async def yield_then_return(value):
await asyncio.sleep_ms(0)
return value
# awaitable class -- equivalent, rarely written
class YieldThenReturn:
def __init__(self, value):
self._value = value
def __await__(self):
yield # let the loop run once
return self._value # value of the ``await`` expression
Ambas pueden ser objeto de await de la misma manera:
async def main():
a = await yield_then_return(1)
b = await YieldThenReturn(2)
print(a, b) # prints: 1 2
La forma async def es más corta, se lee como Python normal y deja que el lenguaje gestione toda la contabilidad de suspensión y reanudación. La forma de clase no obtiene nada adicional en este ejemplo.
8.10.3. Cuándo se justifica la forma de clase¶
Cuando el objeto tiene que ser algo que la aplicación pasa por ahí – y no una función que el llamador invoca para obtener una corrutina – la forma de clase es la única opción:
Un valor similar a un future que la aplicación crea, entrega a un productor y luego espera con
awaitpara recuperar el resultado producido.Una primitiva personalizada construida sobre una existente donde la API natural es
await my_thingen lugar deawait my_thing.wait(). La propia claseasyncio.Taskes un ejemplo integrado – escribirawait taskfunciona porqueTaskdefine__await__.
Para cualquier cosa que encaje en la forma llamar a una función, obtener una corrutina, esperarla, async def gana.
8.10.4. Los detalles del protocolo¶
__await__ es un método normal (no async def) que devuelve un iterador. Escribirlo como generador – incluyendo al menos un yield en el cuerpo – lo convierte en uno automáticamente. Cada yield suspende la corrutina que hace await sobre el objeto y devuelve el control al bucle de eventos; el generador se reanuda la próxima vez que el bucle planifica la tarea. Un return simple (o llegar al final) finaliza la espera; lo que produzca el return se convierte en el valor de la expresión await.
En la práctica, la rara clase que hace esto delega el ceder en un awaitable existente en lugar de impulsar el bucle por sí misma:
class WaitForEvent:
def __init__(self, event):
self._event = event
def __await__(self):
yield from self._event.wait().__await__()
return self._event
El yield from reutiliza el __await__ del evento subyacente para la suspensión real. La mayoría de las aplicaciones asyncio nunca necesitan escribir ninguna de las dos formas.