上下文管理器及with的相關總結

  • 2019 年 11 月 8 日
  • 筆記

什麼是上下文管理器

基本語法

with EXPR as VAR:      BLOCK

概念

  • 上下文表達式:with open(‘test.txt’) as f:
  • 上下文管理器:open(‘test.txt’)
  • f 不是上下文管理器,應該是資源對象

作用

  • with語句就是簡潔版的try/finally語句
  • 程式碼塊前後必然會執行的內容

原理

**上下文管理器是內部實現了__enter__和__exit__方法的對象**

class Foo:      def __init__(self):          print("實例化一個對象")        def __enter__(self):          print("進入")          return self        def __exit__(self, exc_type, exc_val, exc_tb):          print("退出")          return True        def func(self):          print("被執行的方法")      with Foo() as f:      f.func()      >>>實例化一個對象  >>>進入  >>>被執行的方法  >>>退出
  • __enter__方法說明

    上下文管理器的__enter__方法是可以帶返回值的,默認返回None,這個返回值通過with...as...中的 as 賦給它後面的那個變數,所以 with EXPR as VAR 就是將EXPR對象__enter__方法的返回值賦給 VAR,VAR可以是單個變數,或者由「()」括起來的元組(不能是僅僅由「,」分隔的變數列表,必須加「()」)。    with...as...並非固定組合,單獨使用with...也是可以的,上下文管理器的__enter__方法還是正常執行,只是這個返回值並沒有賦給一個變數,with下面的程式碼塊也不能使用這個返回值。
  • __exit__方法說明

    上下文管理器的__exit__方法接收3個參數exc_type、exc_val、exc_tb,如果程式碼塊BLOCK發生了異常e並退出,這3個參數分別為type(e)、str(e)、e.__traceback__,否則都為None。    同樣__exit__方法也是可以帶返回值的,這個返回值應該是一個布爾類型True或False,默認為None(即False)。如果為False,異常會被拋出,用戶需要進行異常處理。如果為True,則表示忽略該異常。

上下文管理器的使用

異常處理

​ 處理異常,通常都是使用 try...execept.. 來捕獲處理的。這樣做一個不好的地方是,在程式碼的主邏輯里,會有大量的異常處理代理,這會很大的影響我們的可讀性。

好一點的做法呢,可以使用 with 將異常的處理隱藏起來。

仍然是以上面的程式碼為例,我們將1/0 這個一定會拋出異常的程式碼寫在 func

__exit__ 函數的三個參數

  • exc_type:異常類型
  • exc_val:異常值
  • exc_tb:異常的錯誤棧資訊
class Foo:      def __init__(self):          print("實例化一個對象")        def __enter__(self):          print("進入")          return self        def __exit__(self, exc_type, exc_val, exc_tb):          import traceback          if exc_val:              print("異常類型:",exc_type)              print("異常值:",exc_val)              traceback.print_tb(exc_tb,-1)#列印最開始的錯誤資訊,可設置錯誤棧數以及寫入文件          print("退出")          return True        def func(self):          print(1 / 0)          print("被執行的方法")    with Foo() as f:      f.func()    ---------------------------------------------  實例化一個對象  進入    File "F:/PyProgram/test98/test1.py", line 62, in func      print(1 / 0)  異常類型: <class 'ZeroDivisionError'>  異常值: division by zero  退出

資源管理

​ 在我們日常使用場景中,經常會操作一些資源,比如文件對象、資料庫連接、Socket連接等,資源操作完了之後,不管操作的成功與否,最重要的事情就是關閉該資源,否則資源打開太多而沒有關閉,程式會報錯 。

常見的上下文管理器

file  decimal.Context  thread.LockType  threading.Lock  threading.RLock  threading.Condition  threading.Semaphore  threading.BoundedSemaphore

理解並使用 contextlib

​ Python還提供了一個contextmanager裝飾器,允許用戶將一個生成器定義為上下文管理器,該裝飾器將生成器中的程式碼通過yield語句分成兩部分,yield之前的程式碼為__enter__方法,yield之後的程式碼為__exit__方法,yield的返回值即__enter__方法的返回值,用於賦給as後的變數。

實現資源管理

import contextlib    @contextlib.contextmanager  def open_func(file_name):      # __enter__方法      print('open file:', file_name, 'in __enter__')      file_handler = open(file_name, 'r')        # 【重點】:yield      yield file_handler        # __exit__方法      print('close file:', file_name, 'in __exit__')      file_handler.close()      return    with open_func('userinfo.txt') as file_in:      for line in file_in:          print(line)

實現異常捕獲

import contextlib  import traceback  import sys  @contextlib.contextmanager  def open_func(file_name):      # __enter__方法      print('open file:', file_name, 'in __enter__')      file_handler = open(file_name, 'r')        try:          yield file_handler      except Exception as exc:          exc_type, exc_val, exc_tb = sys.exc_info()          print("異常類型:", exc_type)          print("異常值:", exc_val)          traceback.print_tb(exc_tb, -1)          print('the exception was thrown')      finally:          print('close file:', file_name, 'in __exit__')          file_handler.close()            return    with open_func('userinfo.txt') as file_in:      for line in file_in:          print(1/0)          print(line)