python coroutine协程

本文探讨了Python中如何将普通函数转化为协程函数,主要通过装饰器使其变为生成器,然后通过inspect.CO_ITERABLE_COROUTINE标志位将生成器转变为协程。协程在await表达式中可被调用,执行过程可通过迭代控制。协程对象是awaitable对象,当执行结束会抛出StopIteration异常。协程不应直接引发未处理的StopIteration异常。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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 协程 Coroutine 使用教程与常见问题 #### 一、协程简介 协程是一种并发编程技术,它允许程序在执行过程中暂停和恢复。协程可以看作一种特殊的函数,能够暂停其执行去运行其他的任务,并能在之后返回到暂停处继续执行。相较于线程或进程而言,协程拥有更低的资源消耗以及更高的执行效率[^4]。 #### 二、使用 async 和 await 定义协程 `async` 和 `await` 是 Python 中用于定义和调用协程的关键字。这两个关键字使得编写异步代码变得更加简洁直观,有助于处理并发任务。下面是一个简单的例子来展示如何创建并启动一个基本的协程: ```python import asyncio async def say_after(delay, what): await asyncio.sleep(delay) print(what) async def main(): task1 = say_after(1, 'hello') task2 = say_after(2, 'world') # Wait until both tasks are completed (should take around 2 seconds.) await task1 await task2 # Run the event loop to execute coroutines. asyncio.run(main()) ``` 这段代码展示了两个延迟打印语句的任务被安排在一个事件循环里依次完成;其中每个任务都会等待指定的时间间隔后再输出相应的内容[^1]。 #### 三、利用 asyncio 编写并发异步代码 `asyncio` 模块提供了丰富的工具支持开发者构建复杂的异步应用程序。除了上述提到的基础功能外,还包含了诸如队列、锁机制等功能组件帮助管理多任务间的协作关系。这里给出一段更加复杂一点的例子说明如何同时发起多个 HTTP 请求而不阻塞主线程的工作流程: ```python import aiohttp import asyncio async def fetch(session, url): async with session.get(url) as response: return await response.text() async def main(urls): async with aiohttp.ClientSession() as session: results = await asyncio.gather(*[fetch(session, url) for url in urls]) for result in results: print(result[:100]) # Print first 100 characters of each page. urls = ["https://example.com", "https://www.python.org"] asyncio.run(main(urls)) ``` 此段脚本会向给定 URL 列表发送 GET 请求并将接收到的数据部分显示出来。值得注意的是整个过程是在同一个线程内高效地完成了多项I/O操作而无需担心传统同步模式下可能产生的性能瓶颈问题[^2]。 #### 四、Gevent 库介绍及其应用场景 对于那些希望简化开发工作量又不想深入研究底层细节的人来说,`gevent` 可能是个不错的选择。该库基于 `greenlet` 构建而成并通过所谓的“猴子补丁”实现了透明化控制流转移的效果——即当某个绿色线程处于休眠状态时操作系统将会自动切换至下一个可运行实例上继续推进整体进度直至所有待办事项都被妥善处置完毕为止。特别是在面对大量短时间内的高频率交互请求(比如Web服务器端口监听)或是长时间占用外部资源的操作场合之下往往可以获得较为理想的吞吐率表现[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值