一、with语句
with 语句:
要使用 with 语句,首先要明白上下文管理器这一概念,有了上下文管理器,with 语句才能工作。
什么是上下文管理器:
这个管理器就是在对象内实现了两个方法:enter() 和__exit__()
- enter()方法会在with的代码块执行之前执行
- exit()会在代码块执行结束后执行。exit()方法内会自带当前对象的清理方法。
下面是一组与上下文管理器和with 语句有关的概念:
- 上下文管理协议(Context Management Protocol):包含方法 enter() 和__exit__(),支持该协议的对象要实现这两个方法。
- 上下文管理器(Context Manager):支持上下文管理协议的对象,这种对象实现了__enter__() 和 exit() 方法。上下文管理器定义执行 with 语句时要建立的运行时上下文,负责执行 with 语句块上下文中的进入与退出操作。通常使用 with语句调用上下文管理器,也可以通过直接调用其方法来使用。
- 运行时上下文(runtimecontext):由上下文管理器创建,通过上下文管理器的 enter() 和__exit__() 方法实现,enter()方法在语句体执行之前进入运行时上下文,exit() 在语句体执行完后从运行时上下文退出。with 语句支持运行时上下文这一概念。
- 上下文表达式(Context Expression):with 语句中跟在关键字 with之后的表达式,该表达式要返回一个上下文管理器对象。
- 语句体(with-body):with语句包裹起来的代码块,在执行语句体之前会调用上下文管理器的 enter() 方法,执行完语句体之后会执行__exit__() 方法。
-
作用:适用于对资源进行访问的场合,确保不管使用过程中是否发生异常都会执行必要的“清理”操作,释放资源,比如文件使用后自动关闭、线程中锁的自动获取和释放等。
-
工作条件:使用with语句调用上下文管理器(Context Manager)
-
基本语法:
-
使用with语句语法格式:
这里 context_expression 要返回一个上下文管理器对象,该对象并不赋值给 as 子句中的 target(s) ,如果指定了 as 子句的话,会将上下文管理器的 enter() 方法的返回值赋值给 target(s)。target(s) 可以是单个变量,或者由“()”括起来的元组(不能是仅仅由“,”分隔的变量列表,必须加“()”)。
with context_expression[as target(s)]
with-body
- 使用 with 语句操作文件对象
这里使用了 with 语句,不管在处理文件过程中是否发生异常,都能保证 with 语句执行完毕后已经关闭了打开的文件句柄。
for line in somefile:
print line
# ...more code
Python的with语句是提供一个有效的机制,让代码更简练,同时在异常产生时,清理工作更简单。
二、 with语句执行过程
- 执行 context_expression,生成上下文管理器 context_manager
- 调用上下文管理器的 enter() 方法;如果使用了 as 子句,则将 enter() 方法的返回值赋值给 as 子句中的 target(s)
- 执行语句体 with-body
- 不管是否执行过程中是否发生了异常,执行上下文管理器的 exit() 方法,exit() 方法负责执行“清理”工作,如释放资源等。如果执行过程中没有出现异常,或者语句体中执行了语句 break/continue/return,则以 None 作为参数调用 exit(None, None, None) ;如果执行过程中出现异常,则使用 sys.exc_info 得到的异常信息为参数调用 exit(exc_type, exc_value, exc_traceback)
- 出现异常时,如果 exit(type, value, traceback) 返回 False,则会重新抛出异常,让with 之外的语句逻辑来处理异常,这也是通用做法;如果返回 True,则忽略异常,不再对异常进行处理
三、 with语句如何工作
with语句执行的解析:
with context_expr() as var:
doSomething()
- 当with语句执行时,便执行上下文表达式(context_expr)(一般为某个方法)来获得一个上下文管理器对象,上下文管理器的职责是提供一个上下文对象,用于在with语句块中处理细节:
- 一旦获得了上下文对象,就会调用它的__enter__()方法,将完成with语句块执行前的所有准备工作,如果with语句后面跟了as语句,则用__enter__()方法的返回值来赋值;
- 当with语句块结束时,无论是正常结束,还是由于异常,都会调用上下文对象的__exit__()方法,exit()方法有3个参数,如果with语句正常结束,三个参数全部都是 None;如果发生异常,三个参数的值分别等于调用sys.exc_info()函数返回的三个值:类型(异常类)、值(异常实例)和跟踪记录(traceback),相应的跟踪记录对象。
- 因为上下文管理器主要作用于共享资源,enter()和__exit__()方法基本是完成的是分配和释放资源的低层次工作,比如:数据库连接、锁分配、信号量加/减、状态管理、文件打开/关闭、异常处理等。
class A(object):
def __enter__(self):
print('__enter__() called')
return self
def print_hello(self):
print("hello world!")
def __exit__(self, e_t, e_v, t_b):
print('__exit__() called')
首先会执行__enter__方法
with A() as a: # a为__enter__的返回对象
a.print_hello()
print('got instance')
结束会执行__exit__方法
输出结果:
__enter__() called
hello world!
got instance
__exit__() called