首发时间:2018-02-23 15:28


之前看到一篇博客说博主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]

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.

The context manager’s __exit__() is loaded for later use.

The context manager’s __enter__() method is invoked.

If a target was included in the with statement, the return value from __enter__() is assigned to it.

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 __exit__() methods should not reraise the passed-in exception; this is the caller’s responsibility.”,大概意思就是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/


python之with语句的原理的更多相关文章

  1. 理解python的with语句

    Python’s with statement provides a very convenient way of dealing with the situation where you have ...

  2. Python之with语句

    Python之with语句 在Python中,我们在打开文件的时候,为了代码的健壮性,通常要考虑一些异常情况,比如: try: ccfile = open('/path/data') content ...

  3. (Python )控制流语句if、for、while

    这一节,我们将学习Python的控制流语句,主要包括if.for.while.break.continue 和pass语句 1. If语句 if语句也许是我们最熟悉的语句.其使用方法如下: x=inp ...

  4. Python的with语句

    写过多线程程序的人肯定对各种锁很熟悉,尤其是下面这种代码 def lock_usage: lock.Lock() if(...) : lock.Unlock() return lock.Unlock( ...

  5. Python学习教程(learning Python)--3.3.4 Python的if-elif-else语句

    Python的if-elif-else语句用于多种条件判断后选择某个语句块执行.该语句可以利用一系列条件表达式进行检查,并在某个表达式为真的情况下执行相应的代码.需要注意的是,虽然if/elif/el ...

  6. 转: 理解Python的With语句

    Python’s with statement provides a very convenient way of dealing with the situation where you have ...

  7. python的with语句,超级强大

    With语句是什么? 有一些任务,可能事先需要设置,事后做清理工作.对于这种场景,Python的with语句提供了一种非常方便的处理方式.一个很好的例子是文件处理,你需要获取一个文件句柄,从文件中读取 ...

  8. 简单探讨python中的语句和语法

    python程序结构 python"一切皆对象",这是接触python听到最多的总结了.在python中最基层的单位应该就是对象了,对象需要靠表达式建立处理,而表达式往往存在于语句 ...

  9. python基础——继承实现的原理

    python基础--继承实现的原理 1 继承顺序 class A(object): def test(self): print('from A') class B(A): def test(self) ...

随机推荐

  1. JAVA发送http get/post请求,调用http接口、方法

    import java.io.BufferedReader; import java.io.IOException;import java.io.InputStream; import java.io ...

  2. 关于vue-router,路由重定向的使用分析

    看之前的项目,突然发现一个不算bug的bug,之前也是一直没有想到,现在发现之后越来越觉得有必要改掉, 项目用的是vue做的,自然切换用的就是路由,一级路由包括:首页.记录和个人中心,二级路由是在记录 ...

  3. OPC安装-配置(http://www.mabotech.com)

    1.使用opc,需要在机器上安装OPC运行环境.opc运行环境包含:opc_aeps.dll.opccomn_ps.dll.opcdaauto.dll.OpcEnum.exe.opcproxy.dll ...

  4. C#开发必会

    1.字符串的各种属性和方法: 2.数组的各种熟悉和方法: 3.public.private.protect.internal.protect internal 4.构造函数 5.属性 6.ADO.NE ...

  5. 从零开始学 Web 之 Ajax(四)接口文档,验证用户名唯一性案例

    大家好,这里是「 从零开始学 Web 系列教程 」,并在下列地址同步更新...... github:https://github.com/Daotin/Web 微信公众号:Web前端之巅 博客园:ht ...

  6. Python 3 进阶 —— 使用 PyMySQL 操作 MySQL

    PyMySQL 是一个纯 Python 实现的 MySQL 客户端操作库,支持事务.存储过程.批量执行等. PyMySQL 遵循 Python 数据库 API v2.0 规范,并包含了 pure-Py ...

  7. Python入门到精通学习书籍推荐!

    1.Python基础教程(第2版 修订版)<Python基础教程(第2版修订版)>包括Python程序设计的方方面面,内容涉及的范围较广,既能为初学者夯实基础,又能帮助程序员提升技能,适合 ...

  8. Spring Boot 1.5.* 升级 2.1 - 完善中

    Spring Boot 原版本 1.5.12.RELEASE 新版本 2.1.0.RELEASE Spring Cloud 原版本 Edgware.SR3 新版本 Finchley.SR2 一.Act ...

  9. 使用js从element的matrix推导transform的scale、rotate 和 translate参数

    transform 网上很多都只介绍了还原角度和缩放的参数,但是没有就偏移量的计算,自己还原了一下公式的意义,进行了公式的反推,具体的推到过程就不详叙了,可以参看w3c的矩阵含义. 直接上干货. fu ...

  10. [js高手之路]深入浅出webpack教程系列2-配置文件webpack.config.js详解(上)

    [js高手之路]深入浅出webpack教程系列索引目录: [js高手之路]深入浅出webpack教程系列1-安装与基本打包用法和命令参数 [js高手之路]深入浅出webpack教程系列2-配置文件we ...