8.10. Classes aguardáveis¶
8.10.1. O que é um aguardável¶
Quando uma coroutine escreve await x, a linguagem pergunta a x como esperar por ele. Qualquer objeto que saiba responder é aguardável. Existem dois tipos:
Os objetos coroutine que as funções
async defdevolvem. Chamarsend_request()produz um destes cada vez – o objeto coroutine, não o resultado da função. Escreverawait send_request()é o que realmente executa o corpo.Objetos de uma classe que define um método
__await__.async defé a forma abreviada para o primeiro tipo;__await__é a forma manual de criar o segundo tipo.
O código da aplicação usa o primeiro tipo por defeito. O segundo tipo existe para o caso raro em que o próprio objeto precisa de ser aquilo que o chamador usa com await, não o resultado de chamar um método nele.
8.10.2. As duas formas lado a lado¶
A mesma lógica escrita como coroutine e como 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 usadas com awaitda 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 gerir toda a contabilidade de suspensão e retoma. A forma de classe não acrescenta nada neste exemplo.
8.10.3. Quando a forma de classe justifica a sua existência¶
Quando o objeto tem de ser algo que a aplicação passa – não uma função que o chamador invoca para obter uma coroutine – a forma de classe é a única opção:
Um valor semelhante a um future que a aplicação cria, entrega a um produtor, e usa com
awaitposteriormente para obter o resultado produzido.Uma primitiva personalizada construída sobre uma existente onde a API natural é
await my_thingem vez deawait my_thing.wait(). A própria classeasyncio.Taské um exemplo incorporado – escreverawait taskfunciona porqueTaskdefine__await__.
Para tudo o que corresponde ao padrão chamar uma função, obter uma coroutine, aguardá-la, async def é a melhor escolha.
8.10.4. Os detalhes do protocolo¶
__await__ é um método regular (não async def) que devolve um iterador. Escrevê-lo como gerador – incluindo pelo menos um yield no corpo – torna-o um automaticamente. Cada yield suspende a coroutine que usa awaitno objeto e devolve o controlo ao event loop; o gerador é retomado na próxima vez que o loop escalonar a tarefa. Um return simples (ou chegar ao fim) termina a espera; o que o return produz torna-se o valor da expressão await.
Na prática, a classe rara que faz isto delega a produção do yield a um aguardável existente em vez de conduzir o loop diretamente:
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 real. A maioria das aplicações asyncio nunca precisa de escrever qualquer das formas.