2015-11-30 6 views
8

Ho un codice semplice fatto con asyncio di Python 3.4 usando call_later. Il codice dovrebbe stampare, attende 10 secondi, quindi stampare di nuovo (ma invece solleva TypeError quando end() dovrebbe essere excecuted, vedi sotto):asyncio's call_later solleva l'oggetto 'generator' non è richiamabile con oggetto coroutine

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!") 

dà l'errore:

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 

Da quello che posso Dagli documenti (https://docs.python.org/3/library/asyncio-task.html#coroutine), call_later prende un oggetto coroutine, che si ottiene chiamando una funzione di coroutine. Questo sembra essere quello che ho fatto, ma asyncio non chiama correttamente end().

Come dovrebbe essere fatto?

risposta

7

call_later è progettato per eseguire una richiamata (ovvero un oggetto funzione regolare), non una coroutine. Le versioni più recenti di Python effettivamente dirlo esplicitamente:

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

Per rendere il vostro lavoro di codice, end deve essere una funzione regolare, che poi passa a 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!") 

uscita:

Starting to wait. 
completed 
Goodbye! 

Se end deve essere una coroutine, un modo più naturale di chiamarlo dopo un ritardo sarebbe utilizzare asyncio.sleep:

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!") 

Sebbene tecnicamente, questo lavoro:

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

In questo caso, c'è un modo per programmare un coroutine da chiamare successivamente con 'asyncio'? O c'è qualche ragione per cui questo non ha senso da fare? –

+3

@NathanaelFarley Bene, puoi usare 'call_later (10, lambda: asyncio.ensure_future (end()))'. Ma probabilmente ha più senso semplicemente mettere un 'yield from asyncio.sleep (10)' dentro 'begin', e poi chiamare' yield from end() 'subito dopo. Se non vuoi bloccare 'begin', puoi semplicemente mettere 'asyncio.sleep' e chiamare a' end' in un'altra coroutine, e chiamare 'asyncio.ensure_future (other_coroutine())' all'interno 'begin' invece . – dano

Problemi correlati