引言
对一个文件进行操作,我们可以这样写:1
2
3f = open(file, mode)
...
f.close() #关闭文件
但是可能会在文件操作的过程中触发异常,导致f.close()
没有被执行,于是我们对其做出以下改良:1
2
3
4
5f = open(file, mode)
try:
...
finally:
f.close()
嗯…貌似有点繁琐,能不能用一个简单点的方法呢?这时with
就出现了:1
2with open(file, mode) as f:
...
相当于用with包裹后,自动处理了文件的关闭。
概念
那具体是怎么实现的呢?
我们先来看看有关上下文管理器的概念:
上下文管理协议(Context Management Protocol)
包含方法 enter() 和 exit(),支持
该协议的对象要实现这两个方法。上下文管理器(Context Manager)
支持上下文管理协议的对象,这种对象实现了
enter() 和 exit() 方法。上下文管理器定义执行 with 语句时要建立的运行时上下文,
负责执行 with 语句块上下文中的进入与退出操作。通常使用 with 语句调用上下文管理器,
也可以通过直接调用其方法来使用。运行时上下文(runtime context)
由上下文管理器创建,通过上下文管理器的 enter() 和
exit() 方法实现,enter() 方法在语句体执行之前进入运行时上下文,exit() 在
语句体执行完后从运行时上下文退出。with 语句支持运行时上下文这一概念。
with 语句执行过程
1 | context_manager = context_expression |
自定义上下文管理器
现在试着自己实现一个上下文管理器,实现对文件资源的释放1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24#/usr/bin/python
# -*- coding: utf-8 -*-
# 自行定義 Context Manager
class File(object):
def __init__(self, filename, mode):
# 設定檔名與開檔模式
self.filename = filename
self.mode = mode
# 配給資源(開啟檔案)
def __enter__(self):
print("開啟檔案:" + self.filename)
self.open_file = open(self.filename, self.mode)
return self.open_file
# 回收資源(關閉檔案)
def __exit__(self, type, value, traceback):
print("關閉檔案:" + self.filename)
self.open_file.close()
with File("file.txt", "w") as f:
print("寫入檔案...")
f.write("Hello, world.")
另一种实现方式1
2
3
4
5
6
7
8
9
10
11
12
13
14
15#/usr/bin/python
# -*- coding: utf-8 -*-
from contextlib import contextmanager
# 自行定義 Context Manager
@contextmanager
def open_file(name, mode):
# 配給資源(開啟檔案)
f = open(name, mode)
yield f
# 回收資源(關閉檔案)
f.close()
with open_file('file.txt', 'w') as f:
f.write("Hello, world.")
对with的总结
with适用于对资源进行访问的场合,确保不管使用过程中是否发生异常都会执行必要的“清理”操作,释放资源,比如文件使用后自动关闭、线程中锁的自动获取和释放等。
nested 同时使用多个with嵌套
1 | with nested(A(), B(), C()) as (X, Y, Z): |
1 | with A() as X: |
需要注意的是,发生异常后,如果某个上下文管理器的 exit() 方法对异常处理返回 False,则更外层的上下文管理器不会监测到异常。
参考