經常在python**中看到with語句,仔細分析下,會發現這個with語句功能好強,可以自動關閉資源。這個在python中叫上下文管理器context manager。那我們要怎麼用它,什麼時候用它呢。這裡我們就來聊一聊。
很多情況,當我們使用完乙個資源後,我們需要手動的關閉掉它,比如操作檔案,建立資料庫連線等。但是,在使用資源的過程中,如果遇到異常,很可能錯誤被直接丟擲,導致來不及關閉資源。所以在大部分程式語言裡,我們使用」try-finally」語句來確保資源會關閉。比如下面的python寫檔案**:
try:
f = open('test.txt', 'a+')
f.write('foo\n')
finally:
f.close()
這樣做固然沒有問題,但是當」try-finally」中間的邏輯複雜,而且還帶有各種巢狀的話,**就很不容易維護。python的with語句,可以說功能同上面的」try-finally」幾乎一樣,但**看上去簡潔的多,我們來實現同樣的功能:
with
open('test.txt', 'a+') as f:
f.write('foo\n')
with語句後面跟著open()方法,如果它有返回值的話,可以使用as語句將其賦值給f。在with語句塊退出時,」f.close()」方法會自動被呼叫,即使」f.write()」出現異常,也能確保close()方法被呼叫。
上例中」open()」方法是python自帶的,那我們怎麼定義自己的型別來使用with語句呢。其實只要你的類定義了」enter()」和」exit()」方法,就可以使用python的上下文管理器了。」enter()」方法會在with語句進入時被呼叫,其返回值會賦給as關鍵字後的變數;而」exit()」方法會在with語句塊退出後自動被呼叫。
我們來實現個跟上節一樣的檔案寫入功能:
class
openfiledemo
(object):
def__init__
(self, filename):
self.filename = filename
def__enter__
(self):
self.f = open(self.filename, 'a+')
return self.f
def__exit__
(self, exc_type, exc_val, exc_tb):
self.f.close()
with openfiledemo('test.txt') as f:
f.write('foo\n')
肯定有朋友注意到上面的」exit()」帶了三個引數,是的,他們是用來異常處理的。大部分情況下,我們希望with語句中遇到的異常最後被丟擲,但也有時候,我們想處理這些異常。」exit()」方法中的三個引數exc_type, exc_val, exc_tb分別代表異常型別,異常值,和異常的traceback。當你處理完異常後,你可以讓」exit()」方法返回true,此時該異常就會不會再被丟擲。比如我們將上例中的」exit()」方法改一下:
def
__exit__
(self, exc_type, exc_val, exc_tb):
self.f.close()
if exc_type != syntaxerror:
return
true
return
false
# only raise exception when syntaxerror
現在,如果遇到syntaxerror的話,異常會被正常丟擲,而其他異常的話都會被忽略。
python中還有乙個contextlib模組提供一些簡便的上下文管理器功能。
closing()方法
如果說with語句塊在退出時會自動呼叫」exit()」方法的話,那用了」contextlib.closing()」的with語句塊則在退出時會自動呼叫」close()」方法。看一下示例:
import contextlib
class
resource
(object):
defopen
(self):
print
'open resource'
defclose
(self):
print
'close resource'
with contextlib.closing(resource()) as r:
r.open()
程式執行後,會列印出
open resource
close resource
說明resource類建立的物件被賦給了as關鍵字後面的變數r,而with語句塊退出時,自動呼叫了」r.close()」方法。
「@contextlib.contextmanager」是乙個裝飾器,由它修飾的方法會有兩部分構成,中間由yield關鍵字分開。由此方法建立的上下文管理器,在**塊執行前會先執行yield上面的語句;在**塊執行後會再執行yield下面的語句。看個例子比較容易明白:
import contextlib
import time
@contextlib.contextmanager
deftimeit
(): start = time.time()
yield
end = time.time()
usedtime = (end - start) * 1000
print
'use time %d ms' % usedtime
with timeit():
time.sleep(1)
這個」timeit()」方法實現了乙個計時器,它會計算由他生成的with語句塊執行時間。可以看出,yield上面的語句就如同之間介紹過的」enter()」方法,而yield下面的語句就如同」exit()」方法。而yield部分就是with語句塊中的**。如果yield後面帶引數的話,我們就可以用as關鍵字賦值給後面的變數,比如上例:
@contextlib.contextmanager
deftimeit
(): start = time.time()
yield start
#...
with timeit() as starttime:
print starttime
#...
需要注意的是,」@contextlib.contextmanager」不像之前介紹的」exit()」方法,遇到異常也會執行。也就是with語句塊丟擲異常的話,yield後面的**將不會被執行。所以,必要時你需要對yield語句使用」try-finally」。
**:
python 上下文管理器
上下文管理器允許你在有需要的時候,精確地分配和釋放資源。使用上下文管理器最廣泛的案例就是with語句了。想象下你有兩個需要結對執行的相關操作,然後還要在它們中間放置一段 上下文管理器就是專門讓你做這種事情的。舉個例子 with open some file w as opened file open...
python上下文管理器
上下文管理器是乙個包裝任意 塊的物件。上下文管理器保證進入上下文管理器時,每次 執行的一致性 當退出上下文管理器時,相關資源會被正確 這裡被正確 指的是在 exit 方法自定義 比如關閉資料庫游標 值得注意的是,上下文管理器一定能夠保證退出步驟的執行。如果進入上下文管理器,根據定義,一定會有退出步驟...
Python 上下文管理器
python中的上下文管理器是乙個包裝任意 塊的物件。它在處理資源的開啟關閉 異常的處理等方面有很好的實現方法。1.上下文管理器的語法 假設我們需要讀取乙個檔案中的資料,如下 try test file open test.txt r contents test file.read finally ...