2.1.3 上下文管理器
上下文管理器是带有enter
和exit
方法的对象,在with语句中使用:
In [ ]:
with manager as var:
do_something(var)
最简单的等价case是
In [ ]:
var = manager.__enter__()
try:
do_something(var)
finally:
manager.__exit__()
换句话说,在PEP343定义的上下文管理器协议,使将try..except..finally结构中枯燥的部分抽象成一个独立的类,而只保留有趣的do_something
代码块成为可能。
- 首先调用enter方法。它会返回一个值被赋值给
var
。as
部分是可选的:如果不存在,enter
返回的值将被忽略。 with
下面的代码段将被执行。就像try
从句一样,它要么成功执行到最后,要么break、continue或者return,或者它抛出一个异常。无论哪种方式,在这段代码结束后,都将调用exit。如果抛出异常,关于异常的信息会传递给exit
,将在下一个部分描述。在一般的情况下,异常将被忽略,就像finally
从句一样,并且将在exit
结束时重新抛出。 假如我们想要确认一下文件是否在我们写入后马上关闭:
In [23]:
class closing(object):
def __init__(self, obj):
self.obj = obj
def __enter__(self):
return self.obj
def __exit__(self, *args):
self.obj.close()
with closing(open('/tmp/file', 'w')) as f:
f.write('the contents\n')
这里我们确保当with
代码段退出后,f.close()
被调用。因为关闭文件是非常常见的操作,对这个的支持已经可以在file
类中出现。它有一个exit方法,调用了close
并且被自己用于上下文管理器:
In [ ]:
with open('/tmp/file', 'a') as f:
f.write('more contents\n')
try..finally
的常用用途是释放资源。不同的情况都是类似的实现:在enter
阶段,是需要资源的,在exit
阶段,资源被释放,并且异常,如果抛出的话,将被传递。就像with文件一样,当一个对象被使用后通常有一些自然的操作,最方便的方式是由一个内建的支持。在每一次发布中,Python都在更多的地方提供了支持:
- 所以类似文件的对象:
- file ➔ 自动关闭
- fileinput,tempfile (py >= 3.2)
- bz2.BZ2File,gzip.GzipFile,tarfile.TarFile,zipfile.ZipFile
- ftplib,nntplib ➔ 关闭连接 (py >= 3.2 或 3.3)
- 锁
- multiprocessing.RLock ➔ 锁和解锁
- multiprocessing.Semaphore
- memoryview ➔ 自动释放 (py >= 3.2 和 2.7)
- decimal.localcontext ➔ 临时修改计算的精度
- _winreg.PyHKEY ➔ 打开或关闭hive键
- warnings.catch_warnings ➔ 临时杀掉警告
- contextlib.closing ➔ 与上面的例子类似,调用
close
- 并行程序
- concurrent.futures.ThreadPoolExecutor ➔ 激活并行,然后杀掉线程池 (py >= 3.2)
- concurrent.futures.ProcessPoolExecutor ➔ 激活并行,然后杀掉进程池 (py >= 3.2)
- nogil ➔ 临时解决GIL问题 (仅cython :( )