上下文管理器
上下文,在读书时主要是指承上启下的意思。而这里的上下文,则是指代码中的环境。上下文管理器就是管理上文与下文的环境。
任何实现了 __enter__()
和 __exit__()
方法的对象都可称之为上下文管理器,上下文管理器对象可以使用 with
关键字。python 语言中,很多内置的对象都是上下文管理器,例如 tkreading.Lock
、文件 file
甚至是第三方模块的 requests.get
对象也实现了上下文管理器。
那么文件对象是如何实现这两个方法的呢?我们可以模拟实现一个自己的文件类,让该类实现 __enter__()
和 __exit__()
方法。
python
class File:
def __init__(self, filename, mode):
self.filename = filename
self.mode = mode
def __enter__(self):
print("===进入上下文管理器===")
self.f = open(self.filename, self.mode)
return self.f
def __exit__(self, *args):
print("===退出上下文管理器===")
self.f.close()
__enter__()
方法返回资源对象,这里就是你将要打开的那个文件对象,__exit__()
方法处理一些清除工作。
这就是上下文管理协议的一个强大之处,异常可以在__exit__
进行捕获并由你自己决定如何处理,是抛出呢还是在这里就解决了。在__exit__
里返回 True
(没有return 就默认为 return False),就相当于告诉 Python解释器,这个异常我们已经捕获了,不需要再往外抛了。
在 写__exit__
函数时,需要注意的事,它必须要有这三个参数:
exc_type
:异常类型exc_val
:异常值exc_tb
:异常的错误栈信息
当主逻辑代码没有报异常时,这三个参数将都为 None。
因为 File 类实现了上下文管理器,现在就可以使用 with 语句了。
python
with File('out.txt', 'w') as f:
print("writing")
f.write('hello, python')
f.close() # 上下文环境退出时会自动调用 __exit__() 方法
这样,你就无需显示地调用 close 方法了,由系统自动去调用,哪怕中间遇到异常 close 方法也会被调用。
在我看来,这和 Python 崇尚的优雅风格有关。
- 可以以一种更加优雅的方式,操作(创建/获取/释放)资源,如文件操作、数据库连接;
- 可以以一种更加优雅的方式,处理异常;
总结
使用上下文管理器有三个好处:
- 提高代码的复用率;
- 提高代码的优雅度;
- 提高代码的可读性;