上下文管理器最常用在需要管理类似文件,网络连接和锁这样的资源的程序中。这些资源的关键点在于他们必须显式地进行关闭或释放才能正确工作。
要编写一个上下文管理器,其背后的主要原则就是我们编写的代码需要包含在由with语句定义的代码块中。要让对象能够兼容with语句,需要实现__enter__()
和__exit__()
方法。
实现一个文件的上下文管理器
class File(object):
def __init__(self, file, mode):
self.file = file
self.mode = mode
def __enter__(self):
self.opened_file = open(self.file, self.mode)
return self.opened_file
def __exit__(self, type, value, traceback):
self.opened_file.close()
# 实现了__enter__()和__exit__()方法后,就能通过with语句进行上下文管理
with File('hello.txt', 'w') as f:
f.write('hello')
内部实现过程:
1、with语句先暂存了File
类的__exit__
方法,然后它调用File
类的__enter__
方法。
2、__enter__
方法打开文件并返回给with语句,打开的文件句柄被传递给opened_file参数。
3、with语句调用之前暂存的__exit__
方法,__exit__
方法关闭了文件。
异常处理:
1、 它把异常的type,value和traceback传递给__exit__
方法
2、 它让__exit__
方法来处理异常
3、如果__exit__
返回的是True
,那么这个异常就被忽略。
4、如果__exit__
返回的是True
以外的任何东西,那么这个异常将被with语句抛出。
通过@contextmanager实现上下文管理器
利用contextlib模块中的@contextmanager装饰器,可以非常方便的编写一些小巧的上下文管理器函数。
from contextlib import contextmanager
@contextmanager
def file(file, mode):
file = open(file, mode)
try:
yield file
finally:
file.close()
# 使用
with file('hello.txt', 'w') as f:
f.write('hello world!')
在file()
函数中,所有位于yield
之前的代码会作为上下文管理器的__enter__()
方法来执行,而所有位于yield
之后的代码会作为__exit__()
方法执行。如果有异常产生,则会在yield
语句中抛出。