Why does my contextmanager-function not work like my contextmanager class in python?

我与影子孤独终老i 提交于 2020-01-11 06:09:11

问题


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()

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.

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?


回答1:


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:

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.

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.



来源:https://stackoverflow.com/questions/15447130/why-does-my-contextmanager-function-not-work-like-my-contextmanager-class-in-pyt

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!