python上下文管理器及with語句

2021-09-08 10:22:00 字數 4930 閱讀 8846

with語句支援在乙個叫上下文管理器的物件的控制下執行一系列語句,語法大概如下:

with context as var:

statements

其中的context必須是個上下文管理器,它實現了兩個方法__enter__,__exit__。

在正常的管理各種系統資源(檔案、鎖定和連線),在涉及到異常時通常是個棘手的問題。異常很可能導致控制流跳過負責釋放關鍵資源的語句。

看一段簡單的檔案寫入**:

filename = 'my_file.txt' 

f = open(filename,'w')

f.write('hello ')

f.write('world')

f.close()

如果發生了意外的情況,例如寫入'world'時磁碟空間不足,就會丟擲異常,那麼close()語句將不會被執行。

一般的解決方案是使用try-finally語句:

try:

filename = 'my_file.txt'

f = open(filename,'w')

f.write('hello ')

f.write('world')

finally:

f.close()

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

with open('my_file','w') as f:

f.write('hello ')

f.write('world')

這樣不僅能處理出現異常的情況,而且還避免了在open乙個檔案後忘記了寫close()方法的情況。

執行緒中的鎖其實也實現了上下文管理協議:

import threading

with threading.lock():

# 關鍵部分

statements

# 關鍵部分結束

再也不怕忘記將鎖釋放了。

上下文管理器要實現__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__的內容

__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:

print alist

生成:

[1, 2]

有異常發生時:

alist=

with listtrans(alist) as working:

raise runtimeerror('we are hosed')

print alist

生成:

runtimeerror: we are hosed

alist無變化。

可以捕捉異常:

alist=

try:

with listtrans(alist) as working:

raise runtimeerror('we are hosed')

except runtimeerror as e:

print e

print alist

生成:

we are hosed

當然,也可以簡單的將__exit__的返回值設為true來忽略異常。

@contextmanager

contextlib模組的contextmanager裝飾器可以更方便的實現上下文管理器。

而任何yield之後的操作都可以放在exit函式中。

from contextlib import contextmanager

@contextmanager

def listtrans(alist):

thecopy=list(alist)

yield thecopy

alist[:]=thecopy

alist=

with listtrans(alist) as working:

print alist

yield返回的值相當於__enter__的返回值。

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

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:

raise runtimeerror

nested與closing

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

nested:用來更方便的減少巢狀寫法:

當要巢狀的寫上下文管理器時:

with open('toreadfile', 'r') as reader:  

with open('towritefile', 'w') as writer:

writer.writer(reader.read())

可以用nested簡化寫法:

with contextlib.nested(open('filetoread.txt', 'r'),  

open('filetowrite.txt', 'w')) as (reader, writer):

writer.write(reader.read())

python2.7後nested就過時了:

with open('filetoread.txt', 'r') as reader,open('filetowrite.txt', 'w') as writer:  

writer.write(reader.read())

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

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()

python 上下文管理器

上下文管理器允許你在有需要的時候,精確地分配和釋放資源。使用上下文管理器最廣泛的案例就是with語句了。想象下你有兩個需要結對執行的相關操作,然後還要在它們中間放置一段 上下文管理器就是專門讓你做這種事情的。舉個例子 with open some file w as opened file open...

python上下文管理器

上下文管理器是乙個包裝任意 塊的物件。上下文管理器保證進入上下文管理器時,每次 執行的一致性 當退出上下文管理器時,相關資源會被正確 這裡被正確 指的是在 exit 方法自定義 比如關閉資料庫游標 值得注意的是,上下文管理器一定能夠保證退出步驟的執行。如果進入上下文管理器,根據定義,一定會有退出步驟...

Python 上下文管理器

python中的上下文管理器是乙個包裝任意 塊的物件。它在處理資源的開啟關閉 異常的處理等方面有很好的實現方法。1.上下文管理器的語法 假設我們需要讀取乙個檔案中的資料,如下 try test file open test.txt r contents test file.read finally ...