Python中的上下文管理器

操做文件對象時能夠:python

with open('a.txt') as f:
    '代碼塊'

上述叫作上下文管理協議,即with語句。編程

想象一下,你有兩個須要結對執行的相關操做,而後,還要在他們中間放置一段代碼。好比打開一個文件,操做文件,而後關閉該文件。網絡

打開文件和關閉文件就是一個結對的操做。函數

上下文管理器的常見用例:是資源的加鎖與解鎖,文件的打開與關閉。code

上下文管理器

上下文管理器協議:是指類須要實現 __ enter __ 和 __ exit __ 方法。對象

就跟迭代器有迭代器協議同樣,迭代器協議須要實現 __ iter __ 和 __ next __ 方法。utf-8

上下文管理器,也就是支持上下文管理協議的對象,簡單點講就是,實現了 __ enter __ 和 __ exit __兩個方法的類。這個類也叫作,上下文管理器的類。資源

寫一個Open類,這個類是一個上下文管理器:it

class Open:
    def __init__(self, filepath, encoding):
        self.filepath = filepath
        self.encoding = encoding

    def __enter__(self): # 當這個類被with關鍵字執行時,就自動調用這個方法。有返回值則調用給 as 聲明的變量
        print('當這個類被with關鍵字執行時,就自動調用這個方法。有返回值則調用給 as 聲明的變量')

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('with 中代碼塊執行完就執行我這個函數')


with Open('1.txt', 'UTF-8') as f:
    print('with 裏面的代碼塊')
'''
結果:
當這個類被with關鍵字執行時,就自動調用這個方法。有返回值則調用給 as 聲明的變量
with 裏面的代碼塊
with 中代碼塊執行完就執行我這個函數
'''

__ exit __(self, exc_type, exc_val, exc_tb):class

裏面的三個參數分別表明:異常類型,異常值,追溯信息。

注意:with語句中的代碼塊出現異常後,with後的代碼都沒法執行

基於類的實現:完整實現Open方法

一個上下文管理器的類,起碼要定義 __ enter __ 和 __ exit __ 方法。

class Open:
    def __init__(self, filepath, method):
        self.file = open(filepath, method, encoding='utf-8')

    def __enter__(self):
        return self.file

    def __exit__(self, type, value, traceback):
        self.file.close()


with Open('1.txt', 'w') as f:
    f.write('1111111111')

咱們來看看底層發生了什麼?

  1. with語句先暫存了 Open 類的 __ exit __ 方法
  2. 而後調用 Open 類的 __ enter __ 方法
  3. __ enter __ 方法打開文件並返回給with語句
  4. 打開的文件句柄傳遞給 as 後面的 f 參數
  5. 執行with裏面的代碼塊。
  6. 調用以前暫存的 __ exit __ 方法
  7. 關閉文件

在第4步和第6步之間,若是發生異常,Python會將異常的type,value,traceback傳遞給 __ exit __ 方法。

當異常發生時,with語句會採起哪些步驟?

  1. with把異常的type, value, traceback 傳遞給 __ exit __ 方法
  2. with讓 __ exit __ 處理異常
  3. 若是 __ exit __ 返回的是True, 那麼這個異常就被優雅的處理了。
  4. 若是 __ exit __ 返回的是True之外的任何東西,那個這個異常將被with 語句拋出。

當 __ exit __()返回值爲True, 那麼異常會被清空,就好像啥都沒發生同樣,with後的語句正常執行.。

完整模擬Open:

class Open:
    def __init__(self, filepath, mode='r', encoding='utf-8'):
        self.filepath = filepath
        self.mode = mode
        self.encoding = encoding

    def __enter__(self):
        self.file = open(self.filepath, mode=self.mode, encoding=self.encoding)
        return self.file

    def __exit__(self, exc_type, exc_val, exc_tb):
        print(exc_type)
        self.file.close()
        return True


with Open('1.txt', 'w', encoding='utf-8') as f:
    f.write('哈哈哈')
    f.werwer # 拋出異常,交給exit處理。後面的代碼正常運行

優勢

  1. 使用with的語句的目的就是把代碼塊放入with中執行, with結束後,自動完成清理工做,無需干預。
  2. 在須要管理一些資源好比文件,網絡鏈接和鎖的編程環境中,能夠在 __ exit __ 中定製自動釋放資源的機制。

基於生成器實現一個上下文管理器

contextlib模塊:可使用一個生成器實現一個上下文管理器,而不是使用一個類。衆所周知,在類中還須要實現 __ enter __ 和 __ exit __ 。

from contextlib import contextmanager

@contextmanager
def point(x, y):
    print('在yield以前')

    yield x * y  # yield出去的值賦給 as 後面的變量

    print('在yield以後')


with point(3, 4) as p:
    print('p',p)
    
    
'''
結果:
在yield以前
p 12
在yield以後
'''

利用contextlib模塊實現一個open

@contextmanager
def my_open(path):

    f = open(path, mode='w')

    yield f  # 把這個f 賦給as後面的變量

    f.close()

with my_open('2.txt') as f:

    f.write('我是你爹')
相關文章
相關標籤/搜索