在我的代码中,我需要能够正确打开和关闭设备,因此需要使用上下文管理器。尽管通常使用__enter____exit__方法将上下文管理器定义为一个类,但似乎也有可能修饰与上下文管理器一起使用的函数(请参见a recent postanother nice example here)。

在下面的(有效的)代码片段中,我实现了两种可能性:一个只需要与另一个交换评论的行即可:

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__方法被称为expeced(终端上打印了文本“close”),但是当我使用wrap函数尝试相同的操作时,文本“close”却没有打印到终端。

我的问题:代码段是否存在问题,我是否丢失了某些内容,或者为什么装饰函数未调用print("close")行?

最佳答案

contextmanager文档中的示例有些误导。 yield之后的函数部分实际上并不与上下文管理器协议(protocol)的__exit__对应。文档中的关键点是:



因此,如果要在contextmanager装饰的函数中处理异常,则需要编写自己的try,包装yield并自己处理异常,在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

This page还显示了一个说明性示例。

关于python - 为什么我的contextmanager-function不能像python中的contextmanager类那样工作?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/15447130/

10-12 23:06