操做文件对象时能够:python
with open('a.txt') as f: '代码块'
上述叫作上下文管理协议,即with语句。编程
想象一下,你有两个须要结对执行的相关操做,而后,还要在他们中间放置一段代码。好比打开一个文件,操做文件,而后关闭该文件。网络
打开文件和关闭文件就是一个结对的操做。函数
上下文管理器的常见用例:是资源的加锁与解锁,文件的打开与关闭。code
上下文管理器协议:是指类须要实现 __ enter __ 和 __ exit __ 方法。对象
就跟迭代器有迭代器协议同样,迭代器协议须要实现 __ iter __ 和 __ next __ 方法。utf-8
上下文管理器,也就是支持上下文管理协议的对象,简单点讲就是,实现了 __ enter __ 和 __ exit __两个方法的类。这个类也叫作,上下文管理器的类。资源
写一个Open类,这个类是一个上下文管理器:it
class Open: def __init__(self, filepath, encoding): self.filepath = filepath self.encoding = encoding def __enter__(self): # 当这个类被with关键字执行时,就自动调用这个方法。有返回值则调用给 as 声明的变量 print('当这个类被with关键字执行时,就自动调用这个方法。有返回值则调用给 as 声明的变量') def __exit__(self, exc_type, exc_val, exc_tb): print('with 中代码块执行完就执行我这个函数') with Open('1.txt', 'UTF-8') as f: print('with 里面的代码块') ''' 结果: 当这个类被with关键字执行时,就自动调用这个方法。有返回值则调用给 as 声明的变量 with 里面的代码块 with 中代码块执行完就执行我这个函数 '''
__ exit __(self, exc_type, exc_val, exc_tb):class
里面的三个参数分别表明:异常类型,异常值,追溯信息。
注意:with语句中的代码块出现异常后,with后的代码都没法执行
基于类的实现:完整实现Open方法
一个上下文管理器的类,起码要定义 __ enter __ 和 __ exit __ 方法。
class Open: def __init__(self, filepath, method): self.file = open(filepath, method, encoding='utf-8') def __enter__(self): return self.file def __exit__(self, type, value, traceback): self.file.close() with Open('1.txt', 'w') as f: f.write('1111111111')
咱们来看看底层发生了什么?
在第4步和第6步之间,若是发生异常,Python会将异常的type,value,traceback传递给 __ exit __ 方法。
当异常发生时,with语句会采起哪些步骤?
当 __ exit __()返回值为True, 那么异常会被清空,就好像啥都没发生同样,with后的语句正常执行.。
class Open: def __init__(self, filepath, mode='r', encoding='utf-8'): self.filepath = filepath self.mode = mode self.encoding = encoding def __enter__(self): self.file = open(self.filepath, mode=self.mode, encoding=self.encoding) return self.file def __exit__(self, exc_type, exc_val, exc_tb): print(exc_type) self.file.close() return True with Open('1.txt', 'w', encoding='utf-8') as f: f.write('哈哈哈') f.werwer # 抛出异常,交给exit处理。后面的代码正常运行
contextlib模块:可使用一个生成器实现一个上下文管理器,而不是使用一个类。众所周知,在类中还须要实现 __ enter __ 和 __ exit __ 。
from contextlib import contextmanager @contextmanager def point(x, y): print('在yield以前') yield x * y # yield出去的值赋给 as 后面的变量 print('在yield以后') with point(3, 4) as p: print('p',p) ''' 结果: 在yield以前 p 12 在yield以后 '''
利用contextlib模块实现一个open
@contextmanager def my_open(path): f = open(path, mode='w') yield f # 把这个f 赋给as后面的变量 f.close() with my_open('2.txt') as f: f.write('我是你爹')