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. Java filter拦截器的使用

    1.web.xml配置 <!-- 验证是否登录 拦截功能 --> <filter> <filter-name>isLogin</filter-name> ...

  2. WCF(一)基础整理

    学习WCF之前,了解下WCF和WebService的区别. WCF和WebService区别 Web Service严格来说是行业标准,也就是Web Service 规范,它使用XML扩展标记语言来表 ...

  3. JS跨域请求 JSONP B/S全代码

    Ajax直接请求普通文件存在跨域无权限访问的问题,甭管你是静态页面.动态网页.web服务.WCF,只要是跨域请求,一律不准:Web页面上调用js文件时则不受是否跨域的影响(不仅如此,我们还发现凡是拥有 ...

  4. 结对编程1.四则运算GUI版

    201421123022 王若凡        201421123026  欧阳勇 coding详细代码 a.需求分析: 这个程序做成GUI(可以是Windows PC 上的,也可以是Mac.Linu ...

  5. 团队作业4——第一次项目冲刺(Alpha版本)4.25

    团队作业4--第一次项目冲刺(Alpha版本) Day four: 会议照片 每日站立会议: 项目进展 今天是项目的Alpha敏捷冲刺的第四天,先大概整理下昨天已完成的任务以及今天计划完成的任务.今天 ...

  6. 201521123067 《Java程序设计》第5周学习总结

    201521123067 <Java程序设计>第5周学习总结 1. 本周学习总结 1.1 尝试使用思维导图总结有关多态与接口的知识点. 1.2 可选:使用常规方法总结其他上课内容. ●在本 ...

  7. 201521123117 《Java程序设计》第2周学习总结

    本周学习总结: 1.String常量,创建之后不能再进行修改 2.类管理机制是包. 3.Java数组的使用. 书面作业: Q1:使用Eclipse关联jdk源代码,并查看String对象的源代码(截图 ...

  8. 201521123117 《Java程序设计》第13周学习总结

    1. 本周学习总结 2. 书面作业 1. 网络基础 1.1 比较ping www.baidu.com与ping cec.jmu.edu.cn,分析返回结果有何不同?为什么会有这样的不同? 分析结果:从 ...

  9. 控制结构(1) 分枝/叶子(branch/leaf)

    // 下一篇:卫语句(guard clause) 典型代码: function doSomething1(){ // ... } function doSomething2(){ // ... } f ...

  10. 多线程:head first Thread.join()

    不使用Thread.join() 测试线程 先上代码: /** * Created by Zero on 2017/8/23. */ public class TestJoin implements ...