我們前面文章介紹了迭代器和可迭代對象,這次介紹python的上下文管理。在python中實現了__enter__和__exit__方法,即支持上下文管理器協議。上下文管理器就是支持上下文管理器協議的對象,它是為了with而生。當with語句在開始運行時,會在上下文管理器對象上調用 __enter__ ...
我們前面文章介紹了迭代器和可迭代對象,這次介紹python的上下文管理。在python中實現了__enter__和__exit__方法,即支持上下文管理器協議。上下文管理器就是支持上下文管理器協議的對象,它是為了with而生。當with語句在開始運行時,會在上下文管理器對象上調用 __enter__ 方法。with語句運行結束後,會在上下文管理器對象上調用 __exit__ 方法
with的語法:
with EXPR as VAR:
BLOCK
這是上面語法的偽代碼:
mgr = (EXPR) exit = type(mgr).__exit__ # Not calling it yet value = type(mgr).__enter__(mgr) exc = True try: try: VAR = value # Only if "as VAR" is present BLOCK except: # The exceptional case is handled here exc = False if not exit(mgr, *sys.exc_info()): raise # The exception is swallowed if exit() returns true finally: # The normal and non-local-goto cases are handled here if exc: exit(mgr, None, None, None)
1、生成上下文管理器mgr
2、如果沒有發現__exit__, __enter__兩個方法,解釋器會拋出AttributeError異常
3、調用上下文管理器的 __enter__() 方法
4、如果語法里的as VAR沒有寫,那麼 偽代碼里的 VAR= 這部分也會同樣被忽略
5、如果BLOCK中的代碼正常結束,或者是通過break, continue ,return 來結束,__exit__()會使用三個None的參數來返回
6、如果執行過程中出現異常,則使用 sys.exc_info的異常信息為參數調用 __exit__(exc_type, exc_value, exc_traceback)
之前我們對文件的操作是這樣的:
try: f = open('filename') except: print("Unexpected error:", sys.exc_info()[0]) else: print(f.readlines()) f.close()
現在有了with語句可以使代碼更加簡潔,減少編碼量,下麵的語句會在執行完後自動關閉文件(即使出現異常也會)。:
with open('example.info', 'r') as f: print(f.readlines())
一個例子:
class TmpTest: def __init__(self,filename): self.filename=filename def __enter__(self): self.f = open(self.filename, 'r') # return self.f def __exit__(self, exc_type, exc_val, exc_tb): self.f.close() test=TmpTest('file') with test as t: print ('test result: {}'.format(t))
返回:
test result: None
這個例子裡面__enter__沒有返回,所以with語句里的"as t"到的是None,修改一下上面的例子:
class TmpTest: def __init__(self,filename): self.filename=filename def __enter__(self): self.f = open(self.filename, 'r') return self.f def __exit__(self, exc_type, exc_val, exc_tb): self.f.close() test=TmpTest('file') with test as t: print ('test result: {}'.format(t))
返回:
test result: <_io.TextIOWrapper name='file' mode='r' encoding='cp936'>
如果在__init__或者__enter__中拋出異常,則不會進入到__exit__中:
class TmpTest: def __init__(self,filename): self.filename=filename print("__init__") raise ImportError def __enter__(self): self.f = open(self.filename, 'r') print("__enter__") return self.f def __exit__(self, exc_type, exc_val, exc_tb): print("__exit__") self.f.close() test=TmpTest('file') with test as t: print ('test result: {}'.format(t))
返回:
__init__ Traceback (most recent call last): File "D:/pythonScript/leetcode/leetcode.py", line 14, in <module> test=TmpTest('file') File "D:/pythonScript/leetcode/leetcode.py", line 5, in __init__ raise ImportError ImportError
如果在__exit__中返回True,則不會產生異常:
class TmpTest: def __init__(self,filename): self.filename=filename print("__init__") def __enter__(self): self.f = open(self.filename, 'r') print("__enter__") return self.f def __exit__(self, exc_type, exc_val, exc_tb): print("__exit__ {} ".format(exc_type)) self.f.close() return True test=TmpTest('file') with test as t: print ('test result: {}'.format(t)) raise ImportError print("no error")
返回:
__init__ __enter__ test result: <_io.TextIOWrapper name='file' mode='r' encoding='cp936'> __exit__ <class 'ImportError'> no error
參考: https://python3-cookbook.readthedocs.io/zh_CN/latest/c08/p03_make_objects_support_context_management_protocol.html?highlight=with
https://docs.python.org/3/library/stdtypes.html#typecontextmanager
https://www.python.org/dev/peps/pep-0343/