8.10. Classes aguardáveis¶
8.10.1. O que é um aguardável¶
Quando uma corrotina escreve await x, a linguagem pergunta a x como esperar por ele. Qualquer objeto que saiba responder é aguardável. Há dois tipos:
Os objetos corrotina que as funções
async defretornam. Chamarsend_request()produz um desses a cada vez – o objeto corrotina, não o resultado da função. Escreverawait send_request()é o que de fato executa o corpo.Objetos de uma classe que define um método
__await__.async defé a forma abreviada do primeiro tipo;__await__é a maneira manual de criar o segundo tipo.
O código da aplicação usa o primeiro tipo por padrão. O segundo tipo existe para o caso raro em que o próprio objeto precisa ser aquilo que o chamador awaits, e não o resultado de chamar um método sobre ele.
8.10.2. As duas formas lado a lado¶
A mesma lógica escrita como uma corrotina e como uma classe aguardável:
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 podem ser awaited da mesma forma:
async def main():
a = await yield_then_return(1)
b = await YieldThenReturn(2)
print(a, b) # prints: 1 2
A forma async def é mais curta, lê-se como Python normal e deixa a linguagem gerenciar toda a contabilidade de suspensão e retomada. A forma de classe não ganha nada a mais neste exemplo.
8.10.3. Quando a forma de classe vale a pena¶
Quando o objeto precisa ser algo que a aplicação repassa por aí – e não uma função que o chamador invoca para obter uma corrotina – a forma de classe é a única opção:
Um valor semelhante a um future que a aplicação cria, entrega a um produtor e
awaits mais tarde para recuperar o resultado produzido.Uma primitiva personalizada construída sobre uma existente, em que a API natural é
await my_thingem vez deawait my_thing.wait(). A própria classeasyncio.Taské um exemplo embutido – escreverawait taskfunciona porqueTaskdefine__await__.
Para qualquer coisa que se encaixe no formato chamar uma função, obter uma corrotina, aguardá-la, async def vence.
8.10.4. Os detalhes do protocolo¶
__await__ é um método regular (não async def) que retorna um iterador. Escrevê-lo como um gerador – incluindo ao menos um yield no corpo – o torna um automaticamente. Cada yield suspende a corrotina que awaits o objeto e devolve o controle ao event loop; o gerador retoma na próxima vez que o loop agendar a tarefa. Um return simples (ou chegar ao fim) finaliza a espera; o que quer que o return produza torna-se o valor da expressão await.
Na prática, a rara classe que faz isso delega a cessão (yielding) a um aguardável existente em vez de conduzir o loop ela mesma:
class WaitForEvent:
def __init__(self, event):
self._event = event
def __await__(self):
yield from self._event.wait().__await__()
return self._event
O yield from reutiliza o __await__ do evento subjacente para a suspensão de fato. A maioria das aplicações asyncio nunca precisa escrever nenhuma das duas formas.