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. 10个经典的Java面试题集合(转载)

    1.Java的HashMap是如何工作的? HashMap是一个针对数据结构的键值,每个键都会有相应的值,关键是识别这样的值. HashMap 基于 hashing 原理,我们通过 put ()和 g ...

  2. 为什么说汽车VIN码是汽车唯一的"身份证"

    人有身份证,那么车有吗?当然了,汽车也是有"身份证"的,出厂时就会跟着车辆一起出生, 一般在车辆的挡风玻璃处.按照国际SAE国际规定,Vin码由17位字符组成,其中包含了车辆的生产 ...

  3. C++ Primmer 学习笔记

    一.开始 (一)输入输出 1.endl的作用 endl操纵符用于结束当前行,将与设备关联的缓冲区内容刷新到设备中.如果没有这个字符,一旦程序突然崩溃,就可能导致输出还停留在缓冲区里,而不显示到设备. ...

  4. SNS团队Beta阶段第六次站立会议(2017.5.27)

    1.立会照片 2.每个人的工作 成员 今天已完成的工作 明天计划完成的工作 罗于婕 发音图标的改进 对界面各部分的图标进行完善.美化 龚晓婷 对于历史记录功能的测试 对于历史记录功能进一步完善 林仕庄 ...

  5. 201521123118《java程序与设计》第6周学习总结

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

  6. Hyperledger Fabric 1.0 从零开始(二)——环境构建(公网)

    1:环境构建 在本文中用到的宿主机环境是Centos ,版本为Centos.x86_647.2,通过Docker 容器来运行Fabric的节点,版本为v1.0.因此,启动Fabric网络中的节点需要先 ...

  7. windows 2008 R2操作系统上使用iis服务运行php和mysql数据库的网站遇到的验证码不显示问题?

    1,本地运行正常,路径没问题. 2,GD2.dll开启 3,IIS服务正常开启,并启用父路径为True 4,检查是否IIS服务中FSO权限问题 开始 -> 程序 -> 管理工具 -> ...

  8. Struts2第四篇【请求数据自动封装、Action得到域对象】

    前言 前三篇的Struts博文基本把Struts的配置信息讲解完了-..本博文主要讲解Struts对数据的处理 一般地,我们使用Servlet的时候都是分为几个步骤的: 得到web层的数据.封装数据 ...

  9. 鸟哥Linux学习笔记04

    1, . 当前目录  ..上层目录 2, 目录的相关操作 1)cd(切换目录) [root@node1 ~]# cd [相对路径或绝对路径] [root@node1 ~]# cd      : 回到自 ...

  10. 排查Linux机器是否已经被入侵

    随着开源产品的越来越盛行,作为一个Linux运维工程师,能够清晰地鉴别异常机器是否已经被入侵了显得至关重要,个人结合自己的工作经历,整理了几种常见的机器被黑情况供参考 背景信息:以下情况是在CentO ...