在我的代码中,我需要能够正确地打开和关闭设备,因此需要使用上下文管理器。虽然上下文管理器通常被定义为具有__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
?
答案 0 :(得分:12)
contextmanager
文档中的示例有点误导。 yield
之后的函数部分并不真正对应于上下文管理器协议的__exit__
。文档中的关键点是:
如果块中发生未处理的异常,则会在生成器发生的位置对其进行重新加载。因此,您可以使用
try...except...finally
语句来捕获错误(如果有),或者确保进行一些清理。
因此,如果要在contextmanager-decorated函数中处理异常,则需要编写自己的try
来包装yield
并自己处理异常,在{{中执行清理代码1}}(或者只是阻止finally
中的异常并在except
之后执行清理)。例如:
try/except
This page也展示了一个有益的例子。