对文件操作完成后应该立即关闭它们,一种比较古老的方法是try_finally块
,但Python提供了一种更为简单的解决方案:with语句
。
with语句的语法为:
with 表达式 [as 目标]:
代码块
with语句支持嵌套,支持多个with子句,它们两者可以相互转换。
# 多个with子句
with expr1 as e1, expr2 as e2:
pass
# 嵌套形式
with expr1 as e1:
with expr2 as e2:
pass
with语句可以在代码块执行完毕后还原进入代码块时的现场。包含有with语句的代码块的执行过程如下:
-
计算表达式的值,返回一个上下文管理器对象。
-
加载上下文管理器对象的__exit__()方法以备后用。
-
调用上下文管理器对象的__enter__()方法。
-
如果with语句中设置了目标对象,则将__enter__()方法的返回值赋值给目标对象。
-
执行with中的代码块。
-
如果代码正常结束,调用上下文管理器对象的__exit__()方法,其返回值直接忽略。
-
如果代码执行过程中发生异常,调用上下文管理器对象的__exit__()方法,并将异常类型、值及trackback信息作为参数传递给__exit__()方法。如果__exit__()返回值为false,则异常会被重新抛出;如果其返回值为true,异常被挂起,程序继续执行。
with的神奇实际得益于一个被称为上下文管理器(context manager)的东西,它用来创建一个运行时的环境。上下文管理器定义程序运行时需要建立的上下文,处理程序的进入和退出,实现了上下文管理协议,即在对象中定义__enter__()和__eixt()方法。
-
__enter__
:不接受任何参数,进入with语句时调用,其返回值被赋给关键字as后面的变量。 -
__exit__
:接受三个参数,异常类型、异常对象和异常跟踪。离开方法时被调用,若返回False将抑制所有异常。它可以处理异常、清理现场或者处理with块中语句执行完成之后需要处理的动作。
上下文管理器主要作用于资源共享,在实际应用中__enter__()和__exit__()方法基本用于资源分配以及释放相关的工作,如打开/关闭文件、异常处理、断开流的连接、锁分配等。
为了更好地辅助上下文管理,Python还提供了contextlib模块
,该模块是通过Generator实现的,contextlib中的contextmanager作为装饰器来提供一种针对函数级别的上下文管理机制,可以直接作用于函数/对象而不用去关心__enter__()和__exit__()方法的具体实现。
(最近更新:2019年05月20日)