with的作用:

with关键字是一个替你管理实现上下文协议对象的东西,适用于对资源进行访问的场合,确保不管使用过程中是否发生异常都会执行必要的“清理”操作,释放资源,比如文件使用后自动关闭、线程中锁的自动获取和释放等。它将常用的 try ... except ... finally ... 模式很方便的被复用。看一个最经典的例子:

with open("test.txt",'r') as fp:
content = fp.read()

在这段代码中,无论 with 中的代码块在执行的过程中发生任何情况,文件最终都会被关闭。如果代码块在执行的过程中发生了一个异常,那么在这个异常被抛出前,程序会先将被打开的文件关闭。

with 的一般执行过程:

一段基本的 with 表达式,其结构是这样的:

with EXPR as VAR:
BLOCK

其中: EXPR 可以是任意表达式; as VAR 是可选的。其一般的执行过程是这样的:

  1. 计算 EXPR ,并获取一个上下文管理器。
  2. 上下文管理器的 __exit()__ 方法被保存起来用于之后的调用。
  3. 调用上下文管理器的 __enter()__ 方法。
  4. 如果 with 表达式包含 as VAR ,那么 EXPR 的返回值被赋值给 VAR 。
  5. 执行 BLOCK 中的表达式。
  6. 调用上下文管理器的 __exit()__ 方法。如果 BLOCK 的执行过程中发生了一个异常导致程序退出,那么异常的 type 、 value 和 traceback (即 sys.exc_info()的返回值 )将作为参数传递给 __exit()__ 方法。否则,将传递三个 None 。
mgr = (EXPR)
exit = type(mgr).__exit__ # 这里没有执行
value = type(mgr).__enter__(mgr)
exc = True
try:
try:
VAR = value # 如果有 as VAR
BLOCK
except:
exc = False
if not exit(mgr, *sys.exc_info()):
raise
finally:
if exc:
exit(mgr, None, None, None)

将这个过程用代码表示,就是上边这样的。

这个过程有几个细节:

如果上下文管理器中没有 __enter()__ 或者 __exit()__ 中的任意一个方法,那么解释器会抛出一个 AttributeError 。
在 BLOCK 中发生异常后,如果 __exit()__ 方法返回一个可被看成是 True 的值,那么这个异常就不会被抛出,后面的代码会继续执行。

现在介绍一下sys模块的sys.exc_info():

import sys

try:    

  block

except:    

  info=sys.exc_info()
  print(info)

或者以如下的形式:

import sys    

tp,val,td = sys.exc_info() 

sys.exc_info()的返回值是一个tuple, (type, value/message, traceback)

这里的type ---- 异常的类型

value/message ---- 异常的信息或者参数

traceback ---- 包含调用栈信息的对象,从这点上可以看出此方法涵盖了traceback.

import sys
try:
#text.txt not exist
fp = open('text.txt','rb')
data = fp.write()
except:
print(sys.exc_info())

以上代码返回的数据值为:(<class 'FileNotFoundError'>, FileNotFoundError(2, 'No such file or directory'), <traceback object at 0x00000000011B8B48>)

实现上下文管理器类:

第一种方法是实现一个类,其含有一个实例属性 db 和上下文管理器所需要的方法 __enter()__ 和 __exit()__ 。

class transaction(object):
def __init__(self, db):
self.db = db def __enter__(self):
self.db.begin() def __exit__(self, type, value, traceback):
if type is None:
self.db.commit()
else:
self.db.rollback()

使用生成器装饰器

在Python的标准库中,有一个装饰器可以通过生成器获取上下文管理器。使用生成器装饰器的实现过程如下:

from contextlib import contextmanager
@contextmanager
def transaction(db):
db.begin()
try:
yield db
except:
db.rollback()
raise
else:
db.commit()

