一 、with語句的原理
- 上下文管理協議(Context Management Protocol):包含方法
__enter__()和__exit__(),支持該協議的對象要實現這兩個方法。 - 上下文管理器(Context Manager):支持上下文管理協議的對象,這種對象實現了
__enter__()和__exit__()方法。上下文管理器定義執行with語句時要建立的運行時上下文,負責執行with語句塊上下文中的進入與退出操作。通常使用with語句調用上下文管理器,也可以通過直接調用其方法來使用。
說完上面兩個概念,我們再從with語句的常用表達式入手,一段基本的with表達式,其結構是這樣的:
1 with EXPR as VAR: 2 3 BLOCK
其中EXPR可以是任意表達式;as VAR是可選的。其一般的執行過程是這樣的:
- 執行EXPR,生成上下文管理器context_manager;
- 獲取上下文管理器的
__exit()__方法,並保存起來用於之后的調用; - 調用上下文管理器的
__enter__()方法;如果使用了as子句,則將__enter__()方法的返回值賦值給as子句中的VAR; - 執行BLOCK中的表達式;
- 不管是否執行過程中是否發生了異常,執行上下文管理器的
__exit__()方法,__exit__()方法負責執行“清理”工作,如釋放資源等。如果執行過程中沒有出現異常,或者語句體中執行了語句break/continue/return,則以None作為參數調用__exit__(None, None, None);如果執行過程中出現異常,則使用sys.exc_info得到的異常信息為參數調用__exit__(exc_type, exc_value, exc_traceback); - 出現異常時,如果
__exit__(type, value, traceback)返回False,則會重新拋出異常,讓with之外的語句邏輯來處理異常,這也是通用做法;如果返回True,則忽略異常,不再對異常進行處理。
二、自定義上下文管理器
Python的with語句是提供一個有效的機制,讓代碼更簡練,同時在異常產生時,清理工作更簡單。
# coding = utf-8 # 2019/7/19 Luckyxxt:有趣的事,Python永遠不會缺席! #!/usr/bin/env python class DBManager(object): def __init__(self): pass def __enter__(self): print('__enter__') return self def __exit__(self, exc_type, exc_val, exc_tb): print('__exit__') return True def getInstance(): return DBManager() with getInstance() as dbManagerIns: print('with demo')
with后面必須跟一個上下文管理器,如果使用了as,則是把上下文管理器的 __enter__() 方法的返回值賦值給 target,target 可以是單個變量,或者由“()”括起來的元組(不能是僅僅由“,”分隔的變量列表,必須加“()”)
代碼運行結果如下:
1 ''' 2 __enter__ 3 with demo 4 __exit__ 5 6 '''
結果分析:當我們使用with的時候,__enter__方法被調用,並且將返回值賦值給as后面的變量,並且在退出with的時候自動執行__exit__方法
1 class With_work(object):
2 def __enter__(self):
3 """進入with語句的時候被調用"""
4 print('enter called')
5 return "xxt"
6
7 def __exit__(self, exc_type, exc_val, exc_tb):
8 """離開with的時候被with調用"""
9 print('exit called')
10
11
12 with With_work() as f:
13 print(f)
14 print('hello with')
1 ''' 2 enter called 3 xxt 4 hello with 5 exit called 6 7 '''
三、總結
自定義上下文管理器來對軟件系統中的資源進行管理,比如數據庫連接、共享資源的訪問控制等。
轉自:https://www.cnblogs.com/xxtalhr/p/11211347.html#_labelTop

