python - yield without value 在上下文管理器中做什么

标签 python generator yield contextmanager

import contextlib
import time

@contextlib.contextmanager
def time_print(task_name):
    t = time.time()
    try:
        yield
    finally:
        print task_name, "took", time.time() - t, "seconds."


def doproc():
    x=1+1


with time_print("processes"):
    [doproc() for _ in range(500)]

# processes took 15.236166954 seconds.

使用这个装饰器时doproc什么时候执行?

最佳答案

yield 表达式将控制权返回给使用生成器的任何对象。生成器在此时暂停,这意味着 @contextmanager 装饰器知道代码已完成 setup 部分。

换句话说,你想在上下文管理器 __enter__ 阶段做的所有事情都必须在 yield 之前发生。

一旦您的上下文退出(因此 with 语句下的 block 完成),@contextmanager 装饰器将被调用用于 __exit__ 部分上下文管理器协议(protocol),并将做两件事之一:

  • 如果没有异常,它将恢复您的生成器。所以你的生成器在 yield 行取消暂停,然后你进入清理阶段,部分

  • 如果出现异常,装饰器会使用 generator.throw() 在生成器中引发该异常。就好像 yield 行导致了那个异常。因为您有一个 finally 子句,它会在您的生成器因异常而退出之前执行。

因此,在您的特定示例中,顺序如下:

  1. 使用 time_print("processes"):

    这将创建上下文管理器并对其调用 __enter__

  2. 生成器开始执行,t = time.time() 运行。

  3. yield 表达式暂停生成器,控制权返回装饰器。如果有 as target 部分,这将获取生成的任何内容并将其返回给 with 语句。这里生成了 None(只有一个普通的 yield 表达式)。

  4. [doproc() for _ in range(500)] 运行并完成。

  5. 运行上下文管理器__exit__方法,没有传入异常。

  6. 装饰器恢复生成器,它从中断处继续。

  7. 进入 finally: block 并执行 print task_name, "took", time.time() - t, "seconds."

  8. 生成器退出,装饰器__exit__方法退出,全部完成。

关于python - yield without value 在上下文管理器中做什么,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35489844/

相关文章:

python - 产量与生成器表达式 - 返回不同类型

php - 在 PHP 中创建可选生成器

scala - 如何动态生成具有 for-yield 的并行 future

c# - 是否有像 yield 的 'opposite' 这样的东西?

python - Mysqldb 和 Python KeyError 处理

python - 列表中的格式化字符串

python - 如何在没有命令的情况下使用 discord.py 发送消息

python - 如何将数据框的格式从两行转换为单行

python - 生成器与嵌套 for 循环

algorithm - 出于好奇 : How are serial numbers generated? 提示,算法?