目录

  一、with语句

  二、上下文管理器

  三、contextlib模块

基本概念

上下文管理协议(Context Management Protocol)

  包含方法 __enter__() 和 __exit__(),支持该协议的对象要实现这两个方法。

上下文管理器(Context Manager)

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

运行时上下文(runtime context)

  由上下文管理器创建,通过上下文管理器的 __enter__() 和__exit__() 方法实现,__enter__() 方法在语句体执行之前进入运行时上下文,__exit__() 在语句体执行完后从运行时上下文退出。with 语句支持运行时上下文这一概念。

上下文表达式(Context Expression)

  with 语句中跟在关键字 with 之后的表达式,该表达式要返回一个上下文管理器对象。

语句体(with-body)

  with 语句包裹起来的代码块,在执行语句体前会调用上下文管理器的 __enter__() 方法,执行完语句体之后会执行 __exit__() 方法。

一、with语句

  关于 Python 中 with 语句的详细说明:PEP 343

  with 语句用上下文管理器定义的方法包裹一段代码的执行,等价于简单版的try...except...finally语句。with语句的主要针对的情境:不论一个代码块的执行过程是否出现异常,都要在结束的时候执行一些操作(比如清理)。

  with语句语法:

with_stmt ::=  "with" with_item ("," with_item)* ":" suite
with_item ::=  expression ["as" target]

  或:

with expression [as variable]:with-block

  expression应该返回一个支持“上下文管理协议”的对象,如果 as 分句存在的话,这个对象的返回值会被赋给 as 后面的变量名。

第一种语法表示中with语句的执行流程:

  1. 计算上下文表达式 (with_item 中给出的表达式) ,获得一个上下文管理器,该上下文管理器包括__enter__() 和 __exit__()方法;
  2. 加载该上下文管理器的 __exit__() 方法留待以后使用;
  3. 调用上下文管理器的 __enter__() 方法;
  4. 如果 with 语句中的 with_item 指定了目标别名(as 分句后面的变量名),__enter__() 的返回值被赋给这个目标别名;

  *注

  with 语句确保只要 __enter__() 正常返回,那么__exit__()一定会被调用。因此如果在将值赋给目标别名时发生错误,该错误将被当做发生在suite中。可以看下面的第6步。

  5. with语句中嵌套的 suite 部分被执行(第二种表示中的 with-block 部分);

  6. 上下文管理器的 __exit__() 方法被调用,如果是异常造成 suite(with-block) 部分退出,异常的类型、值和回溯都被当做参数传给 __exit__(type, value, traceback) 方法,这三个值和sys.exc_info的返回值相同。如果suite(with-block) 部分没有抛出异常,__exit__()的三个参数都是None。

  如果 suite 部分是由于异常导致的退出,且__exit__()方法的返回值是false,异常将被重举;如果返回值是真,异常将被终止,with 语句后的代码继续执行。

  如果 suite 部分是由于不是异常的其他原因导致的退出,__exit__()方法的返回值被忽视,执行在退出发生的地方继续

  单个上下文管理器示例:

with open(r'C:\misc\data') as myfile:for line in myfile:print(line)#...more code...

  Python中的文件对象包含会在with代码段后自动关闭文件对象的上下文管理器,所以即便 for 循环中抛出处理文件对象时的异常,也能保证 myfile 引用的对象被正常关闭。

  如果这段代码使用 try...finally...语句改写:

myfile = open(r'C:\misc\data')
try:for line in myfile:print(line)#...more code...
finally:myfile.close()

  可见使用 with 语句能够简化代码。

  多个上下文管理器被处理的方式就像多个 with 语句嵌套执行一样:

with A() as a, B() as b:suite

  等价于:

with A() as a:with B() as b:suite

  多个上下文管理器示例:

