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

Ale*_*lex 4 python contextmanager python-2.7

在我的代码中,我需要能够正确地打开和关闭设备,因此需要使用上下文管理器.虽然上下文管理器通常被定义为带有__enter____exit__方法的类,但似乎还有可能装饰一个与上下文管理器一起使用的函数(参见最近的帖子另一个很好的例子).

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

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

我尝试的是运行代码并停止它CTRL-C.当我Wrap在上下文管理器中使用该类时,该__exit__方法被调用为已解决(文本'close'在终端中打印),但是当我尝试使用该wrap函数时,文本'close'不会打印到终奌站.

我的问题:代码片段是否存在问题,我是否遗漏了某些内容,或者为什么print("close")没有使用装饰函数调用该行?

Bre*_*arn 12

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

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

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

@contextlib.contextmanager
def cm():
    print "before"
    exc = None
    try:
        yield
    except Exception, exc:
        print "Exception was caught"
    print "after"
    if exc is not None:
        raise exc

>>> with cm():
...     print "Hi!"
before
Hi!
after

>>> with cm():
...     print "Hi!"
...     1/0
before
Hi!
Exception was caught
after
Run Code Online (Sandbox Code Playgroud)

此页面还显示了一个有益的例子.