理解Python中的with…as…文法

來源:互聯網
上載者:User

標籤:

先說明一個常見問題,檔案開啟:

try:    f = open(‘xxx‘)    do somethingexcept:    do somethingfinally:    f.close()

其實我個人不止一次在網上看到有這麼寫的了,這個是錯的。
首先正確的如下:

try:    f = open(‘xxx‘)except:    print ‘fail to open‘    exit(-1)try:    do somethingexcept:    do somethingfinally:    f.close()

很麻煩不是麼,但正確的方法就是這麼寫。

我們為什麼要寫finally,是因為防止程式拋出異常最後不能關閉檔案,但是需要關閉檔案有一個前提就是檔案已經開啟了。

在第一段錯誤碼中,如果異常發生在f=open(‘xxx’)的時候,比如檔案不存在,立馬就可以知道執行f.close()是沒有意義的。改正後的解決方案就是第二段代碼。

好了言歸正轉,開始討論with文法。

首先我們從下面這個問題談起,try-finally的文法結構:

set things uptry:    do somethingfinally:    tear things down

這東西是個常見結構,比如檔案開啟,set things up就表示f=open(‘xxx‘)tear things down就表示f.close()。在比如像多線程鎖,資源請求,最終都有一個釋放的需求。Try…finally結構保證了tear things down這一段永遠都會執行,即使上面do something得工作沒有完全執行。

也就是說with是一個控制流程語句,跟if/for/while/try之類的是一類的,with可以用來簡化try finally代碼,看起來可以比try finally更清晰。
這裡新引入了一個”上下文管理協議"context management protocol",實現方法是為一個類定義__enter____exit__兩個函數。

with expresion as variable

的執行過程是,首先執行__enter__函數,它的傳回值會賦給as後面的variable,想讓它返回什麼就返回什麼,只要你知道怎麼處理就可以了,如果不寫as variable,傳回值會被忽略。

然後,開始執行with-block中的語句,不論成功失敗(比如發生異常、錯誤,設定sys.exit()),在with-block執行完成後,會執行__exit__函數。
這樣的過程其實等價於:

try:        執行 __enter__的內容        執行 with_block.  finally:        執行 __exit__內容  

最終的python-dev團隊的解決方案。(python 2.5以後增加了with運算式的文法)

class controlled_execution:    def __enter__(self):        set things up        return thing    def __exit__(self, type, value, traceback):        tear things downwith controlled_execution() as thing:        do something

只不過,現在把一部分代碼封裝成了__enter__函數,清理代碼封裝成__exit__函數。

我們可以自己實現一個例子:

import sys  class test:  def __enter__(self):         print("enter")         return 1  def __exit__(self,*args):         print("exit")         return True  with test() as t:      print("t is not the result of test(), it is __enter__ returned")      print("t is 1, yes, it is {0}".format(t))      raise NameError("Hi there")      sys.exit()      print("Never here")  

在這裡,python使用了with-as的文法。當python執行這一句時,會調用__enter__函數,然後把該函數return的值傳給as後指定的變數。之後,python會執行下面do something的語句塊。最後不論在該語句塊出現了什麼異常,都會在離開時執行__exit__
另外,__exit__除了用於tear things down,還可以進行異常的監控和處理,注意後幾個參數。要跳過一個異常,只需要返回該函數True即可。下面的範例代碼跳過了所有的TypeError,而讓其他異常正常拋出。

def __exit__(self, type, value, traceback):    return isinstance(value, TypeError)

python2.5及以後,file對象已經寫好了__enter____exit__函數,我們可以這樣測試:

>>> f = open("x.txt")>>> f<open file ‘x.txt‘, mode ‘r‘ at 0x00AE82F0>>>> f.__enter__()<open file ‘x.txt‘, mode ‘r‘ at 0x00AE82F0>>>> f.read(1)‘X‘>>> f.__exit__(None, None, None)>>> f.read(1)Traceback (most recent call last):    File "<stdin>", line 1, in <module>ValueError: I/O operation on closed file

之後,我們如果要開啟檔案並保證最後關閉他,只需要這麼做:

with open("x.txt") as f:    data = f.read()    do something with data

如果有多個項,我們可以這麼寫:

with open("x.txt") as f1, open(‘xxx.txt‘) as f2:    do something with f1,f2

上文說了__exit__函數可以進行部分異常的處理,如果我們不在這個函數中處理異常,他會正常拋出,這時候我們可以這樣寫(python 2.7及以上版本,之前的版本參考使用contextlib.nested這個庫函數):

try:    with open( "a.txt" ) as f :        do somethingexcept xxxError:    do something about exception

總之,with-as運算式極大的簡化了每次寫finally的工作,這對保持代碼的優雅性是有極大協助的。

理解Python中的with…as…文法

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.