In [8]: async def funcc():
...: print('2333')
...: return '2333'
In [14]: x = funcc()
In [15]: try:
...: x.send(None)
...: except StopIteration as exc:
...: print(exc.value)
2333
2333
In [2]: @asyncio.coroutines.coroutine
...: def f():
...: print('2333')
...: return '2333'
In [3]: x = f()
In [4]: try:
...: x.send(None)
...: except StopIteration as exc:
...: print(exc.value)
2333
2333
上面代码很相似,从上面就可以看出 是如何把一个普通函数变为协程函数的。通过send传递参数,若不需要参数传递 None 就好。上面代码中 f 是一个普通函数,通过装饰器 asyncio.coroutines.coroutine, 装饰后变为一个协程函数(同时也是生成器);
下面看一下 asyncio.coroutines.coroutine 源代码:
def coroutine(func):
"""Decorator to mark coroutines.
If the coroutine is not yielded from before it is destroyed,
an error message is logged.
"""
if inspect.iscoroutinefunction(func): # 如果是协程函数就直接返回
# In Python 3.5 that's all we need to do for coroutines
# defined with "async def".
return func
if inspect.isgeneratorfunction(func): # 如果 func 是生成器
coro = func
else:
@functools.wraps(func) # 将 func 装饰为生成器(只要函数中包含 yield)
def coro(*args, **kw):
res = func(*args, **kw)
if (base_futures.isfuture(res) or inspect.isgenerator(res) or
isinstance(res, CoroWrapper)):
res = yield from res
else:
# If 'res' is an awaitable, run it.
try:
await_meth = res.__await__
except AttributeError:
pass
else:
if isinstance(res, collections.abc.Awaitable):
res = yield from await_meth()
return res
coro = types.coroutine(coro) # 将生成器 coro 变为 协程类型,co.co_flags | 0x100
if not _DEBUG:
wrapper = coro
else:
@functools.wraps(func)
def wrapper(*args, **kwds):
w = CoroWrapper(coro(*args, **kwds), func=func)
if w._source_traceback:
del w._source_traceback[-1]
# Python < 3.5 does not implement __qualname__
# on generator objects, so we set it manually.
# We use getattr as some callables (such as
# functools.partial may lack __qualname__).
w.__name__ = getattr(func, '__name__', None)
w.__qualname__ = getattr(func, '__qualname__', None)
return w
wrapper._is_coroutine = _is_coroutine # For iscoroutinefunction().
return wrapper
将生成器变为协程:https://docs.python.org/3/library/inspect.html#inspect.CO_ITERABLE_COROUTINE
The flag is used to transform generators into generator-based coroutines. Generator objects with this flag can be used in await expression, and can yield from coroutine objects. See PEP 492 for more details.
所以说上面测试代码中 f() 被装饰后由一个普通函数就变成了一个生成器函数然后通过修改标志位变为一个协程函数,本质上还是一生成器函数(通过inspect.CO_ITERABLE_COROUTINE 标志位将生成器转换为基于生成器的协程),并且此协程可以在 await 表达式中使用。该函数并不是协程对象而是基于生成器的协程函数。
然后我们看一下协程对象的定义:
Coroutine 对象属于 awaitable 对象。 协程的执行可通过调用 __await__() 并迭代其结果来进行控制。 当协程结束执行并返回时,迭代器会引发 StopIteration,该异常的 value 属性将指向返回值。 如果协程引发了异常,它会被迭代器所传播。 协程不应该直接引发未处理的 StopIteration 异常。
coroutine 对象是由复合语句 async def 创建的继承自awaitable类的对象,而基于生成器的协程是通过设置 inspect.CO_ITERABLE_COROUTINE 标志位来实现协程的。
本文探讨了Python中如何将普通函数转化为协程函数,主要通过装饰器使其变为生成器,然后通过inspect.CO_ITERABLE_COROUTINE标志位将生成器转变为协程。协程在await表达式中可被调用,执行过程可通过迭代控制。协程对象是awaitable对象,当执行结束会抛出StopIteration异常。协程不应直接引发未处理的StopIteration异常。
3万+

被折叠的 条评论
为什么被折叠?



