Python标准库的contextlib模块定义了ContextManager类,该类的对象可以正确管理程序中的资源。Python具有与上下文管理器一起使用的with关键字。文件对象(由内置open()
函数返回)支持ContextManager API。因此,我们经常发现在处理文件时使用了关键字。
以下代码块打开一个文件,并在其中写入一些数据。操作结束后,文件被关闭,失败的文件描述符可能会泄漏而导致文件损坏。
f = open("file.txt","w") f.write("hello world") f.close()
但是,使用以下语法使用文件的上下文管理器功能可以完成相同的文件操作。
with open("file.txt","w") as f: f.write("hello world") print ("file is closed")
如上 ,文件对象实现了ContextManager。通过with关键字启用。with块包含要为文件对象处理的语句。with块结束后,文件对象将自动关闭(该close()
方法无需显式调用)。受到块影响的任何对象将仅在块内部处于活动状态,并将立即放置在其末端。
ContextManager类具有两个基本方法__enter __()和__exit __()
__enter __() -with块开始时将被调用。它指示程序已进入与此对象相关的运行时上下文。
with块结束时会调用__exit __() -。它指示程序退出与此对象相关的运行时上下文。
文件对象也拥有这两种方法,可以通过以下解释器会话确认。
>>> f = open("file.txt","w") >>> f.__enter__() <_io.TextIOWrapper name = 'file.txt' mode = 'w' encoding = 'cp1252'> >>> f.write("hello world") 11 >>> f.__exit__() >>> f.write("hello world") Traceback (most recent call last): File "<pyshell#10>", line 1, in <module> f.write("hello world") ValueError: I/O operation on closed file.
调用__exit __()方法时,文件关闭。这就是ValueError出现的原因,因为我们在关闭文件后尝试将一些数据写入文件。
下面给出了contextManager的更通用的用法。首先,我们使用__enter __()和__exit __()方法定义一个类,并使用with语句为其对象启用contextManager。
import contextlib class WithExample: def __init__(self): print ("object initialized") def __enter__(self): print ("entered context") def __exit__(self, *args): print ("exited context") with WithExample() as w: print ('this is a contextlib example') print ('used by with statement') print ('end of with block')
输出显示,只要with块开始,就会执行__enter __()方法。块中的语句得到处理。块结束后,将自动调用__exit __()方法。
object initialized entered context this is a contextlib example used by with statement exited context end of with block
contextlib模块具有@contextmanager装饰器,借助它,我们可以编写基于生成器的工厂函数来自动支持with语句。使用装饰器对文件对象进行上下文管理如下-
from contextlib import contextmanager @contextmanager def openfile(name): try: f = open(name, 'w') yield f finally: f.close() with openfile(file.txt') as f: f.write('hello world') print ('file is closed')
因此,ContextManager是Python的一个非常有用的功能,用于有效管理程序中的资源。