天天看点

Python 中 with用法及原理

前言

with 语句适用于对资源进行访问的场合,确保不管使用过程中是否发生异常都会执行必要的“清理”操作,释放资源,比如文件使用后自动关闭/线程中锁的自动获取和释放等。

问题引出

如下代码:

上面代码存在2个问题:

(1)文件读取发生异常,但没有进行任何处理;

(2)可能忘记关闭文件句柄;

改进

虽然这段代码运行良好,但比较冗长。

而使用with的话,能够减少冗长,还能自动处理上下文环境产生的异常。如下面代码:

with 工作原理

(1)紧跟with后面的语句被求值后,返回对象的 –enter–() 方法被调用,这个方法的返回值将被赋值给as后面的变量;

(2)当with后面的代码块全部被执行完之后,将调用前面返回对象的 –exit–() 方法。

with工作原理代码示例:

运行结果:

整个运行过程如下:

(1)enter()方法被执行;

(2)enter()方法的返回值,在这个例子中是”Foo”,赋值给变量sample;

(3)执行代码块,打印sample变量的值为”Foo”;

(4)exit()方法被调用;

【注】exit()方法中有3个参数, exc_type, exc_val, exc_tb,这些参数在异常处理中相当有用。

exc_type: 错误的类型

exc_val: 错误类型对应的值

exc_tb: 代码中错误发生的位置

示例代码:

输出结果:

总结

实际上,在with后面的代码块抛出异常时,exit()方法被执行。开发库时,清理资源,关闭文件等操作,都可以放在exit()方法中。

总之,with-as表达式极大的简化了每次写finally的工作,这对代码的优雅性是有极大帮助的。

如果有多项,可以这样写:

​<code>​with​</code>​语句的原理

上下文管理协议(Context Management Protocol):包含方法  __enter__() 和 __exit__() ,支持该协议的对象要实现这两个方法。

上下文管理器(Context Manager):支持上下文管理协议的对象,这种对象实现了 __enter__() 和 __exit__() 方法。上下文管理器定义执行​<code>​with​</code>​语句时要建立的运行时上下文,负责执行​<code>​with​</code>​语句块上下文中的进入与退出操作。通常使用​<code>​with​</code>​语句调用上下文管理器,也可以通过直接调用其方法来使用。

​<code>​with​</code>​语句的常用表达式:

其一般的执行过程是这样的:

1、执行EXPR,生成上下文管理器context_manager;

2、获取上下文管理器的 __exit()__ 方法,并保存起来用于之后的调用;

3、调用上下文管理器的 __enter__() 方法;如果使用了​<code>​as​</code>​子句,则将 __enter__() 方法的返回值赋值给​<code>​as​</code>​子句中的VAR;

4、执行BLOCK中的表达式;

5、不管是否执行过程中是否发生了异常,执行上下文管理器的 __exit__() 方法, __exit__() 方法负责执行“清理”工作,如释放资源等。如果执行过程中没有出现异常,或者语句体中执行了语句​<code>​break/continue/return​</code>​,则以​<code>​None​</code>​作为参数调用​<code>​__exit__(None, None, None)​</code>​;如果执行过程中出现异常,则使用​<code>​sys.exc_info​</code>​得到的异常信息为参数调用​<code>​__exit__(exc_type, exc_value, exc_traceback)​</code>​;

6、出现异常时,如果​<code>​__exit__(type, value, traceback)​</code>​返回False,则会重新抛出异常,让​<code>​with​</code>​之外的语句逻辑来处理异常,这也是通用做法;如果返回True,则忽略异常,不再对异常进行处理。

自定义上下文管理器

​<code>​ Python​</code>​的​<code>​with​</code>​语句是提供一个有效的机制,让代码更简练,同时在异常产生时,清理工作更简单。

示例1:

【注】with后面必须跟一个上下文管理器,如果使用了as,则是把上下文管理器的 __enter__() 方法的返回值赋值给 target,target 可以是单个变量,或者由“()”括起来的元组(不能是仅仅由“,”分隔的变量列表,必须加“()”)

代码运行结果如下:

结果分析:当我们使用with的时候,__enter__方法被调用,并且将返回值赋值给as后面的变量,并且在退出with的时候自动执行__exit__方法。

示例2:

去期待陌生,去拥抱惊喜。