2015-11-30 3 views
8

Ich habe einige einfache Code-Code mit Python 3.4 ascencio mit call_later gemacht. Der Code sollte drucken, wartet 10 Sekunden und dann wieder drucken (sondern wirft TypeError wenn end() excecuted werden soll, siehe unten):asyncios call_later löst 'generator' -Objekt ist nicht mit coroutine Objekt

import asyncio 

@asyncio.coroutine 
def begin(): 
    print("Starting to wait.") 
    asyncio.get_event_loop().call_later(10, end()) 

@asyncio.coroutine 
def end(): 
    print("completed") 

if __name__ == "__main__": 
    try: 
     loop = asyncio.get_event_loop() 
     loop.create_task(begin()) 
     loop.run_forever() 
    except KeyboardInterrupt: 
     print("Goodbye!") 

Gibt den Fehler:

Exception in callback <generator object coro at 0x7fc88eeaddc8>() 
handle: <TimerHandle when=31677.188005054 <generator object coro at 0x7fc88eeaddc8>()> 
Traceback (most recent call last): 
    File "/usr/lib64/python3.4/asyncio/events.py", line 119, in _run 
    self._callback(*self._args) 
TypeError: 'generator' object is not callable 

Von dem, was ich kann Tell aus der Dokumentation (https://docs.python.org/3/library/asyncio-task.html#coroutine), call_later nimmt ein Coroutine-Objekt, das durch den Aufruf einer Coroutine-Funktion erhalten wird. Dies scheint zu sein, was ich getan habe, aber asyncio ruft end() nicht richtig auf.

Wie soll das gemacht werden?

Antwort

7

call_later wurde entwickelt, um einen Rückruf (dh ein normales Funktionsobjekt), keine Coroutine zu nehmen. Neuere Versionen von Python wird dies tatsächlich ausdrücklich sagen:

Starting to wait. 
Task exception was never retrieved 
future: <Task finished coro=<coro() done, defined at /usr/lib/python3.4/asyncio/coroutines.py:139> exception=TypeError('coroutines cannot be used with call_at()',)> 
Traceback (most recent call last): 
    File "/usr/lib/python3.4/asyncio/tasks.py", line 238, in _step 
    result = next(coro) 
    File "/usr/lib/python3.4/asyncio/coroutines.py", line 141, in coro 
    res = func(*args, **kw) 
    File "aio.py", line 6, in begin 
    asyncio.get_event_loop().call_later(10, end()) 
    File "/usr/lib/python3.4/asyncio/base_events.py", line 392, in call_later 
    timer = self.call_at(self.time() + delay, callback, *args) 
    File "/usr/lib/python3.4/asyncio/base_events.py", line 404, in call_at 
    raise TypeError("coroutines cannot be used with call_at()") 
TypeError: coroutines cannot be used with call_at() 

Um Ihren Code funktioniert, end eine reguläre Funktion sein muss, die Sie dann passieren zu call_later:

import asyncio 

@asyncio.coroutine 
def begin(): 
    print("Starting to wait.") 
    asyncio.get_event_loop().call_later(10, end) 

def end(): 
    print("completed") 

if __name__ == "__main__": 
    try: 
     loop = asyncio.get_event_loop() 
     loop.create_task(begin()) 
     loop.run_forever() 
    except KeyboardInterrupt: 
     print("Goodbye!") 

Ausgang:

Starting to wait. 
completed 
Goodbye! 

Wenn end eine Coroutine sein muss, wäre eine natürlichere Methode, es nach einer Verzögerung aufzurufen,zu verwenden:

import asyncio 

@asyncio.coroutine 
def begin(): 
    print("Starting to wait.") 
    yield from asyncio.sleep(10) 
    yield from end() 

@asyncio.coroutine 
def end(): 
    print("completed") 

if __name__ == "__main__": 
    try: 
     loop = asyncio.get_event_loop() 
     loop.create_task(begin()) 
     loop.run_forever() 
    except KeyboardInterrupt: 
     print("Goodbye!") 

technisch Obwohl funktionierts:

asyncio.get_event_loop().call_later(10, lambda: asyncio.async(end())) 
+1

In diesem Fall gibt es eine Möglichkeit, eine Koroutine zu planen später mit 'asyncio' genannt werden? Oder gibt es einen Grund, warum das nicht sinnvoll ist? –

+3

@NathanaelFarley Nun, Sie können 'call_later (10, lambda: asyncio.ensure_future (end()))' verwenden. Aber es macht wahrscheinlich mehr Sinn, einfach eine 'yield von asyncio.sleep (10)' in 'begin' zu setzen und danach' yield from end() 'aufzurufen. Wenn Sie 'begin' nicht blockieren wollen, können Sie einfach 'asyncio.sleep' und' end' in einer anderen Coroutine aufrufen und stattdessen 'asyncio.ensure_future (other_coroutine())' innerhalb von 'begin' aufrufen . – dano