with open('data') as fin, open(''res', 'w') as fout:for line in fin:if 'some key' in line:fout.write(line)

  

二、上下文管理器类型
  context manager 是Python中 with 语句执行时用来定义运行时上下文的对象,上下文管理器控制着 进 / 出 运行时上下文的功能,上下文管理器通常由 with 语句触发,也可以直接通过调用他们的方法来使用他们。上下文管理器的通常用于保存和恢复各式各样的全局状态、加解锁资源和关闭打开的文件等等。  

  Python的 with 语句支持由上下文管理器定义的运行时上下文,由两个方法来实现,这两个方法允许用户在自定义的类中定义运行时上下文,执行流程在 with 语句开始前进入上下文,当 with 语句结束后退出。

上下文管理器必须提供一对方法:

  

contextmanager.__enter__()

  进入运行时上下文,要么返回该对象,要么返回一个与运行时上下文相关的对象,如果有 as 分句的话,with 语句将会把该方法的返回值和 as 分句指定的目标(别名)进行绑定。

  比如文件对象在__enter__()里返回自己,这样 open() 函数可以被当做环境表达式在一个 with 语句中使用。

另一种情形中,decimal.localcontext()返回一个相关的对象。上下文管理器将活跃的小数上下文设置为初始小数上下文的拷贝,然后返回拷贝后的引用。这样可以在 with 语句中修改当前的小数上下文而不影响with 语句外的代码。  contextmanager.__exit__(exc_type, exc_val, exc_tb) 退出运行时上下文,参数是 with 代码块中抛出的异常的详细信息,返回一个Bool型的标识符,该标识符说明上下文管理器能否处理异常:如果上下文管理器可以处理这个异常,__exit__() 应当返回一个true值来指示不需要传播这个异常;如果返回false,就会导致__exit__() 返回后重新抛出这个异常,如果上下文不是因为异常而退出,则三个参数都是 None。  该方法如果返回True ,说明上下文管理器可以处理异常,使得 with 语句终止异常传播,然后直接执行 with 语句后的语句;否则异常会在该方法调用结束后正常传播,该方法执行时发生的异常会替代所有其他在执行 with 代码块时出现的异常。在__exit__()中不需要显式重举传入的异常,只需要返回 false 表明 __exit__() 正常执行且传入的异常需要重举即可,重举传入__exit__()方法的异常是调用__exit__()的函数的工作。这种机制便于上下文管理器检测 __exit__() 方法是否真的执行失败。Python定义了若干上下文管理器,Python的 generator 类型和装饰器 contextlib.contextmanager 提供了实现上下文管理器协议的手段,如果一个生成器函数被装饰器 contextlib.contextmanager 所装饰,它将会返回一个实现了必要的__enter__() 和__exit__() 方法的上下文管理器,而不是普通的迭代器!

  Note that there is no specific slot for any of these methods in the type structure for Python objects in the Python/C API. Extension types wanting to define these methods must provide them as a normal Python accessible method. Compared to the overhead of setting up the runtime context, the overhead of a single class dictionary lookup is negligible.

   自定义上下文管理器示例:

class TraceBlock(object):def message(self, arg):print('running' + arg)def __enter__(self):print('starting with block')return selfdef __exit__(self, exc_type, exc_value, exc_tb):if exc_type is None:print('exited normally\n')else:print('raise an exception! ' + str(exc_type))return False #Propagateif __name__ == '__main__':with TraceBlock() as action:action.message('test1')print('reached')with TraceBlock() as action:action.message('test2')raise TypeErrorprint ('not reached')

  

三、contextlib模块

  采用“二、上下文管理器类型”中说明的传统方法即编写一个包含__enter__()和__exit__()方法的类来创建上下文管理器并不难。不过有些时候,对于很少的上下文来说,完全编写所有代码会是额外的负担。在这些情况下,可以使用装饰器 contextlib.contextmanager() 将一个生成器函数转换为上下文管理器。

  contextlib模块提供对 with 语句的支持:

contextlib.contextmanager(func)

  装饰器,用来装饰一个生成器函数,使其成为一个上下文管理器。通过该装饰器可以方便地定义上下文管理器,不必创建一个类或单独指定__enter__() 和__exit__() 方法。

  示例

  (该例子不应该用于实际生成HTML!):

from contextlib import contextmanager@contextmanager
def tag(name):print "<%s>" % nameyieldprint "</%s>" % name>>> with tag("h1"):
...    print "foo"
...
<h1>
foo
</h1>

  上下文管理器的 __enter__() 和 __exit__() 方法由 @contextmanager 负责提供,不再是之前的迭代子。被装饰的生成器函数只能产生一个值,否则会导致异常 RuntimeError;如果使用了 as 子句的话,产生的值会赋值给 as 子句中的 target。

  生成器函数中 yield 之前的语句在 __enter__() 方法中执行;yield 之后的语句在 __exit__() 中执行;yield 产生的值赋给 as 子句中的 variable 变量。

  生成器 yield 的地方,切换到执行 with 语句中的代码块(with-block),生成器在代码块退出后继续执行。

  如果 with 代码块中有未被处理的异常,它会被重举到生成器函数中的 yield 处,因此可以在生成器函数中使用 try...except...finally 语句来处理被重举的异常

  如果捕获异常的目标是记录日志或执行一些操作(而不是抑制它),生成器必须重举异常。否则生成器上下文管理器将会告知 with 语句说异常已经被处理了,但是一旦执行到 with 语句后的代码,异常会立即继续。

  需要注意的是,@contextmanager 只是省略了 __enter__() / __exit__() 的编写,实际意义是减少了代码量。

  但装饰器@contextmanager并不负责资源的“获取”和“清理”工作(需要开发者自己实现)。“获取”操作需要定义在 yield 语句之前,“清理”操作需要定义 yield 语句之后,这样 with 语句在执行 __enter__() / __exit__() 方法时会执行这些语句以获取/释放资源,即生成器函数中需要实现必要的逻辑控制,包括资源访问出现错误时抛出适当的异常。

  示例:

from contextlib import contextmanager@contextmanager
def make_context() :print 'enter'try :yield {}except RuntimeError, err :print 'error' , errfinally :print 'exit'with make_context() as value :print value

  with-block中抛出的异常将被重举到生成器的 yield 处,在生成器中可以捕获该异常。

  

contextlib.closing(thing)

  返回一个上下文管理器,在完成代码块的执行时关闭参数 thing。等价于:

from contextlib import contextmanager@contextmanager
def closing(thing):try:yield thingfinally:thing.close()

  可以这么使用:

from contextlib import closing
import urllibwith closing(urllib.urlopen('http://www.python.org')) as page:for line in page:print line

  该例中,不用显式地关闭 page。即使发生错误,也会在 with 代码块退出时执行 page.close()

转载于:https://www.cnblogs.com/Security-Darren/p/4196634.html

Python——with语句、context manager类型和contextlib库相关推荐

  1. Python数据类型、Numpy数据类型和Pytorch中的tensor类型间的相互转化

    数据类型包括Python数据类型.Numpy数据类型和Pytorch中的tensor,Pytorch中的tensor又包括CPU上的数据类型和GPU上的数据类型. 一.Python数据类型 Pytho ...

  2. python with语句与contextlib

    with语句用于异常处理,适用于存在资源访问的场合,无论在资源访问的过程中是否发生异常,都会执行必要的清理操作,释放资源,比如文件打开后自动关闭.线程中锁的自动获取和释放 要明白with语句是怎样使用 ...

  3. C++ Primer 第五版 第6章 6.3——函数返回类型和return语句阅读笔记

    习题答案请参考:C++ Primer 第五版 第6章 6.3--函数返回类型和return语句习题答案 目录 6.3 返回类型和return语句 6.3.1 无返回值函数 无返回值函数的特性 6.3. ...

  4. C++ Primer 第五版 第6章 6.3——函数返回类型和return语句习题答案

    理论讲解请参考:C++ Primer 第五版 第6章 6.3--函数返回类型和return语句阅读笔记 目录 6.31 6.32 6.33 6.34 6.35 6.36 6.38 6.31 返回引用无 ...

  5. python知识:json格式文本;异常处理;字符串处理;unicode类型和str类型转换

    python知识:json格式文本:异常处理:字符串处理:unicode类型和str类型转换 参考文章: (1)python知识:json格式文本:异常处理:字符串处理:unicode类型和str类型 ...

  6. Python基础必掌握的5种数据类型和8种内置函数基本操作

    今天跟着我一起学习/复习一下Python语法的5种基本数据类型和8种数据基本操作.如果你已经掌握了这块内容,也建议跟着文中代码进行操作加深印象巩固学习成果. 整套学习自学教程中应用的数据都是<三 ...

  7. Python中编码问题:u'\xe6\x97\xa0\xe5\x90\x8d' 类型和 ‘\u559c\u6b22\u4e00\u4e2a\u4eba ’ 转为utf-8的解决办法...

    Python中编码问题:u'\xe6\x97\xa0\xe5\x90\x8d' 类型和 '\u559c\u6b22\u4e00\u4e2a\u4eba ' 转为utf-8的解决办法 相信小伙伴们遇到过 ...

  8. Python中编码问题:u‘\xe6\x97\xa0\xe5\x90\x8d‘ 类型和 ‘\u559c\u6b22\u4e00\u4e2a\u4eba ’ 转为utf-8的解决办法

    Python中编码问题:u'\xe6\x97\xa0\xe5\x90\x8d' 类型和 '\u559c\u6b22\u4e00\u4e2a\u4eba ' 转为utf-8的解决办法 参考文章: (1) ...

  9. 返回类型和return语句

    目录 1. 返回类型和return语句 1.1 无返回值函数 1.2 有返回值函数 1.2.0 不要返回局部对象的引用或指针 1.2.1 引用返回左值 1.2.2 用列表来初始化返回值 1.2.3 主 ...

最新文章

  1. 09_Java面向对象_第9天(类、封装)_讲义
  2. UniDAC使用教程(四):数据类型映射
  3. 解决SSH连接Linux超时自动断开
  4. springmvc二十五:springmvc支持ajax
  5. 百度地图API —— Hello World!
  6. 机器学习-损失函数 (转)
  7. 扎心!“就业难”背后的“十宗罪”我竟占了一半
  8. oracle severity,ORACLE10G如何清除OEM下的历史警告信息
  9. c语言程序设计 猜数字,C语言程序设计(猜数字游戏)报告.doc
  10. Python学习笔记:模块与包
  11. 《转》Android 今日头条屏幕适配方案终极版正式发布!
  12. 普通卷积、转置卷积、空洞卷积(dilated convolution、Atrous Convolution)
  13. Kinect for Windows sdk下载 以及安装时错误的解决方案
  14. uniapp小程序webSocket封装、断线重连、心跳检测
  15. Python运行报错most recent call last
  16. 什么是依赖注入(DI)
  17. Android中文件的读写---assets和raw下的资源文件
  18. 锐捷客户端-您不在许可范围中,请确认您的权限
  19. speedoffice(word)如何修改行间距
  20. mysql创建应用账号

热门文章

  1. 算法72----最大正方形
  2. 【TP3.2】模板 select选项采坑
  3. Linux下wine用法
  4. Yii 2 —— Model表前缀
  5. ionic react-native和native开发移动app到底那个好
  6. memcpy()函数
  7. while/do while /for 循环中continue
  8. VMware Vsphere4 资源分配与资源限制
  9. 【CocoaPods】CocoaPods:Objective-C依赖库管理(XCode 4.6)
  10. Reading-又更新了一些内容【Kotlin+MVP+Retrofit】