Python实用程序与with语句上下文 (contextlib)
Python标准库中的contextlib模块定义了ContextManager类,其对象可以正确管理程序中的资源。Python具有与上下文管理器一起工作的with关键字。文件对象(由内置的open()函数返回)支持ContextManager API。因此,我们经常在处理文件时使用with关键字。
下面的代码块打开一个文件并在其中写入一些数据。操作完成后,文件将被关闭,否则文件描述符可能会泄漏,导致文件损坏。
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()方法)。任何被with块处理的对象都只在块内有效,并在其结束时立即被释放。
ContextManager类有两个基本方法__enter__()和__exit__()
__enter__() − 当with块开始时将被调用。它表示程序已进入与此对象相关的运行时上下文。
__exit__() − 当with块结束时被调用。它表示程序退出与此对象相关的运行时上下文。
文件对象也拥有这两个方法,可以通过以下解释器会话确认。
>>> 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的一个非常有用的特性,可以有效地管理程序中的资源。