第一眼上看去,这种实现方式更为简单,但是其机制更为复杂。看一下其执行过程吧:

  1. Python解释器识别到 yield 关键字后, def 会创建一个生成器函数替代常规的函数。
  2. 装饰器 contextmanager 被调用并返回一个帮助方法,这个帮助函数在被调用后会生成一个 GeneratorContextManager 实例。最终 with 表达式中的 EXPR 调用的是由 contentmanager 装饰器返回的帮助函数。
  3. with 表达式调用 transaction(db) ,实际上是调用帮助函数。帮助函数调用生成器函数,生成器函数创建一个生成器。
  4. 帮助函数将这个生成器传递给 GeneratorContextManager ,并创建一个 GeneratorContextManager 的实例对象作为上下文管理器。
  5. with 表达式调用实例对象的上下文管理器的 __enter()__ 方法。
  6. __enter()__ 方法中会调用这个生成器的 next() 方法。这时候,生成器方法会执行到 yield db 处停止,并将 db 作为 next() 的返回值。如果有 as VAR ,那么它将会被赋值给 VAR 。
  7. with 中的 BLOCK 被执行。
  8. BLOCK 执行结束后,调用上下文管理器的 __exit()__ 方法。 __exit()__ 方法会再次调用生成器的 next() 方法。如果发生 StopIteration 异常,则 pass 。
  9. 如果没有发生异常生成器方法将会执行 db.commit() ,否则会执行 db.rollback() 。

再次看看上述过程的代码大致实现:

def contextmanager(func):
def helper(*args, **kwargs):
return GeneratorContextManager(func(*args, **kwargs))
return helper class GeneratorContextManager(object):
def __init__(self, gen):
self.gen = gen def __enter__(self):
try:
return self.gen.next()
except StopIteration:
raise RuntimeError("generator didn't yield") def __exit__(self, type, value, traceback):
if type is None:
try:
self.gen.next()
except StopIteration:
pass
else:
raise RuntimeError("generator didn't stop")
else:
try:
self.gen.throw(type, value, traceback)
raise RuntimeError("generator didn't stop after throw()")
except StopIteration:
return True
except:
if sys.exc_info()[1] is not value:
raise

锁机制:

@contextmanager
def locked(lock):
lock.acquired()
try:
yield
finally:
lock.release()
#标准输出重定向
@contextmanager
def stdout_redirect(new_stdout):
old_stdout = sys.stdout
sys.stdout = new_stdout
try:
yield
finally:
sys.stdout = old_stdout with open("file.txt", "w") as f:
with stdout_redirect(f):
print "hello world"

出自:http://www.jb51.net/article/92387.htm

https://www.ibm.com/developerworks/cn/opensource/os-cn-pythonwith/

http://preshing.com/20110920/the-python-with-statement-by-example/

Python自学笔记-with详解的更多相关文章

  1. Scrapy笔记04- Selector详解

    Scrapy笔记04- Selector详解 在你爬取网页的时候,最普遍的事情就是在页面源码中提取需要的数据,我们有几个库可以帮你完成这个任务: BeautifulSoup是python中一个非常流行 ...

  2. Scrapy笔记05- Item详解

    Scrapy笔记05- Item详解 Item是保存结构数据的地方,Scrapy可以将解析结果以字典形式返回,但是Python中字典缺少结构,在大型爬虫系统中很不方便. Item提供了类字典的API, ...

  3. python之OS模块详解

    python之OS模块详解 ^_^,步入第二个模块世界----->OS 常见函数列表 os.sep:取代操作系统特定的路径分隔符 os.name:指示你正在使用的工作平台.比如对于Windows ...

  4. python之sys模块详解

    python之sys模块详解 sys模块功能多,我们这里介绍一些比较实用的功能,相信你会喜欢的,和我一起走进python的模块吧! sys模块的常见函数列表 sys.argv: 实现从程序外部向程序传 ...

  5. python中threading模块详解(一)

    python中threading模块详解(一) 来源 http://blog.chinaunix.net/uid-27571599-id-3484048.html threading提供了一个比thr ...

  6. python自学笔记

    python自学笔记 python自学笔记 1.输出 2.输入 3.零碎 4.数据结构 4.1 list 类比于java中的数组 4.2 tuple 元祖 5.条件判断和循环 5.1 条件判断 5.2 ...

  7. Python数据类型及其方法详解

    Python数据类型及其方法详解 我们在学习编程语言的时候,都会遇到数据类型,这种看着很基础也不显眼的东西,却是很重要,本文介绍了python的数据类型,并就每种数据类型的方法作出了详细的描述,可供知 ...

  8. python引用和对象详解

    python引用和对象详解 @[马克飞象] python中变量名和对象是分离的 例子 1: a = 1 这是一个简单的赋值语句,整数 1 为一个对象,a 是一个引用,利用赋值语句,引用a指向了对象1. ...

  9. Python中time模块详解

    Python中time模块详解 在平常的代码中,我们常常需要与时间打交道.在Python中,与时间处理有关的模块就包括:time,datetime以及calendar.这篇文章,主要讲解time模块. ...

