python doc 1 —— contextmanager

一.contextmanager修饰器的解释

[email protected]

  此函数是一个装饰器,可以不需要创建新的类或者独立的__enter__() 和__exit__(),为context managers (上下文管理器)来定义factory function(工厂函数)的方法。

  虽然许多对象本身支持在with语句中使用,但有时候需要去管理资源本身,而不是上下文管理,并且不能和contextlib.closing一起应用close()的方法。

  context managers可以精确的分配和释放资源,比如我们常用的with:

with open('something.txt', 'w') as f:
	f.write(sth)

  被修饰的函数在被调用时必须返回generator-iterator(生成器迭代器)。这个迭代器必须只产生一个值,(如果有的话)这个值将绑定到with语句的as子句中的目标。如下例中的resource。

  在generator(生成器)yield(生成)时,执行嵌套在with语句中的块。然后在块运行完毕,并退出后,generator恢复并继续运行。如果块中发生了未处理的异常,它将会在generator内部yield出现异常的点处重新生成。因此,可以尝试try…except…finally语句来捕捉错误,或者采取一些清理措施。如果被捕捉的异常仅仅是为了记录或者执行某系操作,并非完全抑制程序进行,这个generator必须冲重新加载异常。否则,generator context manager将会向with语句指示已经处理的异常,并继续执行跟在with语句后面的语句。

from contextlib import contextmanager

@contextmanager
def managed_resource(*args, **kwds):
    # 获取资源,可以从文本中读取资源,read()等
    resource = acquire_resource(*args, **kwds)
    try:
        yield resource
    finally:
        # 发布资源
        release_resource(resource)

>>> with managed_resource(timeout=3600) as resource:
...     # 即使块中的代码引发了异常,这个块中的资源也会在结尾发布

2.For example

  可以采取以下的形式,将时间函数装饰起来,以便于算法优化时候,通过时间判断其计算能力,十分方便。(以下只是代码应用的方式)

@contextmanager
def timer(name):
    start = time.time()
    yield
    end = time.time()
    print('{} COST: {}'.format(name, end - start))

with timer('Timer Evaluation: '):
    evalution(function)

二.例子解释

 1.无异常情况

  正常情况下,当程序运行到yield时,调用with语句部分,然后继续执行原语句。

from contextlib import contextmanager

@contextmanager
def example():
    resource = ['a', 'b', 'c', 'd', 'e']
    print('start')
    try:
        yield resource
    finally:
        print('end')


with example() as resource:
    for i in resource:
        print(i)
'''
start
a
b
c
d
e
end
'''

 2.异常情况

  当设定一个错误在with语句中,当contextmanager执行到yield时,发生错误,便跳出with,继续执行后面的语句,输出end。
  当然将异常部分放到yield的前面,此时不会执行yield后面source的部分,只会输出start 和end。

from contextlib import contextmanager

@contextmanager
def example():
    resource = ['a', 'b', 'c', 'd', 'e']
    print('start')
    try:
        # raise Exception('test')
        yield resource
    finally:
        print('end')


with example() as resource:
    for i in resource:
        print(i)
        if i == 'd':
            raise Exception('test')

'''
start
a
b
c
d
end
Traceback (most recent call last):
  File "example.py", line 18, in <module>
    raise Exception('test')
Exception: test
'''

  将resource从类中调用:

from contextlib import contextmanager
class Resource:
    def query(self):
        print('query data')

@contextmanager
def make_resource():
    print('start to connect')
    yield Resource()
    print('end connect')
    pass

with make_resource() as r:
    r.query()

三.总结

 yield的作用

  yield前面的部分可以看作是一个代码块在执行操作,其后面的部分可以当做contextmanager中exit函数中。

  Python的迭代协议:可迭代的对象定义了一个__next__方法,它要么返回迭代中的下一项,或者引发一个特殊的StopIteration异常来终止迭代。
  要支持这一协议,函数包含一条yield语句,该语句特别编译为生成器。当调用时,它们返回一个迭代器对象,该对象支持用一个名为__next__的自动创建的方法来继续执行的接口。生成器函数也可能有一条return语句,总是在def语句块的末尾,直接终止值的生成。从技术上讲,可以在任何常规函数退出执行之后,引发一个StopIteration异常来实现。从调用者的角度来看,生成器的__next__方法继续函数并且运行到下一个yield结果返回或引发一个StopIteration异常。
  直接效果就是生成器函数,编写为包含yield语句的def语句,自动支持迭代协议,并且由此可能用在任何迭代环境中以随着时间并根据需要产生结果。





参考资料:
[1]:https://docs.python.org/3.6/
[2]:https://www.cnblogs.com/Clisa/p/6106162.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章