转载:简单介绍Python中的try和finally和with方法
用 Python 做一件很平常的事情: 打開文件, 逐行讀入, 最后關掉文件; 進一步的需求是, 這也許是程序中一個可選的功能, 如果有任何問題, 比如文件無法打開, 或是讀取出錯, 那么在函數內需要捕獲所有異常, 輸出一行警告并退出. 代碼可能一開始看起來是這樣的
?
| 1 2 3 4 5 6 7 8 | def read_file(): ??try: ????f = open('yui', 'r') ????print ''.join(f.readlines()) ??except: ????print 'error occurs while reading file' ??finally: ????f.close() |
??? 不過這顯然無法運作, 因為? f? 是在? try? 塊中定義的, 而在? finally? 中無法引用.
??? 如果將? f? 提取到? try? 塊外部, 如
?
| 1 2 3 4 5 6 7 8 | def read_file(): ???f = open('azusa', 'r') ??try: ????print ''.join(f.readlines()) ??except: ????print 'error occurs while reading file' ??finally: ????f.close() |
那么, 問題在于當打開文件失敗, 拋出異常將不會被捕獲.
??? 挫一點的方法自然是, 再套一層? try? 吧
?
| 1 2 3 4 5 6 7 8 9 10 11 | 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' |
??? 當然這不僅僅是多一層縮進挫了, 連警告輸出都白白多一次呢.
??? 正規一點的方式是, 使用 Python 引入的? with? 結構來解決, 如
?
| 1 2 3 4 5 6 | def readFile(): ??try: ?????with open('mio', 'r') as f: ??????print ''.join(f.readlines()) ??except: ????print 'error occurs while reading file' |
??? 當文件打開失敗時, 異常自然會被? except? 到; 否則, 在? with? 塊結束之后, 打開的文件將自動關閉.
??? 除了打開文件, 還有其它這樣可以用于? with? 的東西么? 或者說, 怎么自定義一個什么東西, 讓它能用于? with 呢?
??? 直接回答后一個問題吧, 秘密在于 Python 虛擬機在? with? 塊退出時會去尋找對象的? __exit__? 方法并調用它, 把釋放資源的動作放在這個? __exit__? 函數中就可以了; 另外, 對象還需要一個? __enter__? 函數, 當進入? with 塊時, 這個函數被調用, 而它的返回值將作為? as? 后引用的值. 一個簡單的例子是
?
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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!') |
??? 執行這一段代碼, 輸出將會是
?
| 1 2 3 4 5 6 7 8 | init enter <type 'exceptions.ValueError'> kon! ?File "test.py", line 17, in <module> ??raise ValueError('kon!') ?? exit |
???? __exit__? 函數接受三個參數, 分別是異常對象類型, 異常對象和調用棧. 如果? with? 塊正常退出, 那么這些參數將都是? None . 返回? True? 表示發生的異常已被處理, 不再繼續向外拋出.
??? 簡單的介紹到此為止, 詳細的情況可以參考? PEP 343? (這數字真不錯, 7 3 ).
下面介紹下 with 語句的實例用法 & 高級用法:
Python高端、大氣、上檔次的with語句
在說with語句之前,先看看一段簡單的代碼吧
?
| 1 2 3 4 5 | lock = threading.Lock() ... lock.acquire() elem = heapq.heappop(heap) lock.release() |
很簡單直觀,多個線程共用一個優先級隊列的時候,首先先用互斥鎖lock.acquire()把優先級隊列鎖上,然后取元素,再然后lock.release()釋放這個鎖。
雖然看似非常符合邏輯的一個過程,但是里面隱藏著一個巨大的bug:當heap里面沒有元素的時候,會拋出一個IndexError異常,再然后堆棧回滾,再然后lock.release()根本不會執行,這個鎖就永遠得不到釋放,因此就發生了喜聞樂見的死鎖問題。這個也是很多大神們討厭異常的原因。經典Java風格的解決方案就是
?
| 1 2 3 4 5 6 7 | lock = threading.Lock() ... lock.acquire() try: ??elem = heapq.heappop(heap) finally: ??lock.release() |
這個雖然可以,但是怎么看怎么dirty,和Python優雅、簡單的風格出入很大。其實,自從Python2.5開始引入了with語句,一切就變得非常簡單:
?
| 1 2 3 4 | lock = threading.Lock() ... with lock: ??elem = heapq.heappop(heap) |
在此無論以何種方式離開with語句的代碼塊,鎖都會被釋放。
with語句的設計目的就是為了使得之前需要通過try...finally解決的清理資源問題變得簡單、清晰,它的的用法是
?
| 1 2 | with expression [as variable]: ??with-block |
其中expression返回一個叫做「context manager」的對象,然后這個對象被賦給variable(如果有的話)。「context manager」對象有兩個方法,分別是__enter__()和__exit__(),很明顯一個在進入with-block時調用,一個離開with-block的時候調用。
這樣的對象不需要自己去實現,在Python標準庫里面很多API都是已經實現了這兩個方法,最常見的一個例子就是讀寫文件的open語句。
?
| 1 2 | with open('1.txt', encoding = 'utf-8') as fp: ??lines = fp.readlines() |
無論是正常離開還是因為異常原因離開with語句塊,打開的文件資源總是會釋放。
接下去討論一下with語句配合contextlib庫的一些比較實用的方法,比如需要同時打開兩個文件,一個讀一個寫,這個時候就可以這樣寫:
?
| 1 2 3 4 | from contextlib import nested ... with nested(open('in.txt'), open('out.txt', 'w')) as (fp_in, fp_out): ??... |
這樣就可以省掉兩個with的語句的嵌套了,另外如果遇到一些還沒有支持「context manager」的API呢?比如urllib.request.urlopen(),這個返回的對象因為不是「context manager」,結束的時候還需要自己去調用close方法。
類似這種API,contextlib提供了一個叫做closing方法,它會在離開with語句的時候,自動調用對象的close方法,因此urlopen也可以這樣寫:
?
| 1 2 3 4 5 | from contextlib import closing ... with closing(urllib.request.urlopen('http://www.yahoo.com')) as f: ??for line in f: ????sys.stdout.write(line) |
?用 Python 做一件很平常的事情: 打開文件, 逐行讀入, 最后關掉文件; 進一步的需求是, 這也許是程序中一個可選的功能, 如果有任何問題, 比如文件無法打開, 或是讀取出錯, 那么在函數內需要捕獲所有異常, 輸出一行警告并退出. 代碼可能一開始看起來是這樣的
?
| 1 2 3 4 5 6 7 8 | def read_file(): ??try: ????f = open('yui', 'r') ????print ''.join(f.readlines()) ??except: ????print 'error occurs while reading file' ??finally: ????f.close() |
??? 不過這顯然無法運作, 因為? f? 是在? try? 塊中定義的, 而在? finally? 中無法引用.
??? 如果將? f? 提取到? try? 塊外部, 如
?
| 1 2 3 4 5 6 7 8 | def read_file(): ???f = open('azusa', 'r') ??try: ????print ''.join(f.readlines()) ??except: ????print 'error occurs while reading file' ??finally: ????f.close() |
那么, 問題在于當打開文件失敗, 拋出異常將不會被捕獲.
??? 挫一點的方法自然是, 再套一層? try? 吧
?
| 1 2 3 4 5 6 7 8 9 10 11 | 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' |
??? 當然這不僅僅是多一層縮進挫了, 連警告輸出都白白多一次呢.
??? 正規一點的方式是, 使用 Python 引入的? with? 結構來解決, 如
?
| 1 2 3 4 5 6 | def readFile(): ??try: ?????with open('mio', 'r') as f: ??????print ''.join(f.readlines()) ??except: ????print 'error occurs while reading file' |
??? 當文件打開失敗時, 異常自然會被? except? 到; 否則, 在? with? 塊結束之后, 打開的文件將自動關閉.
??? 除了打開文件, 還有其它這樣可以用于? with? 的東西么? 或者說, 怎么自定義一個什么東西, 讓它能用于? with 呢?
??? 直接回答后一個問題吧, 秘密在于 Python 虛擬機在? with? 塊退出時會去尋找對象的? __exit__? 方法并調用它, 把釋放資源的動作放在這個? __exit__? 函數中就可以了; 另外, 對象還需要一個? __enter__? 函數, 當進入? with 塊時, 這個函數被調用, 而它的返回值將作為? as? 后引用的值. 一個簡單的例子是
?
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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!') |
??? 執行這一段代碼, 輸出將會是
?
| 1 2 3 4 5 6 7 8 | init enter <type 'exceptions.ValueError'> kon! ?File "test.py", line 17, in <module> ??raise ValueError('kon!') ?? exit |
???? __exit__? 函數接受三個參數, 分別是異常對象類型, 異常對象和調用棧. 如果? with? 塊正常退出, 那么這些參數將都是? None . 返回? True? 表示發生的異常已被處理, 不再繼續向外拋出.
??? 簡單的介紹到此為止, 詳細的情況可以參考? PEP 343? (這數字真不錯, 7 3 ).
下面介紹下 with 語句的實例用法 & 高級用法:
Python高端、大氣、上檔次的with語句
在說with語句之前,先看看一段簡單的代碼吧
?
| 1 2 3 4 5 | lock = threading.Lock() ... lock.acquire() elem = heapq.heappop(heap) lock.release() |
很簡單直觀,多個線程共用一個優先級隊列的時候,首先先用互斥鎖lock.acquire()把優先級隊列鎖上,然后取元素,再然后lock.release()釋放這個鎖。
雖然看似非常符合邏輯的一個過程,但是里面隱藏著一個巨大的bug:當heap里面沒有元素的時候,會拋出一個IndexError異常,再然后堆棧回滾,再然后lock.release()根本不會執行,這個鎖就永遠得不到釋放,因此就發生了喜聞樂見的死鎖問題。這個也是很多大神們討厭異常的原因。經典Java風格的解決方案就是
?
| 1 2 3 4 5 6 7 | lock = threading.Lock() ... lock.acquire() try: ??elem = heapq.heappop(heap) finally: ??lock.release() |
這個雖然可以,但是怎么看怎么dirty,和Python優雅、簡單的風格出入很大。其實,自從Python2.5開始引入了with語句,一切就變得非常簡單:
?
| 1 2 3 4 | lock = threading.Lock() ... with lock: ??elem = heapq.heappop(heap) |
在此無論以何種方式離開with語句的代碼塊,鎖都會被釋放。
with語句的設計目的就是為了使得之前需要通過try...finally解決的清理資源問題變得簡單、清晰,它的的用法是
?
| 1 2 | with expression [as variable]: ??with-block |
其中expression返回一個叫做「context manager」的對象,然后這個對象被賦給variable(如果有的話)。「context manager」對象有兩個方法,分別是__enter__()和__exit__(),很明顯一個在進入with-block時調用,一個離開with-block的時候調用。
這樣的對象不需要自己去實現,在Python標準庫里面很多API都是已經實現了這兩個方法,最常見的一個例子就是讀寫文件的open語句。
?
| 1 2 | with open('1.txt', encoding = 'utf-8') as fp: ??lines = fp.readlines() |
無論是正常離開還是因為異常原因離開with語句塊,打開的文件資源總是會釋放。
接下去討論一下with語句配合contextlib庫的一些比較實用的方法,比如需要同時打開兩個文件,一個讀一個寫,這個時候就可以這樣寫:
?
| 1 2 3 4 | from contextlib import nested ... with nested(open('in.txt'), open('out.txt', 'w')) as (fp_in, fp_out): ??... |
這樣就可以省掉兩個with的語句的嵌套了,另外如果遇到一些還沒有支持「context manager」的API呢?比如urllib.request.urlopen(),這個返回的對象因為不是「context manager」,結束的時候還需要自己去調用close方法。
類似這種API,contextlib提供了一個叫做closing方法,它會在離開with語句的時候,自動調用對象的close方法,因此urlopen也可以這樣寫:
?
| 1 2 3 4 5 | from contextlib import closing ... with closing(urllib.request.urlopen('http://www.yahoo.com')) as f: ??for line in f: ????sys.stdout.write(line) |
轉載于:https://www.cnblogs.com/alan-babyblog/p/5153343.html
總結
以上是生活随笔為你收集整理的转载:简单介绍Python中的try和finally和with方法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 电脑影子系统如何卸载
- 下一篇: python基础day2作业:购物车