上下文管理器,英文context managers,在python官方文檔中這樣描述:
上下文管理器是一個對象,它定義了在執行 with 語句時要建立的運行時上下文。 上下文管理器處理進入和退出所需運行時上下文以執行代碼塊。 通常使用 with 語句(在 with 語句中描述),但是也可以通過直接調用它們的方法來使用。
那麼可能還會有進一步的疑問:"上下文"又是什麼呢?知乎某大佬關於"上下文"的解釋
如果還是難懂,那就舉個例子。在python中,寫入文件的基本操作可能是這樣的:1f = open('a.txt', 'w')
2f.write('22')
3f.close()
1f = open('a.txt', 'w')
2try:
3 f.write('22')
4except:
5 print('File eroor!')
6finally:
7 f.close()
1with open('b.txt', 'w') as f:
2 f.write('22')
可能會有這樣疑問,不就是節省了一行文件關閉的代碼而已嘛,也沒看出有多高效啊!
這裡先拋出結論:使用with管理上下文不僅可以在執行with語句體後自動執行退出操作(即__exit__方法定義語句),更重要的是能夠在發生異常時,確保始終能執行退出操作、釋放相應的資源。
在PEP343(該PEP文檔由python之父龜叔發起,最早在python2.5版本引入。如想了解更多關於PEP知識,可閱讀PEP入門指南),對上下文管理器給出如下定義:
上下文管理器是指提供了一對專門方法__enter __()和__exit __(),這些方法在with語句的主體進入和退出時被調用。在Python語言中添加了一個「 with」新語句,從而可以排除try / finally語句的標準用法。也就是說,如果某個類定義了__enter__、__exit__方法,允許在語句體執行前進入該上下文、執行後退出該上下文,那麼這樣的類就可以稱作是上下文管理器對象。而且該用法可用作對try/finally的替代,以處理可能存在的異常。
前面舉了文件操作的with用法,說明文件操作的對象是一個上下文管理器對象,那麼我們先來看下文件操作類來怎樣定義的: 1##_pyio.py
2### Context manager ###
3def __enter__(self): # That's a forward reference
4 """Context management protocol. Returns self (an instance of IOBase)."""
5 self._checkClosed()
6 return self
7
8def __exit__(self, *args):
9 """Context management protocol. Calls close()"""
10 self.close()
1"""IOBase also supports the :keyword:`with` statement. In this example,
2fp is closed after the suite of the with statement is complete:
3
4with open('spam.txt', 'r') as fp:
5 fp.write('Spam and eggs!')"""
1from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
2def multi_thread():
3 with ThreadPoolExecutor() as executor:
4 return list(executor.map(is_prime, PRIMES))
5
6def multi_process():
7 with ProcessPoolExecutor() as executor:
8 return list(executor.map(is_prime, PRIMES))
1##_base.py
2def __enter__(self):
3 return self
4
5def __exit__(self, exc_type, exc_val, exc_tb):
6 self.shutdown(wait=True)
7 return False
註:ThreadPoolExecutor和ProcessPoolExecutor兩個類均繼承自Executor類,而Excutor類是一個上下文管理器對象。
需補充說明的是,正如上述示例代碼給出的那樣,__exit__()方法默認接收3個參數:exc_type、exc_val、exc_tb,如果with語句體發生異常,則3個參數分別賦值為type(error)、str(error)、error.__traceback__,否則均為None。
至此,我們可以得出這樣的結論:一個上下文管理器對象是指定義了__enter__和__exit__方法的類對象;反之,定義了__enter__和__exit__方法的類可以應用with包裝實現上下文管理器用法。
應用with包裝上下文管理器時:執行with語句生成一個實例對象時,自動調用__enter__方法創建一個適用於上下文環境的類對象,完畢後無論是否觸發異常都會調用__exit__方法執行退出操作。
實際上,上下文管理器常適用於帶有資源管理的操作,如前面例子中給出的文件操作和並發操作。這裡,我們舉一個操作資料庫的例子,實現一個簡單的打開資料庫類。 1import pymysql
2class OpenMySQL(object):
3 def __init__(self, db):
4 self.conn = pymysql.connect(host="localhost", user="root", password="123456", db=db, charset='utf8')
5
6 def __enter__(self):
7 return self.conn.cursor()
8
9 def __exit__(self, exc_type, exc_val, exc_tb):
10 self.conn.commit()
11 self.conn.close()
12
13if __name__ == '__main__':
14 with OpenMySQL('mytest') as sql:
15 sql_insert = 'insert into tbtest values (2);'
16 sql.execute(sql_insert)
代碼功能執行正常,且相比於基本的資料庫connect()--commit()--close()操作流程來說,要優雅許多。
註:寫完例子後查閱源碼發現pymysql.connect方法返回對象本身就是一個上下文管理器對象……。但僅僅用於舉例的話,這也足以說明with包裝上下文管理器的用法了。
本文對python中上下文管理器和with用法進行了簡單介紹,包括:
上下文管理器通常使用with語句實現
with語句可實現簡潔版的try/finally替代用法相關閱讀: