with 语句
with 语句是 Pyhton 提供的一种简化语法,确保不管使用过程中是否发生异常都会执行必要的“清理”操作,释放资源。
在没有学习with的句法之前,通常我们都是使用try…finally语句,即使是在出现错误的情况下 也能运行某些清理代码。而今天学习的with语句就是对这些代码功能的一些简单封装。
with语句通常会使用在如下的几个环境之中:
- 打开关闭一个文件
- 释放一个锁
- 创建一个临时的代码补丁
- 在特殊环境下运行受保护的代码
那么,接下来我们就举打开关闭文件的例子说明:
我们先使用file()方法,看看两者之间有什么不同?
hosts=open('/etc/hosts')
try:
for line in hosts:
if line.startswith('#'): #startswith以‘#’结尾
continue
print(line.strip())
finally:
hosts.close()
上面的代码,我们是要打开位于/etc目录的hosts文件,使用file()方法之后,又手动调用close()关闭文件。
那么,使用with语句之后,我们可以重写为:
with open ('/etc/hosts') as hosts:
for line in hosts:
if line.startswith('#'): #startswith以‘#’结尾
continue
print(line.strip())
with的执行过程:
-
在执行 with 语句时,首先执行 with 后面的 open 代码
-
执行完代码后,会将代码的结果通过 as 保存到 hosts 中
-
然后在下面实现真正要执行的操作
-
在操作后面,并不需要写文件的关闭操作,文件会在使用完后自动关闭
上下文管理器
任何实现了 _enter_(self) 和 _exit_(self,exc_type,exc_value,traceback) 方法的对象都可称之为上下文管理器,上下文管理器对象可以使用 with 关键字。显然,文件(file)对象也实现了上下文管理器协议。
从底层原理的角度来说,with语句的执行过程如下:
- 调用__enter__方法,任何返回值都会绑定到指定的as字句
- 执行内部代码块 调用__exit__方法
- 执行内部代码块 调用__exit__方法执行内部代码块 调用__exit__方法
我们可以模拟实现一个自己的文件类,让该类实现 _enter_() 和 _exit_() 方法。
class File():
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("will exit")
self.f.close()
注:当我们打开文件时,就会自动调用_enter_,最终会返回该资源对象。当退出文件时,会自动调用__exit_方法,把文件关闭,做一些清理工作。
因为 File 类实现了上下文管理器,现在就可以使用 with 语句了。
with File('out.txt', 'w') as f:
print("writing")
f.write('哈哈哈,你好啊!')
contextlib模块
contextlib模块是实现上下文管理的另外一种方式,这个模块提供了与上下文管理器一起使用的辅助函数。它使用的是 contextmanager 装饰器,通过 yield 将函数分割成两部分,yield 之前的语句在\ enter 方法中执行,yield 之后的语句在 _exit_ 方法中执行。紧跟在 yield 后面的值是函数的返回值。
from contextlib import contextmanager
@contextmanager
def my_open(path, mode):
f = open(path, mode)
yield f
f.close()
使用:
with my_open('out.txt', 'w') as f:
f.write("hello , the simplest context manager")
参考文献:
【Python】with及上下文管理器的原理和应用
Pythong高级编程(第二版)