我们前面文章介绍了迭代器和可迭代对象, 这次介绍 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://www.cnblogs.com/flashBoxer/p/9664813.html