在上下文管理器中,没有值的收益是什么

Shu*_*man 13 python yield generator 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.
Run Code Online (Sandbox Code Playgroud)

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

Mar*_*ers 16

yield表达式将控制权返回给使用生成器的任何内容.生成器此时暂停,这意味着@contextmanager装饰器知道代码是通过设置部分完成的.

换句话说,您在上下文管理器__enter__阶段要做的所有事情都必须在之前进行yield.

一旦你的上下文退出(所以with语句下的块完成),@contextmanager就会调用装饰器来完成__exit__上下文管理器协议的一部分,并且会做以下两件事之一:

  • 如果没有例外,它将恢复你的发电机.因此,您的发电机在该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. 生成器退出,装饰器__exit__方法退出,一切都完成了.

  • @Alex:你总是产生*东西*。那可以是 `None`,就像 `return` 退出一个函数,然后返回值是 `None` 一样。但是你也可以显式地产生一些东西,所以`yield some_object`,此时可以使用`with context_manager as <target>`语法将该值分配给目标。 (3认同)

Man*_*rla 9

@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
Run Code Online (Sandbox Code Playgroud)

正如@Martijun Pieters 所解释的,在内部contextlib.contextmanager调用这些进入退出魔术函数。希望这可以帮助!