2015-10-29 6 views
15

PEP 0492 aggiunge il nuovo metodo magico __await__. L'oggetto che implementa questo metodo diventa oggetto simile al futuro e può essere atteso utilizzando await. E 'chiaro:Come posso aspettare __await__ di un oggetto simile a un futuro?

import asyncio 


class Waiting: 
    def __await__(self): 
     yield from asyncio.sleep(2) 
     print('ok') 

async def main(): 
    await Waiting() 

if __name__ == "__main__": 
    loop = asyncio.get_event_loop() 
    loop.run_until_complete(main()) 

Ok, ma cosa succede se voglio chiamare alcuni async def definita funzione invece di asyncio.sleep? Non posso usare await perché __await__ non è async funzione, non posso usare perché yield from coroutine native richiede await espressione:

async def new_sleep(): 
    await asyncio.sleep(2) 

class Waiting: 
    def __await__(self): 
     yield from new_sleep() # this is TypeError 
     await new_sleep() # this is SyntaxError 
     print('ok') 

Come posso risolverlo?

+0

C'è qualche motivo non si può semplicemente attuarla come una funzione asincrona separata all'interno della classe in attesa? Quindi, aspetta Waiting.new_sleep()? – shongololo

risposta

18

uso diretto __await__() chiamata:

async def new_sleep(): 
    await asyncio.sleep(2) 

class Waiting: 
    def __await__(self): 
     return new_sleep().__await__() 

La soluzione è stato consigliato da Yury Selivanov (l'autore di PEP 492) per aioodbc library

+0

Grazie. Digita in risposta, correggi - new_sleep() .__ attende __() –

+0

Risolto. Grazie per la correzione –

+0

questa risposta: https://stackoverflow.com/a/46722215/371191 è un po 'più generale, in quanto consente di attendere più attese –

3

non ho capito il motivo per cui non posso resa da coroutine nativo all'interno __await__, ma sembra che sia possibile resa dal generatore coroutine all'interno __await__ e resa da coroutine nativo all'interno che generatore coroutine . Funziona:

async def new_sleep(): 
    await asyncio.sleep(2) 

class Waiting: 
    def __await__(self): 
     @asyncio.coroutine 
     def wrapper(coro): 
      return (yield from coro) 
     return (yield from wrapper(new_sleep())) 
3

Per attendere all'interno di una funzione __await__, utilizzare il codice seguente:

async def new_sleep(): 
    await asyncio.sleep(1) 


class Waiting: 
    def __await__(self): 
     yield from new_sleep().__await__() 
     print('first sleep') 
     yield from new_sleep().__await__() 
     print('second sleep') 
     return 'done' 
2

Versione corta: await foo può essere sostituito da yield from foo.__await__()


Combinando tutte le idee delle altre risposte -

nel caso più semplice, solo delegando ad altri lavori awaitable:

def __await__(self): 
    return new_sleep().__await__() 

Questo funziona perché il metodo __await__ restituisce un iteratore (vedere PEP 492), quindi restituire un altro iteratore di __await__ va bene.

Ciò significa, ovviamente, che non possiamo assolutamente modificare il comportamento di sospensione dell'originale. L'approccio più generale è quello di rispecchiare la parola chiave await e utilizzare yield from - questo ci permette di combinare più iteratori awaitables' in uno:

def __await__(self): 
    # theoretically possible, but not useful for my example: 
    #yield from something_else_first().__await__() 
    yield from new_sleep().__await__() 

Ecco la cattura: questo non sta facendo esattamente la stessa cosa, come la prima variante!yield from è espressione, in modo di fare esattamente la stessa di prima, abbiamo bisogno di tornare anche quel valore:

def __await__(self): 
    return (yield from new_sleep().__await__()) 

Questo rispecchia direttamente come avremmo scrivere una corretta delegazione utilizzando la await sintassi:

return await new_sleep() 

extra bit - qual è la differenza tra questi due?

def __await__(self): 
    do_something_synchronously() 
    return new_sleep().__await__() 

def __await__(self): 
    do_something_synchronously() 
    return (yield from new_sleep().__await__()) 

La prima variante è una funzione semplice: quando si chiama esso, do_... viene eseguito e un iteratore restituito. Il secondo è una funzione generatore; chiamarlo non esegue affatto il nostro codice! Solo quando l'iteratore restituito viene restituito per la prima volta, verrà eseguito do_.... Questo fa la differenza nel seguito, un po situazione artificiosa:

def foo(): 
    tmp = Waiting.__await__() 
    do_something() 
    yield from tmp 
Problemi correlati