Python contextlib.contextmanager

本文深入探讨了Python中contextlib模块的contextmanager装饰器用法,通过实例解释如何使用该装饰器简化with语句的实现,并介绍了其内部机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

看着代码又发现了一个奇怪的东西:

    @contextlib.contextmanager
    def __call__(self, incoming):
        result_wrapper = []

        yield lambda: result_wrapper.append(
            self._dispatch_and_handle_error(incoming))

        if result_wrapper[0] == NotificationResult.HANDLED:
            incoming.acknowledge()
        else:
            incoming.requeue()

contextlib.contextmanager(func)

This function is a decorator that can be used to define a factory function for with statement context managers, without needing to create a class or separate __enter__() and __exit__() methods.

A simple example (this is not recommended as a real way of generating HTML!):

from contextlib import contextmanager

@contextmanager
def tag(name):
    print "<%s>" % name
    yield
    print "</%s>" % name

>>> with tag("h1"):
...    print "foo"
...
<h1>
foo
</h1>

The function being decorated must return a generator-iterator when called. This iterator must yield exactly one value, which will be bound to the targets in the with statement’s as clause, if any.

At the point where the generator yields, the block nested in the with statement is executed. The generator is then resumed after the block is exited. 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. If an exception is trapped merely in order to log it or to perform some action (rather than to suppress it entirely), the generator must reraise that exception. Otherwise the generator context manager will indicate to the with statement that the exception has been handled, and execution will resume with the statement immediately following the with statement.

 

也就是说用了contextmanager装饰器的generator,yield语句之前的部分会在进入with的语句块之前执行(generator生成过程,generator函数开始到yield之间的部分),yield的值可以用于with中的as来定义个语句块内的变量。yield的值相当于是__enter__函数的返回,而yield语句后面部分将会在with语句块退出时执行,相当在__exit__函数中执行。

 

先来试验一下派森大法中generator的执行步骤:

>>> def FakeGenerator():
...     print 'iteration begin'
...     yield {"msg":'Hello'}
...     print 'iteration end'
...
>>> for obj in FakeGenerator():
...     print 'get msg:', obj['msg']
...
iteration begin
get msg: Hello
iteration end

也就是说每次迭代中FakeGenerator只会执行到yield语句,然后就获取yield返回的对象给for循环里的语句使用,等到下次迭代时再从yield后面的语句开始执行,真是吊炸天!这样就几乎实现了with语句中对象加了__enter__和__exit__方法时的功能。

不过每次要写个for来代替with显得太土,于是就有了我们的@contextmanager装饰器,它的实现或许是这样的

>>> class FakeGeneratorContext(object):
...     def __init__(self, func):
...             self.generator_func = func
...             self.__exit__ = None
...             self.__enter__= None
...             self.generator = None
...     def __enter__(self):
...             self.generator = self.generator_func()
...             return self.generator.next()
...     def __exit__(self, ext, exv, tb):
...             try:
...                     self.generator.next()
...             except StopIteration:
...                     pass
>>> def fakecontext(func):
...     def wrapper():
...             context = FakeGeneratorContext(func)
...             return context
...     return wrapper
...
>>> @fakecontext
... def FakeGenerator():
...     print 'iteration begin'
...     yield {'msg':'Hello'}
...     print 'iteration end'
...
>>> with FakeGenerator() as obj:
...     print obj["msg"]
...
iteration begin
Hello
iteration end

有点烦了,再玩下去就和搞Java的一样了

转载于:https://www.cnblogs.com/lailailai/p/3893964.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值