1. 程式人生 > 程式設計 >Python實現上下文管理器的方法

Python實現上下文管理器的方法

問題

你想自己去實現一個新的上下文管理器,以便使用with語句。

解決方案

實現一個新的上下文管理器的最簡單的方法就是使用 contexlib 模組中的 @contextmanager 裝飾器。 下面是一個實現了程式碼塊計時功能的上下文管理器例子:

import time
from contextlib import contextmanager

@contextmanager
def timethis(label):
  start = time.time()
  try:
    yield
  finally:
    end = time.time()
    print('{}: {}'.format(label,end - start))

# Example use
with timethis('counting'):
  n = 10000000
  while n > 0:
    n -= 1

在函式 timethis() 中,yield 之前的程式碼會在上下文管理器中作為 __enter__() 方法執行, 所有在 yield 之後的程式碼會作為 __exit__() 方法執行。 如果出現了異常,異常會在yield語句那裡丟擲。

下面是一個更加高階一點的上下文管理器,實現了列表物件上的某種事務:

@contextmanager
def list_transaction(orig_list):
  working = list(orig_list)
  yield working
  orig_list[:] = working

這段程式碼的作用是任何對列表的修改只有當所有程式碼執行完成並且不出現異常的情況下才會生效。 下面我們來演示一下:

>>> items = [1,2,3]
>>> with list_transaction(items) as working:
...   working.append(4)
...   working.append(5)
...
>>> items
[1,3,4,5]
>>> with list_transaction(items) as working:
...   working.append(6)
...   working.append(7)
...   raise RuntimeError('oops')
...
Traceback (most recent call last):
  File "<stdin>",line 4,in <module>
RuntimeError: oops
>>> items
[1,5]
>>>

討論

通常情況下,如果要寫一個上下文管理器,你需要定義一個類,裡面包含一個 __enter__() 和一個 __exit__() 方法,如下所示:

import time

class timethis:
  def __init__(self,label):
    self.label = label

  def __enter__(self):
    self.start = time.time()

  def __exit__(self,exc_ty,exc_val,exc_tb):
    end = time.time()
    print('{}: {}'.format(self.label,end - self.start))

儘管這個也不難寫,但是相比較寫一個簡單的使用 @contextmanager 註解的函式而言還是稍顯乏味。

@contextmanager 應該僅僅用來寫自包含的上下文管理函式。 如果你有一些物件(比如一個檔案、網路連線或鎖),需要支援 with 語句,那麼你就需要單獨實現 __enter__() 方法和 __exit__() 方法。

以上就是Python實現上下文管理器的方法的詳細內容,更多關於Python實現上下文管理器的資料請關注我們其它相關文章!