2015-10-29 4 views
15

PEP 0492 fügt neue __await__ magische Methode hinzu. Objekt, das diese Methode implementiert, wird Zukunftsobjekt und kann mit await erwartet werden. Es ist klar:Wie kann ich im __await__ des zukünftigen Objekts warten?

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, aber was ist, wenn ich will einige async def definierte Funktion statt asyncio.sleep anrufen? Ich kann nicht await verwenden, da __await__ nicht async Funktion ist, kann ich nicht, weil yield from nativer Koroutinen erfordert await Ausdruck:

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') 

Wie kann ich es lösen?

+0

Gibt es einen Grund, warum Sie können es als eine separate Asynchron-Funktion innerhalb der Warte Klasse nicht nur implementieren? Warten Sie also auf Waiting.new_sleep()? – shongololo

Antwort

18

Verwenden direkten __await__() Aufruf:

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

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

Die Lösung von Yury Selivanov (der Autor von PEP 492) für aioodbc library

+0

Danke. Tippfehler in der Antwort, richtig - new_sleep() .__ erwarten __() –

+0

Fixed. Danke für die Korrektur –

+0

Diese Antwort: https://stackoverflow.com/a/46722215/371191 ist ein wenig allgemeiner, da es mehrere Wartezeiten erwartet –

3

Ich verstehe nicht, warum kann ich nicht Ausbeute von nativem Koroutine innerhalb __await__, aber sieht aus wie es zu Ausbeute von Generator Koroutine innerhalb __await__ und Ausbeute von nativem Koroutine innerhalb dieses Generator Koroutine möglich ist . Es funktioniert:

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

erwarten in einer __await__ Funktion empfohlen wurde, verwenden Sie die folgender Code:

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
def __await__(self): 
    return new_sleep().__await__() 

folgt aus: -:Kurzfassung

im einfachsten Fall nur delegieren zu einem anderen awaitable Werke await foo kann durch yield from foo.__await__()


Kombination all die Ideen aus den anderen Antworten ersetzt werden funktioniert, weil die __await__-Methode einen Iterator zurückgibt (siehe PEP 492), daher ist die Rückgabe eines weiteren Iterators von __await__ in Ordnung.

Das bedeutet natürlich, dass wir das Federungsverhalten des Originals überhaupt nicht ändern können. Der allgemeinere Ansatz ist es, das await Schlüsselwort spiegeln und yield from verwenden - das läßt uns mehr awaitables' Iteratoren miteinander kombinieren:

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

Hier ist der Haken: dies nicht genau so, wie die erste Variante die gleiche Sache tut!yield from ist ein Ausdruck, so genau wie vor das gleiche zu tun, müssen wir auch diesen Wert zurück:

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

Diese direkt spiegelt, wie wir die richtige Delegation mit der await Syntax schreiben würde:

return await new_sleep() 

zusätzliches Bit - was ist der Unterschied zwischen diesen beiden?

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

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

Die erste Variante ist eine einfache Funktion: Wenn Sie es nennen, do_... ausgeführt wird und ein Iterator zurückgegeben. Die zweite ist eine Generatorfunktion; wenn sie aufgerufen wird, führt sie überhaupt keinen Code aus! Nur wenn der zurückgegebene Iterator zum ersten Mal zurückgegeben wird, wird do_... ausgeführt. Das macht einen Unterschied im Folgenden ein wenig gekünstelt Situation:

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