为什么我的contextmanager函数不能像python中的contextmanager类一样工作?

时间:2013-03-16 08:19:16

标签: python python-2.7 contextmanager

在我的代码中,我需要能够正确地打开和关闭设备,因此需要使用上下文管理器。虽然上下文管理器通常被定义为具有__enter____exit__方法的类,但似乎还可以修饰一个函数以供上下文管理器使用(请参阅a recent post和{ {3}})。

在以下(工作)代码片段中,我实现了两种可能性;只需要将注释行与另一行交换:

import time
import contextlib

def device():
    return 42

@contextlib.contextmanager
def wrap():
    print("open")
    yield device
    print("close")
    return

class Wrap(object):
    def __enter__(self):
        print("open")
        return device
    def __exit__(self, type, value, traceback):
        print("close")


#with wrap() as mydevice:
with Wrap() as mydevice:
    while True:
        time.sleep(1)
        print mydevice()

我尝试运行代码并使用CTRL-C停止代码。当我在上下文管理器中使用Wrap类时,__exit__方法被调用为已经过时(文本'close'在终端中打印),但当我尝试使用{{{ 1}}功能,文本'close'不会打印到终端。

我的问题:代码片段是否存在问题,我是否遗漏了某些内容,或者为什么没有使用修饰函数调用行wrap

1 个答案:

答案 0 :(得分:12)

contextmanager文档中的示例有点误导。 yield之后的函数部分并不真正对应于上下文管理器协议的__exit__。文档中的关键点是:

  

如果块中发生未处理的异常,则会在生成器发生的位置对其进行重新加载。因此,您可以使用try...except...finally语句来捕获错误(如果有),或者确保进行一些清理。

因此,如果要在contextmanager-decorated函数中处理异常,则需要编写自己的try来包装yield并自己处理异常,在{{中执行清理代码1}}(或者只是阻止finally中的异常并在except之后执行清理)。例如:

try/except

This page也展示了一个有益的例子。