python语言使用什么语句实现上下文管理协议_Python 上下文管理器
上下文管理器
在使用Python編程中,可以會(huì)經(jīng)常碰到這種情況:有一個(gè)特殊的語句塊,在執(zhí)行這個(gè)語句塊之前需要先執(zhí)行一些準(zhǔn)備動(dòng)作;當(dāng)語句塊執(zhí)行完成后,需要繼續(xù)執(zhí)行一些收尾動(dòng)作。
例如:當(dāng)需要操作文件或數(shù)據(jù)庫(kù)的時(shí)候,首先需要獲取文件句柄或者數(shù)據(jù)庫(kù)連接對(duì)象,當(dāng)執(zhí)行完相應(yīng)的操作后,需要執(zhí)行釋放文件句柄或者關(guān)閉數(shù)據(jù)庫(kù)連接的動(dòng)作。
又如,當(dāng)多線程程序需要訪問臨界資源的時(shí)候,線程首先需要獲取互斥鎖,當(dāng)執(zhí)行完成并準(zhǔn)備退出臨界區(qū)的時(shí)候,需要釋放互斥鎖。
對(duì)于這些情況,Python中提供了上下文管理器(Context Manager)的概念,可以通過上下文管理器來定義/控制代碼塊執(zhí)行前的準(zhǔn)備動(dòng)作,以及執(zhí)行后的收尾動(dòng)作。
上下文管理協(xié)議
那么在Python中怎么實(shí)現(xiàn)一個(gè)上下文管理器呢?這里,又要提到兩個(gè)”魔術(shù)方法”,__enter__和__exit__,下面就是關(guān)于這兩個(gè)方法的具體介紹。__enter__(self) Defines what the context manager should do at the beginning of the block created by the with statement. Note that the return value of __enter__ is bound to the target of the with statement, or the name after the as.
__exit__(self, exception_type, exception_value, traceback) Defines what the context manager should do after its block has been executed (or terminates). It can be used to handle exceptions, perform cleanup, or do something always done immediately after the action in the block. If the block executes successfully, exception_type, exception_value, and traceback will be None. Otherwise, you can choose to handle the exception or let the user handle it; if you want to handle it, make sure __exit__ returns True after all is said and done. If you don’t want the exception to be handled by the context manager, just let it happen.
也就是說,當(dāng)我們需要?jiǎng)?chuàng)建一個(gè)上下文管理器類型的時(shí)候,就需要實(shí)現(xiàn)__enter__和__exit__方法,這對(duì)方法就稱為上下文管理協(xié)議(Context Manager Protocol),定義了一種運(yùn)行時(shí)上下文環(huán)境。
with語句
在Python中,可以通過with語句來方便的使用上下文管理器,with語句可以在代碼塊運(yùn)行前進(jìn)入一個(gè)運(yùn)行時(shí)上下文(執(zhí)行__enter__方法),并在代碼塊結(jié)束后退出該上下文(執(zhí)行__exit__方法)。
with語句的語法如下:
Pythonwith?context_expr?[as?var]:
with_suite
context_expr是支持上下文管理協(xié)議的對(duì)象,也就是上下文管理器對(duì)象,負(fù)責(zé)維護(hù)上下文環(huán)境
as var是一個(gè)可選部分,通過變量方式保存上下文管理器對(duì)象
with_suite就是需要放在上下文環(huán)境中執(zhí)行的語句塊
在Python的內(nèi)置類型中,很多類型都是支持上下文管理協(xié)議的,例如file,thread.LockType,threading.Lock等等。這里我們就以file類型為例,看看with語句的使用。
with語句簡(jiǎn)化文件操作
當(dāng)需要寫一個(gè)文件的時(shí)候,一般都會(huì)通過下面的方式。代碼中使用了try-finally語句塊,即使出現(xiàn)異常,也能保證關(guān)閉文件句柄。
Pythonlogger?=?open("log.txt",?"w")??try:
logger.write('Hello?')
logger.write('World')??finally:
logger.close()
print?logger.closed
其實(shí),Python的內(nèi)置file類型是支持上下文管理協(xié)議的,可以直接通過內(nèi)建函數(shù)dir()來查看file支持的方法和屬性:
Python>>>?print?dir(file)
['__class__',?'__delattr__',?'__doc__',?'__enter__',?'__exit__',?'__format__',?'??__getattribute__',?'__hash__',?'__init__',?'__iter__',?'__new__',?'__reduce__',??'__reduce_ex__',?'__repr__',?'__setattr__',?'__sizeof__',?'__str__',?'__subclass??hook__',?'close',?'closed',?'encoding',?'errors',?'fileno',?'flush',?'isatty',?'??mode',?'name',?'newlines',?'next',?'read',?'readinto',?'readline',?'readlines',??'seek',?'softspace',?'tell',?'truncate',?'write',?'writelines',?'xreadlines']??>>>
所以,可以通過with語句來簡(jiǎn)化上面的代碼,代碼的效果是一樣的,但是使用with語句的代碼更加的簡(jiǎn)潔:
Pythonwith?open("log.txt",?"w")?as?logger:
logger.write('Hello?')
logger.write('World')
print?logger.closed
自定義上下文管理器
對(duì)于自定義的類型,可以通過實(shí)現(xiàn)__enter__和__exit__方法來實(shí)現(xiàn)上下文管理器。
看下面的代碼,代碼中定義了一個(gè)MyTimer類型,這個(gè)上下文管理器可以實(shí)現(xiàn)代碼塊的計(jì)時(shí)功能:
Pythonimport?time
class?MyTimer(object):
def?__init__(self,?verbose?=?False):
self.verbose?=?verbose
def?__enter__(self):
self.start?=?time.time()
return?self
def?__exit__(self,?*unused):
self.end?=?time.time()
self.secs?=?self.end?-?self.start
self.msecs?=?self.secs?*?1000
if?self.verbose:
print?"elapsed?time:?%f?ms"?%self.msecs
下面結(jié)合with語句使用這個(gè)上下文管理器:
Pythondef?fib(n):
if?n?in?[1,?2]:
return?1
else:
return?fib(n-1)?+?fib(n-2)
with?MyTimer(True):
print?fib(30)
代碼輸出結(jié)果為:
異常處理和__exit__
在使用上下文管理器中,如果代碼塊 (with_suite)產(chǎn)生了異常,__exit__方法將被調(diào)用,而__exit__方法又會(huì)有不同的異常處理方式。
當(dāng)__exit__方法退出當(dāng)前運(yùn)行時(shí)上下文時(shí),會(huì)并返回一個(gè)布爾值,該布爾值表明了”如果代碼塊 (with_suite)執(zhí)行中產(chǎn)生了異常,該異常是否須要被忽略”。
1. __exit__返回False,重新拋出(re-raised)異常到上層
修改前面的例子,在MyTimer類型中加入了一個(gè)參數(shù)”ignoreException”來表示上下文管理器是否會(huì)忽略代碼塊 (with_suite)中產(chǎn)生的異常。
Pythonimport?time
class?MyTimer(object):
def?__init__(self,?verbose?=?False,?ignoreException?=?False):
self.verbose?=?verbose
self.ignoreException?=?ignoreException
def?__enter__(self):
self.start?=?time.time()
return?self
def?__exit__(self,?*unused):
self.end?=?time.time()
self.secs?=?self.end?-?self.start
self.msecs?=?self.secs?*?1000
if?self.verbose:
print?"elapsed?time:?%f?ms"?%self.msecs
return?self.ignoreException
try:
with?MyTimer(True,?False):
raise?Exception("Ex4Test")
except?Exception,?e:
print?"Exception?(%s)?was?caught"?%e??else:
print?"No?Exception?happened"
運(yùn)行這段代碼,會(huì)得到以下結(jié)果,由于__exit__方法返回False,所以代碼塊 (with_suite)中的異常會(huì)被繼續(xù)拋到上層代碼。
2. __exit__返回Ture,代碼塊 (with_suite)中的異常被忽略
將代碼改為__exit__返回為True的情況:
Pythontry:
with?MyTimer(True,?True):
raise?Exception("Ex4Test")
except?Exception,?e:
print?"Exception?(%s)?was?caught"?%e??else:
print?"No?Exception?happened"
運(yùn)行結(jié)果就變成下面的情況,代碼塊 (with_suite)中的異常被忽略了,代碼繼續(xù)運(yùn)行:
一定要小心使用__exit__返回Ture的情況,除非很清楚為什么這么做。
3. 通過__exit__函數(shù)完整的簽名獲取更多異常信息
對(duì)于__exit__函數(shù),它的完整簽名如下,也就是說通過這個(gè)函數(shù)可以獲得更多異常相關(guān)的信息。__exit__(self, exception_type, exception_value, traceback)
繼續(xù)修改上面例子中的__exit__函數(shù)如下:
Pythondef?__exit__(self,?exception_type,?exception_value,?traceback):
self.end?=?time.time()
self.secs?=?self.end?-?self.start
self.msecs?=?self.secs?*?1000
if?self.verbose:
print?"elapsed?time:?%f?ms"?%self.msecs
print?"exception_type:?",?exception_type
print?"exception_value:?",?exception_value
print?"traceback:?",?traceback
return?self.ignoreException
這次運(yùn)行結(jié)果中,就顯示出了更多異常相關(guān)的信息了:
總結(jié)
本文介紹了Python中的上下文管理器,以及如何結(jié)合with語句來使用上下文管理器。
總結(jié)一下with 語句的執(zhí)行流程:執(zhí)行context_expr 以獲取上下文管理器對(duì)象
調(diào)用上下文管理器的 __enter__() 方法如果有 as var 從句,則將 __enter__() 方法的返回值賦給 var
執(zhí)行代碼塊 with_suite
調(diào)用上下文管理器的 __exit__() 方法,如果 with_suite 產(chǎn)生異常,那么該異常的 type、value 和 traceback 會(huì)作為參數(shù)傳給 __exit__(),否則傳三個(gè) None如果 with_suite 產(chǎn)生異常,并且 __exit__() 的返回值等于 False,那么這個(gè)異常將被重新拋出到上層
如果 with_suite 產(chǎn)生異常,兵器 __exit__() 的返回值等于 True,那么這個(gè)異常就被忽略,繼續(xù)執(zhí)行后面的代碼
在很多情況下,with語句可以簡(jiǎn)化代碼,并增加代碼的健壯性。
本文來自投稿,不代表訪得立場(chǎng),如若轉(zhuǎn)載,請(qǐng)注明出處:http://www.found5.com//view/393.html
總結(jié)
以上是生活随笔為你收集整理的python语言使用什么语句实现上下文管理协议_Python 上下文管理器的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java汽车管理系统_坑爹!花费2亿耗时
- 下一篇: 32位python和64位python区