簡(jiǎn)單介紹Python中的try和finally和with方法
用 Python 做一件很平常的事情: 打開文件, 逐行讀入, 最后關(guān)掉文件; 進(jìn)一步的需求是, 這也許是程序中一個(gè)可選的功能, 如果有任何問題, 比如文件無法打開, 或是讀取出錯(cuò), 那么在函數(shù)內(nèi)需要捕獲所有異常, 輸出一行警告并退出. 代碼可能一開始看起來是這樣的
def read_file():
try:
f = open('yui', 'r')
print ''.join(f.readlines())
except:
print 'error occurs while reading file'
finally:
f.close()
不過這顯然無法運(yùn)作, 因?yàn)?nbsp; f 是在 try 塊中定義的, 而在 finally 中無法引用.
如果將 f 提取到 try 塊外部, 如
def read_file():
f = open('azusa', 'r')
try:
print ''.join(f.readlines())
except:
print 'error occurs while reading file'
finally:
f.close()
那么, 問題在于當(dāng)打開文件失敗, 拋出異常將不會(huì)被捕獲.
挫一點(diǎn)的方法自然是, 再套一層 try 吧
def read_file():
try:
f = open('sawako', 'r')
try:
print ''.join(f.readlines())
except:
print 'error occurs while reading file'
finally:
f.close()
except:
print 'error occurs while reading file'
當(dāng)然這不僅僅是多一層縮進(jìn)挫了, 連警告輸出都白白多一次呢.
正規(guī)一點(diǎn)的方式是, 使用 Python 引入的 with 結(jié)構(gòu)來解決, 如
def readFile():
try:
with open('mio', 'r') as f:
print ''.join(f.readlines())
except:
print 'error occurs while reading file'
當(dāng)文件打開失敗時(shí), 異常自然會(huì)被 except 到; 否則, 在 with 塊結(jié)束之后, 打開的文件將自動(dòng)關(guān)閉.
除了打開文件, 還有其它這樣可以用于 with 的東西么? 或者說, 怎么自定義一個(gè)什么東西, 讓它能用于 with 呢?
直接回答后一個(gè)問題吧, 秘密在于 Python 虛擬機(jī)在 with 塊退出時(shí)會(huì)去尋找對(duì)象的 __exit__ 方法并調(diào)用它, 把釋放資源的動(dòng)作放在這個(gè) __exit__ 函數(shù)中就可以了; 另外, 對(duì)象還需要一個(gè) __enter__ 函數(shù), 當(dāng)進(jìn)入 with 塊時(shí), 這個(gè)函數(shù)被調(diào)用, 而它的返回值將作為 as 后引用的值. 一個(gè)簡(jiǎn)單的例子是
class Test:
def __init__(self):
print 'init'
def __enter__(self):
print 'enter'
return self
def __exit__(self, except_type, except_obj, tb):
print except_type
print except_obj
import traceback
print ''.join(traceback.format_tb(tb))
print 'exit'
return True
with Test() as t:
raise ValueError('kon!')
執(zhí)行這一段代碼, 輸出將會(huì)是
init
enter
<type 'exceptions.ValueError'>
kon!
File "test.py", line 17, in <module>
raise ValueError('kon!')
exit
__exit__ 函數(shù)接受三個(gè)參數(shù), 分別是異常對(duì)象類型, 異常對(duì)象和調(diào)用棧. 如果 with 塊正常退出, 那么這些參數(shù)將都是 None . 返回 True 表示發(fā)生的異常已被處理, 不再繼續(xù)向外拋出.
簡(jiǎn)單的介紹到此為止, 詳細(xì)的情況可以參考 PEP 343 (這數(shù)字真不錯(cuò), 7 3 ).
下面介紹下 with 語句的實(shí)例用法 & 高級(jí)用法:
Python高端、大氣、上檔次的with語句
在說with語句之前,先看看一段簡(jiǎn)單的代碼吧
lock = threading.Lock() ... lock.acquire() elem = heapq.heappop(heap) lock.release()
很簡(jiǎn)單直觀,多個(gè)線程共用一個(gè)優(yōu)先級(jí)隊(duì)列的時(shí)候,首先先用互斥鎖lock.acquire()把優(yōu)先級(jí)隊(duì)列鎖上,然后取元素,再然后lock.release()釋放這個(gè)鎖。
雖然看似非常符合邏輯的一個(gè)過程,但是里面隱藏著一個(gè)巨大的bug:當(dāng)heap里面沒有元素的時(shí)候,會(huì)拋出一個(gè)IndexError異常,再然后堆?;貪L,再然后lock.release()根本不會(huì)執(zhí)行,這個(gè)鎖就永遠(yuǎn)得不到釋放,因此就發(fā)生了喜聞樂見的死鎖問題。這個(gè)也是很多大神們討厭異常的原因。經(jīng)典Java風(fēng)格的解決方案就是
lock = threading.Lock() ... lock.acquire() try: elem = heapq.heappop(heap) finally: lock.release()
這個(gè)雖然可以,但是怎么看怎么dirty,和Python優(yōu)雅、簡(jiǎn)單的風(fēng)格出入很大。其實(shí),自從Python2.5開始引入了with語句,一切就變得非常簡(jiǎn)單:
lock = threading.Lock() ... with lock: elem = heapq.heappop(heap)
在此無論以何種方式離開with語句的代碼塊,鎖都會(huì)被釋放。
with語句的設(shè)計(jì)目的就是為了使得之前需要通過try...finally解決的清理資源問題變得簡(jiǎn)單、清晰,它的的用法是
with expression [as variable]: with-block
其中expression返回一個(gè)叫做「context manager」的對(duì)象,然后這個(gè)對(duì)象被賦給variable(如果有的話)?!竎ontext manager」對(duì)象有兩個(gè)方法,分別是__enter__()和__exit__(),很明顯一個(gè)在進(jìn)入with-block時(shí)調(diào)用,一個(gè)離開with-block的時(shí)候調(diào)用。
這樣的對(duì)象不需要自己去實(shí)現(xiàn),在Python標(biāo)準(zhǔn)庫里面很多API都是已經(jīng)實(shí)現(xiàn)了這兩個(gè)方法,最常見的一個(gè)例子就是讀寫文件的open語句。
with open('1.txt', encoding = 'utf-8') as fp:
lines = fp.readlines()
無論是正常離開還是因?yàn)楫惓T螂x開with語句塊,打開的文件資源總是會(huì)釋放。
接下去討論一下with語句配合contextlib庫的一些比較實(shí)用的方法,比如需要同時(shí)打開兩個(gè)文件,一個(gè)讀一個(gè)寫,這個(gè)時(shí)候就可以這樣寫:
from contextlib import nested
...
with nested(open('in.txt'), open('out.txt', 'w')) as (fp_in, fp_out):
...
這樣就可以省掉兩個(gè)with的語句的嵌套了,另外如果遇到一些還沒有支持「context manager」的API呢?比如urllib.request.urlopen(),這個(gè)返回的對(duì)象因?yàn)椴皇恰竎ontext manager」,結(jié)束的時(shí)候還需要自己去調(diào)用close方法。
類似這種API,contextlib提供了一個(gè)叫做closing方法,它會(huì)在離開with語句的時(shí)候,自動(dòng)調(diào)用對(duì)象的close方法,因此urlopen也可以這樣寫:
from contextlib import closing
...
with closing(urllib.request.urlopen('http://www.yahoo.com')) as f:
for line in f:
sys.stdout.write(line)
用 Python 做一件很平常的事情: 打開文件, 逐行讀入, 最后關(guān)掉文件; 進(jìn)一步的需求是, 這也許是程序中一個(gè)可選的功能, 如果有任何問題, 比如文件無法打開, 或是讀取出錯(cuò), 那么在函數(shù)內(nèi)需要捕獲所有異常, 輸出一行警告并退出. 代碼可能一開始看起來是這樣的
def read_file():
try:
f = open('yui', 'r')
print ''.join(f.readlines())
except:
print 'error occurs while reading file'
finally:
f.close()
不過這顯然無法運(yùn)作, 因?yàn)?nbsp; f 是在 try 塊中定義的, 而在 finally 中無法引用.
如果將 f 提取到 try 塊外部, 如
def read_file():
f = open('azusa', 'r')
try:
print ''.join(f.readlines())
except:
print 'error occurs while reading file'
finally:
f.close()
那么, 問題在于當(dāng)打開文件失敗, 拋出異常將不會(huì)被捕獲.
挫一點(diǎn)的方法自然是, 再套一層 try 吧
def read_file():
try:
f = open('sawako', 'r')
try:
print ''.join(f.readlines())
except:
print 'error occurs while reading file'
finally:
f.close()
except:
print 'error occurs while reading file'
當(dāng)然這不僅僅是多一層縮進(jìn)挫了, 連警告輸出都白白多一次呢.
正規(guī)一點(diǎn)的方式是, 使用 Python 引入的 with 結(jié)構(gòu)來解決, 如
def readFile():
try:
with open('mio', 'r') as f:
print ''.join(f.readlines())
except:
print 'error occurs while reading file'
當(dāng)文件打開失敗時(shí), 異常自然會(huì)被 except 到; 否則, 在 with 塊結(jié)束之后, 打開的文件將自動(dòng)關(guān)閉.
除了打開文件, 還有其它這樣可以用于 with 的東西么? 或者說, 怎么自定義一個(gè)什么東西, 讓它能用于 with 呢?
直接回答后一個(gè)問題吧, 秘密在于 Python 虛擬機(jī)在 with 塊退出時(shí)會(huì)去尋找對(duì)象的 __exit__ 方法并調(diào)用它, 把釋放資源的動(dòng)作放在這個(gè) __exit__ 函數(shù)中就可以了; 另外, 對(duì)象還需要一個(gè) __enter__ 函數(shù), 當(dāng)進(jìn)入 with 塊時(shí), 這個(gè)函數(shù)被調(diào)用, 而它的返回值將作為 as 后引用的值. 一個(gè)簡(jiǎn)單的例子是
class Test:
def __init__(self):
print 'init'
def __enter__(self):
print 'enter'
return self
def __exit__(self, except_type, except_obj, tb):
print except_type
print except_obj
import traceback
print ''.join(traceback.format_tb(tb))
print 'exit'
return True
with Test() as t:
raise ValueError('kon!')
執(zhí)行這一段代碼, 輸出將會(huì)是
init
enter
<type 'exceptions.ValueError'>
kon!
File "test.py", line 17, in <module>
raise ValueError('kon!')
exit
__exit__ 函數(shù)接受三個(gè)參數(shù), 分別是異常對(duì)象類型, 異常對(duì)象和調(diào)用棧. 如果 with 塊正常退出, 那么這些參數(shù)將都是 None . 返回 True 表示發(fā)生的異常已被處理, 不再繼續(xù)向外拋出.
簡(jiǎn)單的介紹到此為止, 詳細(xì)的情況可以參考 PEP 343 (這數(shù)字真不錯(cuò), 7 3 ).
下面介紹下 with 語句的實(shí)例用法 & 高級(jí)用法:
Python高端、大氣、上檔次的with語句
在說with語句之前,先看看一段簡(jiǎn)單的代碼吧
lock = threading.Lock() ... lock.acquire() elem = heapq.heappop(heap) lock.release()
很簡(jiǎn)單直觀,多個(gè)線程共用一個(gè)優(yōu)先級(jí)隊(duì)列的時(shí)候,首先先用互斥鎖lock.acquire()把優(yōu)先級(jí)隊(duì)列鎖上,然后取元素,再然后lock.release()釋放這個(gè)鎖。
雖然看似非常符合邏輯的一個(gè)過程,但是里面隱藏著一個(gè)巨大的bug:當(dāng)heap里面沒有元素的時(shí)候,會(huì)拋出一個(gè)IndexError異常,再然后堆?;貪L,再然后lock.release()根本不會(huì)執(zhí)行,這個(gè)鎖就永遠(yuǎn)得不到釋放,因此就發(fā)生了喜聞樂見的死鎖問題。這個(gè)也是很多大神們討厭異常的原因。經(jīng)典Java風(fēng)格的解決方案就是
lock = threading.Lock() ... lock.acquire() try: elem = heapq.heappop(heap) finally: lock.release()
這個(gè)雖然可以,但是怎么看怎么dirty,和Python優(yōu)雅、簡(jiǎn)單的風(fēng)格出入很大。其實(shí),自從Python2.5開始引入了with語句,一切就變得非常簡(jiǎn)單:
lock = threading.Lock() ... with lock: elem = heapq.heappop(heap)
在此無論以何種方式離開with語句的代碼塊,鎖都會(huì)被釋放。
with語句的設(shè)計(jì)目的就是為了使得之前需要通過try...finally解決的清理資源問題變得簡(jiǎn)單、清晰,它的的用法是
with expression [as variable]: with-block
其中expression返回一個(gè)叫做「context manager」的對(duì)象,然后這個(gè)對(duì)象被賦給variable(如果有的話)。「context manager」對(duì)象有兩個(gè)方法,分別是__enter__()和__exit__(),很明顯一個(gè)在進(jìn)入with-block時(shí)調(diào)用,一個(gè)離開with-block的時(shí)候調(diào)用。
這樣的對(duì)象不需要自己去實(shí)現(xiàn),在Python標(biāo)準(zhǔn)庫里面很多API都是已經(jīng)實(shí)現(xiàn)了這兩個(gè)方法,最常見的一個(gè)例子就是讀寫文件的open語句。
with open('1.txt', encoding = 'utf-8') as fp:
lines = fp.readlines()
無論是正常離開還是因?yàn)楫惓T螂x開with語句塊,打開的文件資源總是會(huì)釋放。
接下去討論一下with語句配合contextlib庫的一些比較實(shí)用的方法,比如需要同時(shí)打開兩個(gè)文件,一個(gè)讀一個(gè)寫,這個(gè)時(shí)候就可以這樣寫:
from contextlib import nested
...
with nested(open('in.txt'), open('out.txt', 'w')) as (fp_in, fp_out):
...
這樣就可以省掉兩個(gè)with的語句的嵌套了,另外如果遇到一些還沒有支持「context manager」的API呢?比如urllib.request.urlopen(),這個(gè)返回的對(duì)象因?yàn)椴皇恰竎ontext manager」,結(jié)束的時(shí)候還需要自己去調(diào)用close方法。
類似這種API,contextlib提供了一個(gè)叫做closing方法,它會(huì)在離開with語句的時(shí)候,自動(dòng)調(diào)用對(duì)象的close方法,因此urlopen也可以這樣寫:
from contextlib import closing
...
with closing(urllib.request.urlopen('http://www.yahoo.com')) as f:
for line in f:
sys.stdout.write(line)
相關(guān)文章
Python函數(shù)的參數(shù)類型和使用技巧詳解
這篇文章主要介紹了Python函數(shù)的參數(shù)類型和使用技巧詳解,函數(shù)指通過專門的代碼組織,用來實(shí)現(xiàn)特定功能的代碼段,具有相對(duì)的獨(dú)立性,可以被其他代碼重復(fù)調(diào)用,需要的朋友可以參考下2023-08-08
Python實(shí)現(xiàn)的json文件讀取及中文亂碼顯示問題解決方法
這篇文章主要介紹了Python實(shí)現(xiàn)的json文件讀取及中文亂碼顯示問題解決方法,涉及Python針對(duì)json文件的讀取載入、編碼轉(zhuǎn)換等相關(guān)操作技巧,需要的朋友可以參考下2018-08-08
python定位xpath 節(jié)點(diǎn)位置的方法
今天小編就為大家分享一篇python定位xpath 節(jié)點(diǎn)位置的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2019-08-08
用Python的Django框架來制作一個(gè)RSS閱讀器
這篇文章主要介紹了用Python的Django框架來制作一個(gè)RSS閱讀器,通過url feeds來制作訂閱類應(yīng)用同樣是Django之所長(zhǎng),需要的朋友可以參考下2015-07-07

