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

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