Python之with語句


Python之with語句

 

在Python中,我們在打開文件的時候,為了代碼的健壯性,通常要考慮一些異常情況,比如:

try:
    ccfile = open('/path/data')
    content = ccfile.readlines()
    ccfile.close()

except IOError:
    log.write('no data read\n')

我們將真正干活的代碼扔到try語句塊中,如果文件操作出現異常,則寫一條錯誤日志;

考慮一種情況,如果文件打開成功,但readlines()調用失敗,異常處理會立即跳轉到except處執行,這樣文件關閉就沒有機會被執行到了。

一種解決辦法就是將close()語句放到finally子句中去,finally的特點是不管有無異常,都會被執行到。

try:
    try:
        ccfile = open('/path/data')
        content = ccfile.readlines()

    except IOError:
        log.write('no data read\n')

finally
    ccfile.close()

 

finally的另一種可選的風格:

try:
    try:
        ccfile = open('/path/data')
        content = ccfile.readlines()

    finally IOError:
        ccfile.close()

except IOError:
    log.write('no data read\n')

 

如上所述的標准化的 try-except和try-finally 的用法是保證資源的分配和回收,比如文件(數據、日志、數據庫等等)、線程資源、數據庫連接等,但它們書寫起來卻不夠優雅。with語句的目的在於從流程圖中把try、except、 finally關鍵字和資源分配、釋放相關代碼統統去掉,

with處理文件操作的一個實例:

with open('/etc/passwd') as f:
    for line in f:
        print(line)
 

這段代碼的作用:打開一個文件,如果一切正常,把文件對象賦值給f,然后用迭代器遍歷文件中每一行,當完成時,關閉文件;而無論在這段代碼的任何地方,如果發生異常,此時文件仍會被關閉。

with看起來如此簡單,但是其背后還有一些工作要做,因為你不能對Python的任意符號使用with語句,它僅能工作於支持上下文管理協議(context management protocol)的對象。也就是說,只有內建了“上下文管理”的對象可以和with一起工作,目前支持該協議的對象有:

  • file
  • decimal.Context
  • thread.LockType
  • threading.Lock
  • threading.RLock
  • threading.Condition
  • threading.Semaphore
  • threading.BoundedSemaphore

 

現在來看with的語法:

with context_expr as var:
    with_suite

 

當with語句執行時,便執行上下文表達式(context_expr)來獲得一個上下文管理器,上下文管理器的職責是提供一個上下文對象,用於在with語句塊中處理細節:

一旦獲得了上下文對象,就會調用它的__enter__()方法,將完成with語句塊執行前的所有准備工作,如果with語句后面跟了as語句,則用__enter__()方法的返回值來賦值;

當with語句塊結束時,無論是正常結束,還是由於異常,都會調用上下文對象的__exit__()方法,__exit__()方法有3個參數,如果with語句正常結束,三個參數全部都是 None;如果發生異常,三個參數的值分別等於調用sys.exc_info()函數返回的三個值:類型(異常類)、值(異常實例)和跟蹤記錄(traceback),相應的跟蹤記錄對象。

因為上下文管理器主要作用於共享資源,__enter__()和__exit__()方法基本是干的需要分配和釋放資源的低層次工作,比如:數據庫連接、鎖分配、信號量加/減、狀態管理、文件打開/關閉、異常處理等。

 

現在,我們可以在自定義類里面創建__enter__()和__exit__()方法,這樣就可以配合with語句創建類實例了:

class A:  
    def __enter__(self):  
        print '__enter__() called'

    def __exit__(self, e_t, e_v, t_b):  
        print '__exit__() called'
        
with A() as a:
    print('got instance')

可以看到輸出為:

__enter__() called
got instance
__exit__() called

 

另外python庫中還有一個模塊contextlib,使你不用構造含有__enter__, __exit__的類就可以使用with:

from __future__ import with_statement
from contextlib import contextmanager  

@contextmanager  
def context():
    print 'entering the zone'
    try:
        yield
    except Exception, e:  
        print 'with an error %s'%e
        raise e
    else:
      print 'with no error'  

with context():  
    print '----in context call------' 

 

 

參考文檔:

http://www.ibm.com/developerworks/cn/opensource/os-cn-pythonwith/

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM