python上下文管理器

1、上下文管理協議html

上下文管理協議是指:爲了讓一個對象兼容with語句,必須在這個對象的類中聲明__ente__()和__exit__()方法。python

 

2、上下文管理器安全

上下文管理器就是是實現了_ente__()和__exit__()方法的對象app

 

3、爲何要有上下文管理器?(參考自:https://blog.csdn.net/xiecj_2006/article/details/42748533python2.7

在正常的管理各類系統資源(文件、鎖定和鏈接),在涉及到異常時一般是個棘手的問題。異常極可能致使控制流跳過負責釋放關鍵資源的語句函數

filename = 'my_file.txt'
f = open(filename,'w') 
f.write('Hello ') 
f.write('World') 
f.close()

若是發生了意外的狀況,例如寫入'World'時磁盤空間不足,就會拋出異常,那麼close()語句將不會被執行。post

通常的解決方案是使用try-finally語句:spa

try:
    filename = 'my_file.txt'
    f = open(filename,'w') 
    f.write('Hello ') 
    f.write('World')
finally:
    f.close() 

但隨着語句的增多,try-finally顯然不夠簡潔,用with-as語句能夠很簡潔的實現以上功能:.net

with open('my_file','w') as f:
    f.write('Hello ')
    f.write('World') 

這樣不只能處理出現異常的狀況,並且還避免了在open一個文件後忘記了寫close()方法的狀況。線程

線程中的鎖其實也實現了上下文管理協議:

import threading
with threading.Lock():
    # 關鍵部分 
      statements
    # 關鍵部分結束

4、.上下文管理器的實現

上下文管理器要實現__enter__和__exit__的特殊方法。

__enter__(self): 進入上下文管理器時調用此方法,其返回值將被放入with-as語句中as說明符指定的變量中。

__exit__(self,type,value,tb):離開上下文管理器調用此方法。若是有異常出現,type、value、tb分別爲異常的類型、值和追蹤信息。若是沒有異常,

3個參數均設爲None。此方法返回值爲True或者False,分別指示被引起的異常獲得了仍是沒有獲得處理。若是返回False,引起的異常會被傳遞出上下文。

 

文件上下文管理協議大概是以下實現的:

class OpenFile(object):
    def __init__(self,filename,mode):
        self.filename=filename
        self.mode=mode
    def __enter__(self):
        self.f=open(self.filename,self.mode)
        return self.f  #做爲as說明符指定的變量的值
    def __exit__(self,type,value,tb):
        self.f.close()
        return False   #異常會被傳遞出上下文
with OpenFile('my_file.txt','w') as f:
    f.write('Hello ')
    f.write('World')

至關於

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

5、.異常的處理 

__exit__函數就可以拿到關於異常的全部信息(異常類型,異常值以及異常追蹤信息),這些信息將幫助異常處理操做。

class ListTrans(object):
    def __init__(self,alist):
        self.alist=alist
    def __enter__(self):
        self.thecopy=list(self.alist)
        return self.thecopy
    def __exit__(self,exc_type,value,tb):
        if exc_type is None:
            self.alist[:]=self.thecopy
        return False 

沒有異常發生時:

alist=[]
with ListTrans(alist) as working:
    working.append(1)
    working.append(2)
print alist 

生成

[1, 2]

有異常發生時:

 

 

1
2
3
4
5
6
alist = []
with ListTrans(alist) as working:
     working.append( 1 )
     working.append( 2 )
     raise  RuntimeError( 'we are hosed' )
print  alist

生成:

1
RuntimeError: we are hosed

alist無變化。

能夠捕捉異常:

1
2
3
4
5
6
7
8
9
alist = []
try :
     with ListTrans(alist) as working:
         working.append( 1 )
         working.append( 2 )
         raise  RuntimeError( 'we are hosed' )
except  RuntimeError as e:
     print  e
print  alist

生成:

1
2
we are hosed
[]

固然,也能夠簡單的將__exit__的返回值設爲True來忽略異常。

4.contextmanager裝飾器

@contextmanager

contextlib模塊的contextmanager裝飾器能夠更方便的實現上下文管理器。

任何可以被yield關鍵詞分割成兩部分的函數,都可以經過裝飾器裝飾的上下文管理器來實現。任何在yield以前的內容均可以看作在代碼塊執行前的操做,

而任何yield以後的操做均可以放在exit函數中。

1
2
3
4
5
6
7
8
9
10
11
from  contextlib  import  contextmanager
@contextmanager
def  listTrans(alist):
     thecopy = list (alist)
     yield  thecopy
     alist[:] = thecopy
alist = []
with listTrans(alist) as working:
     working.append( 1 )
     working.append( 2 )
print  alist

yield返回的值至關於__enter__的返回值。

要注意的是,這不是異常安全的寫法,也就是說,當出現異常時,yield後的語句是不會執行的,想要異常安全,可用try捕捉異常:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from  contextlib  import  contextmanager
@contextmanager
def  listTrans(alist):
     thecopy = list (alist)
     try :
         yield  thecopy
     except  RuntimeError:
         pass
     alist[:] = thecopy
alist = []
with listTrans(alist) as working:
     working.append( 1 )
     working.append( 2 )
     raise  RuntimeError

nested與closing

contextlib模塊還有兩個好玩的方法:nested,closing。

nested:用來更方便的減小嵌套寫法:

當要嵌套的寫上下文管理器時:

1
2
3
with  open ( 'toReadFile' 'r' ) as reader: 
     with  open ( 'toWriteFile' 'w' ) as writer: 
         writer.writer(reader.read())

能夠用nested簡化寫法:

1
2
3
with contextlib.nested( open ( 'fileToRead.txt' 'r' ), 
                        open ( 'fileToWrite.txt' 'w' )) as (reader, writer): 
     writer.write(reader.read())

python2.7後nested就過期了:

1
2
with  open ( 'fileToRead.txt' 'r' ) as reader, open ( 'fileToWrite.txt' 'w' ) as writer: 
         writer.write(reader.read())

closing(object):建立上下文管理器,在執行過程離開with語句時自動執行object.close():

1
2
3
4
5
6
7
class  Door( object ) :
     def  open ( self ) :
         print  'Door is opened'
     def  close( self ) :
         print  'Door is closed'
with contextlib.closing(Door()) as door :
     door. open ()
相關文章
相關標籤/搜索