python中读写文件的语句是相当少的,代码如下:
# 创建文件
f = open('output.txt', 'w')
# 写入内容进文件
f.write('python')
# 关闭文件
f.close()
但是,这还是不够简洁,有可能会忘记写关闭文件的语句f.close(),python执行写入文件的操作是惰性的,因为读写文件都是io操作,所以执行任务处于较低的优先级,当前没有其他任务就会执行写入,要是有其他任务,写入文件就会一直等待;
所以就有了with语句,代码如下:
with open('output.txt', 'w') as f:
f.write('python')
with语句会自动处理上下文,自动关闭文件,那么底层是怎么实现的呢,主要是依赖于__enter__(管理上文),__exit__(管理下文)方法,代码如下:
# 面向对象实现上下文管理
class File(object):
def __init__(self, filename, mode):
self.filename = filename
self.mode = mode
def __enter__(self):
print('entering')
self.f = open(self.filename, self.mode)
return self.f
def __exit__(self, *args):
print('exit')
self.f.close()
with File('input.txt', 'w') as f:
f.write('i love you')
结果:
entering
exit
从代码上来看,__enter__方法就是负责一段业务的环境准备(打开文件),with语句里就执行你要撰写的业务,__exit__方法就负责业务的收尾工作(关闭文件),所以只要是实现了这两个方法的类,就可以用with语句管理上下文。
python也提供了一个模块contextlib,来处理上下文,代码如下:
from contextlib import contextmanager
# 通过 yield 将函数分割成两部分,
# yield 之前的语句在 __enter__ 方法中执行,
# yield 之后的语句在 __exit__ 方法中执行。
# 紧跟在 yield 后面的值是函数的返回值;
@contextmanager
def my_open(path, mode):
f = open(path, mode)
yield f
f.close()
with my_open('input.txt', 'r') as f:
content = f.read()
print(content)
# 结果
i love you
contextmanager就是一个装饰器,配合yield语句,可以将函数里面的业务分割成上文,yield之前的语句是在__enter__方法中执行,yield后面的值是函数的返回值,yield之后的语句在__exit__方法中执行;
总结:
with语句和上下文管理,__enter__方法和__exit__方法就是一种协议,满足这个协议,就可以用with语句访问,并管理上下文;