2015-11-30 27 views
8

Tôi có một số mã mã đơn giản được tạo bằng asyncio của Python 3.4 bằng cách sử dụng call_later. Mã nên in, chờ 10 giây, và sau đó in lại (nhưng thay vì tăng TypeError khi end() nên excecuted, xem dưới đây):call_later của asyncio làm tăng đối tượng 'máy phát' không thể gọi với đối tượng 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!") 

Cung cấp cho các lỗi:

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 

Từ những gì tôi có thể kể từ các tài liệu (https://docs.python.org/3/library/asyncio-task.html#coroutine), call_later lấy một đối tượng coroutine, thu được bằng cách gọi hàm coroutine. Điều này dường như là những gì tôi đã làm, nhưng asyncio không gọi end() đúng cách.

Việc này phải được thực hiện như thế nào?

Trả lời

7

call_later được thiết kế để gọi lại (nghĩa là đối tượng hàm thông thường), không phải là coroutine. Phiên bản mới hơn của Python sẽ thực sự nói điều này một cách rõ ràng:

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

Để làm cho công việc mã của bạn, end cần phải là một chức năng thông thường, bạn sau đó vượt qua để 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!") 

Output:

Starting to wait. 
completed 
Goodbye! 

Nếu end cần phải là coroutine, cách tự nhiên hơn để gọi sau khi trì hoãn sẽ là sử dụng 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!") 

Mặc dù về mặt kỹ thuật, điều này không làm việc:

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

Trong trường hợp đó, là có một cách để sắp xếp một coroutine được gọi sau với 'asyncio'? Hay có lý do nào đó tại sao điều này không có ý nghĩa để làm? –

+3

@NathanaelFarley Vâng, bạn có thể sử dụng 'call_later (10, lambda: asyncio.ensure_future (end()))'. Nhưng nó có lẽ có ý nghĩa hơn khi chỉ cần đặt một 'yield từ asyncio.sleep (10)' bên trong 'begin', và sau đó gọi 'yield from end()' ngay sau đó. Nếu bạn không muốn chặn 'bắt đầu', bạn có thể chỉ cần đặt' asyncio.sleep' và gọi đến 'end' trong một coroutine khác, và chỉ cần gọi' asyncio.ensure_future (other_coroutine()) 'bên trong' begin' để thay thế . – dano

Các vấn đề liên quan