在上下文管理中,没有价值的结果是什么

2024-10-01 11:29:38 发布

您现在位置:Python中文网/ 问答频道 /正文

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何时执行?在


Tags: nameimporttasktimedefprocessessecondsprint
2条回答

@Martijn Pieters的精彩解释。由于yield在您的案例中是多余的,您可以通过创建自己的上下文管理器(不使用yieldcontextlib.contextmanager)。这更简单易读。所以在您的例子中,您可以实现如下内容。在

import time

class time_print(object):
    def __init__(self, task_name):
        self.task_name = task_name

    def __enter__(self):
        self.t = time.time()

    def __exit__(self):
        print self.task_name, "took", time.time() - self.t, "seconds."

def doproc():
    x=1+1


with time_print("processes"):
    # __enter__ is called
    [doproc() for _ in range(500)]
    # __exit__ is called

内部contextlib.contextmanager按照@Martijun Pieters的解释,调用\uuenter\\uuu exit\uuu魔术函数。希望这有帮助!在

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

换句话说,在上下文管理器__enter__阶段中要做的一切都必须在yield之前发生。在

一旦上下文退出(因此with语句下的块完成),将为上下文管理器协议的__exit__部分调用@contextmanager修饰符,并将执行以下两种操作之一:

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

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

因此,在您的具体例子中,顺序如下:

  1. with time_print("processes"):

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

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

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

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

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

  6. 装饰者重新启动生成器,它继续执行它停止的地方。

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

  8. 生成器退出,decorator __exit__方法退出,一切都完成了。

相关问题 更多 >