为什么我的 contextmanager 函数不像 python 中的 contextmanager 类那样工作? [英] Why does my contextmanager-function not work like my contextmanager class in python?

查看:112
本文介绍了为什么我的 contextmanager 函数不像 python 中的 contextmanager 类那样工作?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的代码中,我需要能够正确打开和关闭设备,因此需要使用上下文管理器.虽然上下文管理器通常被定义为具有 __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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