Python中的上下文管理器


操作文件對象時可以:

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

上述叫做上下文管理協議,即with語句。

想象一下,你有兩個需要結對執行的相關操作,然后,還要在他們中間放置一段代碼。比如打開一個文件,操作文件,然后關閉該文件。

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

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

上下文管理器

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

就跟迭代器有迭代器協議一樣,迭代器協議需要實現 __ iter __ 和 __ next __ 方法。

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

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

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):

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

注意: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('我是你爹')


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM