上下文管理器允許你在有需要的時候,精確地分配和釋放資源。
使用上下文管理器最廣泛的案例就是with
語句了。
想象下你有兩個需要結對執行的相關操作,然後還要在它們中間放置一段**。
上下文管理器就是專門讓你做這種事情的。舉個例子:
with open('some_file', 'w') as opened_file:
opened_file.write('hola!')
上面這段**開啟了乙個檔案,往裡面寫入了一些資料,然後關閉該檔案。如果在往檔案寫資料時發生異常,它也會嘗試去關閉檔案。上面那段**與這一段是等價的:
file = open('some_file', 'w')
try:
file.write('hola!')
finally:
file.close()
當與第乙個例子對比時,我們可以看到,通過使用with
,許多樣板**(boilerplate code)被消掉了。 這就是with
語句的主要優勢,它確保我們的檔案會被關閉,而不用關注巢狀**如何退出。
上下文管理器的乙個常見用例,是資源的加鎖和解鎖,以及關閉已開啟的檔案(就像我已經展示給你看的)。
讓我們看看如何來實現我們自己的上下文管理器。這會讓我們更完全地理解在這些場景背後都發生著什麼。
乙個上下文管理器的類,最起碼要定義__enter__
和__exit__
方法。
讓我們來構造我們自己的開啟檔案的上下文管理器,並學習下基礎知識。
class
file
(object):
def__init__
(self, file_name, method):
self.file_obj = open(file_name, method)
def__enter__
(self):
return self.file_obj
def__exit__
(self, type, value, traceback):
self.file_obj.close()
通過定義__enter__
和__exit__
方法,我們可以在with
語句裡使用它。我們來試試:
with file('demo.txt', 'w') as opened_file:
opened_file.write('hola!')
我們的__exit__
函式接受三個引數。這些引數對於每個上下文管理器類中的__exit__
方法都是必須的。我們來談談在底層都發生了什麼。
with
語句先暫存了file
類的__exit__
方法
然後它呼叫file
類的__enter__
方法
__enter__
方法開啟檔案並返回給with
語句
開啟的檔案控制代碼被傳遞給opened_file
引數
我們使用.write()
來寫檔案
with
語句呼叫之前暫存的__exit__
方法
__exit__
方法關閉了檔案
接下來我們來看看__exit__
方法的這三個引數:type
,value
和traceback
。
在第4步和第6步之間,如果發生異常,python會將異常的type
,value
和traceback
傳遞給__exit__
方法。
它讓__exit__
方法來決定如何關閉檔案以及是否需要其他步驟。在我們的案例中,我們並沒有注意它們。
那如果我們的檔案物件丟擲乙個異常呢?萬一我們嘗試訪問檔案物件的乙個不支援的方法。舉個例子:
with file('demo.txt', 'w') as opened_file:
opened_file.undefined_function('hola!')
我們來列一下,當異常發生時,with
語句會採取哪些步驟。
1. 它把異常的type
,value
和traceback
傳遞給__exit__
方法
2. 它讓__exit__
方法來處理異常
3. 如果__exit__
返回的是true,那麼這個異常就被優雅地處理了。
4. 如果__exit__
返回的是true以外的任何東西,那麼這個異常將被with
語句丟擲。
在我們的案例中,__exit__
方法返回的是none
(如果沒有return
語句那麼方法會返回none
)。因此,with
語句丟擲了那個異常。
traceback (most recent call last):
file "", line 2, in
attributeerror: 'file' object has no attribute 'undefined_function'
我們嘗試下在__exit__
方法中處理異常:
class
file
(object):
def__init__
(self, file_name, method):
self.file_obj = open(file_name, method)
def__enter__
(self):
return self.file_obj
def__exit__
(self, type, value, traceback):
print("exception has been handled")
self.file_obj.close()
return
true
with file('demo.txt', 'w') as opened_file:
opened_file.undefined_function()
# output: exception has been handled
我們的__exit__
方法返回了true
,因此沒有異常會被with
語句丟擲。
這還不是實現上下文管理器的唯一方式。還有一種方式是基於生成器。
我們還可以用裝飾器(decorators)和生成器(generators)來實現上下文管理器。
python有個contextlib
模組專門用於這個目的。我們可以使用乙個生成器函式來實現乙個上下文管理器,而不是使用乙個類。
讓我們看看乙個基本的,沒用的例子:
from contextlib import contextmanager
@contextmanager
defopen_file
(name):
f = open(name, 'w')
yield f
f.close()
ok啦!這個實現方式看起來更加直觀和簡單。然而,這個方法需要關於生成器、yield
和裝飾器的一些知識。在這個例子中我們還沒有捕捉可能產生的任何異常。它的工作方式和之前的方法大致相同。
讓我們小小地剖析下這個方法。
1. python直譯器遇到了yield
關鍵字。因為這個緣故它建立了乙個生成器而不是乙個普通的函式。
2. 因為這個裝飾器,contextmanager
會被呼叫並傳入函式名(open_file
)作為引數。
3.contextmanager
函式返回乙個以generatorcontextmanager
物件封裝過的生成器。
4. 這個generatorcontextmanager
被賦值給open_file
函式,我們實際上是在呼叫generatorcontextmanager
物件。
那現在我們既然知道了所有這些,我們可以用這個新生成的上下文管理器了,像這樣:
with open_file('some_file') as f:
f.write('hola!')
python上下文管理器
上下文管理器是乙個包裝任意 塊的物件。上下文管理器保證進入上下文管理器時,每次 執行的一致性 當退出上下文管理器時,相關資源會被正確 這裡被正確 指的是在 exit 方法自定義 比如關閉資料庫游標 值得注意的是,上下文管理器一定能夠保證退出步驟的執行。如果進入上下文管理器,根據定義,一定會有退出步驟...
Python 上下文管理器
python中的上下文管理器是乙個包裝任意 塊的物件。它在處理資源的開啟關閉 異常的處理等方面有很好的實現方法。1.上下文管理器的語法 假設我們需要讀取乙個檔案中的資料,如下 try test file open test.txt r contents test file.read finally ...
python上下文管理器
上下文管理器可以通過使用更可讀 更精簡的 實現資源的分配與釋放 對於上下文管理器的使用,最常見的是使用with語句,with語句可構建資源的分配與釋放的語法糖。先拿最常見的例子來說,即檔案的開啟與關閉。正常語法 f file filename,w try f.write test file fina...