asyncio 的 call_later 引发 'generator' 对象不能用协程对象调用
asyncio's call_later raises 'generator' object is not callable with coroutine object
我有一些使用 call_later
使用 Python 3.4 的 asyncio 制作的简单代码。代码应该打印,等待 10 秒,然后再次打印(而是在应该执行 end()
时引发 TypeError
,见下文):
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!")
报错:
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
据我从文档 (https://docs.python.org/3/library/asyncio-task.html#coroutine) 中得知,call_later
接受一个协程对象,该对象是通过调用协程函数获得的。这似乎是我所做的,但 asyncio 没有正确调用 end()
。
这应该怎么做?
call_later
旨在接受回调(意味着常规函数对象),而不是协程。 Python 的较新版本实际上会明确说明:
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()
为了使您的代码正常工作,end
需要是一个常规函数,然后您将其传递给 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!")
输出:
Starting to wait.
completed
Goodbye!
如果 end
需要成为协程,在延迟后调用它的更自然的方法是使用 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!")
尽管从技术上讲,这确实有效:
asyncio.get_event_loop().call_later(10, lambda: asyncio.async(end()))
我有一些使用 call_later
使用 Python 3.4 的 asyncio 制作的简单代码。代码应该打印,等待 10 秒,然后再次打印(而是在应该执行 end()
时引发 TypeError
,见下文):
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!")
报错:
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
据我从文档 (https://docs.python.org/3/library/asyncio-task.html#coroutine) 中得知,call_later
接受一个协程对象,该对象是通过调用协程函数获得的。这似乎是我所做的,但 asyncio 没有正确调用 end()
。
这应该怎么做?
call_later
旨在接受回调(意味着常规函数对象),而不是协程。 Python 的较新版本实际上会明确说明:
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()
为了使您的代码正常工作,end
需要是一个常规函数,然后您将其传递给 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!")
输出:
Starting to wait.
completed
Goodbye!
如果 end
需要成为协程,在延迟后调用它的更自然的方法是使用 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!")
尽管从技术上讲,这确实有效:
asyncio.get_event_loop().call_later(10, lambda: asyncio.async(end()))