python语言使用什么语句实现上下文管理协议_Python 上下文管理器
上下文管理器
在使用Python編程中,可以會經(jīng)常碰到這種情況:有一個特殊的語句塊,在執(zhí)行這個語句塊之前需要先執(zhí)行一些準(zhǔn)備動作;當(dāng)語句塊執(zhí)行完成后,需要繼續(xù)執(zhí)行一些收尾動作。
例如:當(dāng)需要操作文件或數(shù)據(jù)庫的時候,首先需要獲取文件句柄或者數(shù)據(jù)庫連接對象,當(dāng)執(zhí)行完相應(yīng)的操作后,需要執(zhí)行釋放文件句柄或者關(guān)閉數(shù)據(jù)庫連接的動作。
又如,當(dāng)多線程程序需要訪問臨界資源的時候,線程首先需要獲取互斥鎖,當(dāng)執(zhí)行完成并準(zhǔn)備退出臨界區(qū)的時候,需要釋放互斥鎖。
對于這些情況,Python中提供了上下文管理器(Context Manager)的概念,可以通過上下文管理器來定義/控制代碼塊執(zhí)行前的準(zhǔn)備動作,以及執(zhí)行后的收尾動作。
上下文管理協(xié)議
那么在Python中怎么實現(xiàn)一個上下文管理器呢?這里,又要提到兩個”魔術(shù)方法”,__enter__和__exit__,下面就是關(guān)于這兩個方法的具體介紹。__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)我們需要創(chuàng)建一個上下文管理器類型的時候,就需要實現(xiàn)__enter__和__exit__方法,這對方法就稱為上下文管理協(xié)議(Context Manager Protocol),定義了一種運行時上下文環(huán)境。
with語句
在Python中,可以通過with語句來方便的使用上下文管理器,with語句可以在代碼塊運行前進入一個運行時上下文(執(zhí)行__enter__方法),并在代碼塊結(jié)束后退出該上下文(執(zhí)行__exit__方法)。
with語句的語法如下:
Pythonwith?context_expr?[as?var]:
with_suite
context_expr是支持上下文管理協(xié)議的對象,也就是上下文管理器對象,負(fù)責(zé)維護上下文環(huán)境
as var是一個可選部分,通過變量方式保存上下文管理器對象
with_suite就是需要放在上下文環(huán)境中執(zhí)行的語句塊
在Python的內(nèi)置類型中,很多類型都是支持上下文管理協(xié)議的,例如file,thread.LockType,threading.Lock等等。這里我們就以file類型為例,看看with語句的使用。
with語句簡化文件操作
當(dāng)需要寫一個文件的時候,一般都會通過下面的方式。代碼中使用了try-finally語句塊,即使出現(xiàn)異常,也能保證關(guān)閉文件句柄。
Pythonlogger?=?open("log.txt",?"w")??try:
logger.write('Hello?')
logger.write('World')??finally:
logger.close()
print?logger.closed
其實,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語句來簡化上面的代碼,代碼的效果是一樣的,但是使用with語句的代碼更加的簡潔:
Pythonwith?open("log.txt",?"w")?as?logger:
logger.write('Hello?')
logger.write('World')
print?logger.closed
自定義上下文管理器
對于自定義的類型,可以通過實現(xiàn)__enter__和__exit__方法來實現(xiàn)上下文管理器。
看下面的代碼,代碼中定義了一個MyTimer類型,這個上下文管理器可以實現(xiàn)代碼塊的計時功能:
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語句使用這個上下文管理器:
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__方法又會有不同的異常處理方式。
當(dāng)__exit__方法退出當(dāng)前運行時上下文時,會并返回一個布爾值,該布爾值表明了”如果代碼塊 (with_suite)執(zhí)行中產(chǎn)生了異常,該異常是否須要被忽略”。
1. __exit__返回False,重新拋出(re-raised)異常到上層
修改前面的例子,在MyTimer類型中加入了一個參數(shù)”ignoreException”來表示上下文管理器是否會忽略代碼塊 (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"
運行這段代碼,會得到以下結(jié)果,由于__exit__方法返回False,所以代碼塊 (with_suite)中的異常會被繼續(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"
運行結(jié)果就變成下面的情況,代碼塊 (with_suite)中的異常被忽略了,代碼繼續(xù)運行:
一定要小心使用__exit__返回Ture的情況,除非很清楚為什么這么做。
3. 通過__exit__函數(shù)完整的簽名獲取更多異常信息
對于__exit__函數(shù),它的完整簽名如下,也就是說通過這個函數(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
這次運行結(jié)果中,就顯示出了更多異常相關(guān)的信息了:
總結(jié)
本文介紹了Python中的上下文管理器,以及如何結(jié)合with語句來使用上下文管理器。
總結(jié)一下with 語句的執(zhí)行流程:執(zhí)行context_expr 以獲取上下文管理器對象
調(diào)用上下文管理器的 __enter__() 方法如果有 as var 從句,則將 __enter__() 方法的返回值賦給 var
執(zhí)行代碼塊 with_suite
調(diào)用上下文管理器的 __exit__() 方法,如果 with_suite 產(chǎn)生異常,那么該異常的 type、value 和 traceback 會作為參數(shù)傳給 __exit__(),否則傳三個 None如果 with_suite 產(chǎn)生異常,并且 __exit__() 的返回值等于 False,那么這個異常將被重新拋出到上層
如果 with_suite 產(chǎn)生異常,兵器 __exit__() 的返回值等于 True,那么這個異常就被忽略,繼續(xù)執(zhí)行后面的代碼
在很多情況下,with語句可以簡化代碼,并增加代碼的健壯性。
本文來自投稿,不代表訪得立場,如若轉(zhuǎn)載,請注明出處:http://www.found5.com//view/393.html
總結(jié)
以上是生活随笔為你收集整理的python语言使用什么语句实现上下文管理协议_Python 上下文管理器的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java汽车管理系统_坑爹!花费2亿耗时
- 下一篇: 32位python和64位python区