为什么我的 contextmanager 函数不像 python 中的 contextmanager 类那样工作? [英] Why does my contextmanager-function not work like my contextmanager class in python?
问题描述
在我的代码中,我需要能够正确打开和关闭设备,因此需要使用上下文管理器.虽然上下文管理器通常被定义为具有 __enter__
和 __exit__
方法的类,但似乎也有可能装饰一个与上下文管理器一起使用的函数(参见 最近的一篇文章 和这里还有一个很好的例子a>).
In my code, I need to be able to open and close a device properly, and therefore see the need to use a context manager. While a context manager is usually defined as a class with __enter__
and __exit__
methods, there also seem to be the possibility to decorate a function for use with the context manager (see a recent post and another nice example here).
在以下(工作)代码片段中,我实现了两种可能性;一个只需要将注释行与另一个交换:
In the following (working) code snippet, I have implemented the two possibilities; one just need to swap the commented line with the other one:
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' 不会打印到终端.
What I try is to run the code and stop it with CTRL-C
. When I use the Wrap
class in the context manager, the __exit__
method is called as expeced (the text 'close' is printed in the terminal), but when I try the same thing with the wrap
function, the text 'close' is not printed to the terminal.
我的问题:代码片段是否有问题,我是否遗漏了什么,或者为什么没有用装饰函数调用 print("close")
行?
My question: Is there a problem with the code snippet, am I missing something, or why is the line print("close")
not called with the decorated function?
推荐答案
contextmanager
文档中的示例有些误导.yield
之后的函数部分并不真正对应于上下文管理器协议的 __exit__
.文档中的关键点是:
The example in the documentation for contextmanager
is somewhat misleading. The portion of the function after yield
does not really correspond to the __exit__
of the context manager protocol. The key point in the documentation is this:
如果块中发生未处理的异常,它会在生成器内在产生的点处重新引发.因此,您可以使用 try...except...finally
语句来捕获错误(如果有),或确保进行一些清理.
If an unhandled exception occurs in the block, it is reraised inside the generator at the point where the yield occurred. Thus, you can use a
try...except...finally
statement to trap the error (if any), or ensure that some cleanup takes place.
所以如果你想在你的 contextmanager 装饰的函数中处理异常,你需要编写自己的 try
来包装 yield
并自己处理异常,执行finally
中的清理代码(或者只是阻止 except
中的异常并在 try/except
之后执行你的清理).例如:
So if you want to handle an exception in your contextmanager-decorated function, you need to write your own try
that wraps the yield
and handle the exceptions yourself, executing cleanup code in a finally
(or just block the exception in except
and execute your cleanup after the try/except
). For example:
@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 also shows an instructive example.
这篇关于为什么我的 contextmanager 函数不像 python 中的 contextmanager 类那样工作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!