首发时间:2018-02-23 15:28python
以前看到一篇博客说博主python面试时遇到面试官提问with的原理,而那位博主的博文没有说起with原理,故有此文。
关于with语句,官方文档中是这样描述的:
The with
statement is used to wrap the execution of a block with methods defined by a context manager (see section With Statement Context Managers). This allows common try
...except
...finally
usage patterns to be encapsulated for convenient reuse. 面试
with_stmt ::= "with" with_item ("," with_item)* ":"
suite
数据库with_item ::=
expression
["as"target
] express
The execution of the with statement with one “item” proceeds as follows:测试
The context expression (the expression given in the with_item) is evaluated to obtain a context manager.ui
The context manager’s __exit__() is loaded for later use.this
The context manager’s __enter__() method is invoked.lua
If a target was included in the with statement, the return value from __enter__() is assigned to it.spa
Note
The with statement guarantees that if the __enter__() method returns without an error, then __exit__() will always be called. Thus, if an error occurs during the assignment to the target list, it will be treated the same as an error occurring within the suite would be. See step 6 below.翻译
The suite is executed.
The context manager’s __exit__() method is invoked. If an exception caused the suite to be exited, its type, value, and traceback are passed as arguments to __exit__(). Otherwise, three None arguments are supplied.
谷歌翻译成中文就是:
with语句用于使用由上下文管理器定义的方法来封装块的执行(请参见使用语句上下文管理器一节)。 这容许通用的try…except…finally使用模式被封装以便于重用【这句话大概意思就是“with语句”相似于try…except…finally封装以后的的状况】。
带有一个“项目”的with语句的执行过程以下:
1.上下文表达式(在with_item中给出的表达式)被评估以得到上下文管理器。【会区分类型来处理,如文件,进程等均可以使用with语句】
2.上下文管理器的__exit __()被加载供之后使用。【负责上下文的退出】
3.上下文管理器的__enter __()方法被调用。【负责上下文的进入】
4.若是在with语句中包含目标,则将__enter __()的返回值分配给它。【若是with后面跟着as 对象(如with open() as f),那么此对象得到with上下文对象的__enter__()的返回值,(附:应该是相似操做数据库时的链接对象和游标的区别)】
注意
with语句保证,若是__enter __()方法返回时没有错误,那么将始终调用__exit __()。 所以,若是在分配给目标列表期间发生错误,它将被视为与套件内发生的错误相同。 请参阅下面的第6步。
5.该套件已执行。【意思就是语句体中的过程执行完毕,执行完毕就到第六步--调用__exit__()来退出】
6.上下文管理器的__exit __()方法被调用。 若是异常致使套件退出,则其类型,值和回溯做为参数传递给__exit __()。 不然,将提供三个无参数。
关于退出返回值: If the suite was exited due to an exception, and the return value from the __exit__() method was false, the exception is reraised. If the return value was true, the exception is suppressed, and execution continues with the statement following the with statement. If the suite was exited for any reason other than an exception, the return value from __exit__() is ignored, and execution proceeds at the normal location for the kind of exit that was taken. 中文: 若是套件因为异常而退出,而且__exit __()方法的返回值为false,则会从新对异常进行从新评估。 若是返回值为true,则异常被抑制,并继续执行with语句后面的语句。 若是套件因为除了异常以外的任何缘由而退出,则__exit __()的返回值将被忽略,而且执行将在正常位置继续进行。
意思就是:
若是是异常退出,那么会返回false,(根据文档中的exit的描述“that methods should not reraise the passed-in exception; this is the caller’s responsibility.”,大概意思就是exit()不会处理异常,会从新抛出异常抛出给外面,由调用者处理,由于这是调用者的责任)__exit__()
若是返回 True,则忽略异常,再也不对异常进行处理【(在exit内部处理完异常后,可让”__exit__()”方法返回True,此时该异常就会不会再被抛出,with会认为它的执行体没有发生异常)】
(with会识别返回值,根据返回值来处理,若是是False,那么with会将执行体中的异常抛出,若是是True,那么with会认为没有发生异常(忽略异常),而继续执行外面的语句,但因为内部调用的了__exit__(),因此在异常以后的语句是不会运行的)
附上一个文档中提供的一个关于with中使用锁的例子:
几个测试:
1.执行体中发生异常:
import time class myContextDemo(object): def __init__(self,gen): self.gen = gen def __enter__(self): print("enter in ") return self.gen def __exit__(self, exc_type, exc_val, exc_tb): #exc_type是exception_type exc_val是exception_value exc_tb是exception_trackback print("exit in ") if exc_type is None:#若是是None 则继续执行 print("None:",exc_type, exc_val, exc_tb) else: #异常不为空时执行,这一步,若是with语句体中发生异常,那么也会执行 print("exception:", exc_type, exc_val, exc_tb) print("all done") if __name__=="__main__": gen=(i for i in range(5,10)) G=myContextDemo(gen) with G as f : print("hello") for i in f: print(i,end="\t") #测试1:执行体中发生异常 raise Exception("母鸡啊") print("main continue")
1.抛出异常后,后面main continue再也不执行
2.__exit__()中的else会执行
测试2:当else中强制返回为True时:
import time class myContextDemo(object): def __init__(self,gen): self.gen = gen def __enter__(self): print("enter in ") return self.gen def __exit__(self, exc_type, exc_val, exc_tb): #exc_type是exception_type exc_val是exception_value exc_tb是exception_trackback print("exit in ") if exc_type is None:#若是是None 则继续执行 print("None:",exc_type, exc_val, exc_tb) else: #异常不为空时执行,这一步,若是with语句体中发生异常,那么也会执行 print("exception:", exc_type, exc_val, exc_tb) print("all done") return True #这里若是返回true能够看到发生异常后,main continue能够执行 #即,若是exc_type是true,那么会继续执行,实际上,也能够在这里处理一下异常再返回true if __name__=="__main__": gen=(i for i in range(5,10)) G=myContextDemo(gen) with G as f : print("hello") for i in f: print(i,end="\t") raise Exception("母鸡啊") # print("continue")#这里不会执行 print("main continue")
1.返回True以后,with会忽略异常,继续执行,因此这里“main continue”能执行
2.即便忽略异常,在with体中异常以后的语句依旧不会执行
附:理论上能够在返回True以前处理一下异常
PS:若是你们想要了解得更详细,能够本身尝试去读一下官方文档。
附上关于with语句的详细介绍官方文档:https://www.python.org/dev/peps/pep-0343/