随机推荐

  1. PHP初入,for循环使用

    一: 找出100-999之间的所有"水仙花数".所谓水仙花数是指一个三位 数,各位数字的立方和等于该数本身.(如153次方=1的3次方+5的3次方+3的3次方)并输出这些数字 想想 ...

  2. PS各个工具的字母快捷键和英…

    原文地址:PS各个工具的字母快捷键和英文全名作者:Tycho     选框-Marquee(M)     移动-move(V)     套索-Lasso(L)     魔棒-Wand(W) 喷枪-in ...

  3. 201521123044 《Java程序设计》第6周学习总结

    1. 本章学习总结 1.1 面向对象学习暂告一段落,请使用思维导图,以封装.继承.多态为核心概念画一张思维导图,对面向对象思想进行一个总结. 注1:关键词与内容不求多,但概念之间的联系要清晰,内容覆盖 ...

  4. 201521123001《Java程序设计》第6周学习总结

    1. 本周学习总结 1.1 面向对象学习暂告一段落,请使用思维导图,以封装.继承.多态为核心概念画一张思维导图,对面向对象思想进行一个总结. 注1:关键词与内容不求多,但概念之间的联系要清晰,内容覆盖 ...

  5. request.getParameter()获取URL中文参数乱码的解决办法

    这个问题耽误好长时间,URL传中文参数出现乱码,就算首次使用request接收就添加 request.setCharacterEncoding("UTf-8"); 依然报错不误. ...

  6. Linux入门_2-基础命令

    Linux入门-基础命令 目录 日期命令date 修改时区 日历命令cal 关机启动命令halt,reboot,poweroff whoami.who.who am i.w screen ...

  7. 函数原型属性-JavaScript深入浅出(三)

    前两次总结了JavaScript中的基本数据类型(值类型<引用类型>,引用类型<复杂值>)以及他们在内存中的存储,对内存空间有了一个简单的了解,以及第二次总结了this深入浅出 ...

  8. MyBatis学习(一)简介及入门案例

    1.什么是MyBatis? MyBatis是一个支持普通SQL查询,存储过程,和高级映射的优秀持久层框架.MyBatis去掉了几乎所有的JDBC代码和参数的手工设置以及对结果集的检索封装.MyBati ...

  9. APUE 3 -- 信号 (signal)<II>: 可靠信号

    一个事件可以事一个信号发送给一个进程,这个事件可以是硬件异常,可以是软件条件触发,可以是终端产生信号,也可以是一个kill函数调用.当信号产生后,内核通常会在进程表中设置某种形式的标志(flag).我 ...

  10. arm-linux-gcc 4.3.2编译uboot 1.1.6

    在第三期项目的视频中,官方提供了一整套新的工具链,bootloader, 内核和文件系统(arm-linux-gcc_4.3.2, uboot-2012.04.01, linux-3.4.2)其中ub ...