操作文件对象时可以:
with open('a.txt') as f:
'代码块'
上述叫做上下文管理协议,即with语句。
想象一下,你有两个需要结对执行的相关操作,然后,还要在他们中间放置一段代码。比如打开一个文件,操作文件,然后关闭该文件。
打开文件和关闭文件就是一个结对的操作。
上下文管理器的常见用例:是资源的加锁与解锁,文件的打开与关闭。
上下文管理器
上下文管理器协议:是指类需要实现 __ enter __ 和 __ exit __ 方法。
就跟迭代器有迭代器协议一样,迭代器协议需要实现 __ iter __ 和 __ next __ 方法。
上下文管理器,也就是支持上下文管理协议的对象,简单点讲就是,实现了 __ enter __ 和 __ exit __两个方法的类。这个类也叫做,上下文管理器的类。
写一个Open类,这个类是一个上下文管理器:
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):
里面的三个参数分别代表:异常类型,异常值,追溯信息。
注意: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')
我们来看看底层发生了什么?
- with语句先暂存了 Open 类的 __ exit __ 方法
- 然后调用 Open 类的 __ enter __ 方法
- __ enter __ 方法打开文件并返回给with语句
- 打开的文件句柄传递给 as 后面的 f 参数
- 执行with里面的代码块。
- 调用之前暂存的 __ exit __ 方法
- 关闭文件
在第4步和第6步之间,如果发生异常,Python会将异常的type,value,traceback传递给 __ exit __ 方法。
当异常发生时,with语句会采取哪些步骤?
- with把异常的type, value, traceback 传递给 __ exit __ 方法
- with让 __ exit __ 处理异常
- 如果 __ exit __ 返回的是True, 那么这个异常就被优雅的处理了。
- 如果 __ exit __ 返回的是True以外的任何东西,那个这个异常将被with 语句抛出。
当 __ exit __()返回值为True, 那么异常会被清空,就好像啥都没发生一样,with后的语句正常执行.。
完整模拟Open:
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处理。后面的代码正常运行
优点
- 使用with的语句的目的就是把代码块放入with中执行, with结束后,自动完成清理工作,无需干预。
- 在需要管理一些资源比如文件,网络连接和锁的编程环境中,可以在 __ 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('我是你爹')