python 上下文管理器

2021-08-09 10:28:54 字數 4617 閱讀 6829

上下文管理器允許你在有需要的時候,精確地分配和釋放資源。

使用上下文管理器最廣泛的案例就是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,valuetraceback

在第4步和第6步之間,如果發生異常,python會將異常的type,valuetraceback傳遞給__exit__方法。

它讓__exit__方法來決定如何關閉檔案以及是否需要其他步驟。在我們的案例中,我們並沒有注意它們。

那如果我們的檔案物件丟擲乙個異常呢?萬一我們嘗試訪問檔案物件的乙個不支援的方法。舉個例子:

with file('demo.txt', 'w') as opened_file:

opened_file.undefined_function('hola!')

我們來列一下,當異常發生時,with語句會採取哪些步驟。 

1. 它把異常的type,valuetraceback傳遞給__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...