Python自学笔记-with详解
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 是可选的。其一般的执行过程是这样的:
- 计算 EXPR ,并获取一个上下文管理器。
- 上下文管理器的 __exit()__ 方法被保存起来用于之后的调用。
- 调用上下文管理器的 __enter()__ 方法。
- 如果 with 表达式包含 as VAR ,那么 EXPR 的返回值被赋值给 VAR 。
- 执行 BLOCK 中的表达式。
- 调用上下文管理器的 __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()
第一眼上看去,这种实现方式更为简单,但是其机制更为复杂。看一下其执行过程吧:
- Python解释器识别到 yield 关键字后, def 会创建一个生成器函数替代常规的函数。
- 装饰器 contextmanager 被调用并返回一个帮助方法,这个帮助函数在被调用后会生成一个 GeneratorContextManager 实例。最终 with 表达式中的 EXPR 调用的是由 contentmanager 装饰器返回的帮助函数。
- with 表达式调用 transaction(db) ,实际上是调用帮助函数。帮助函数调用生成器函数,生成器函数创建一个生成器。
- 帮助函数将这个生成器传递给 GeneratorContextManager ,并创建一个 GeneratorContextManager 的实例对象作为上下文管理器。
- with 表达式调用实例对象的上下文管理器的 __enter()__ 方法。
- __enter()__ 方法中会调用这个生成器的 next() 方法。这时候,生成器方法会执行到 yield db 处停止,并将 db 作为 next() 的返回值。如果有 as VAR ,那么它将会被赋值给 VAR 。
- with 中的 BLOCK 被执行。
- BLOCK 执行结束后,调用上下文管理器的 __exit()__ 方法。 __exit()__ 方法会再次调用生成器的 next() 方法。如果发生 StopIteration 异常,则 pass 。
- 如果没有发生异常生成器方法将会执行 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详解的更多相关文章
- Scrapy笔记04- Selector详解
Scrapy笔记04- Selector详解 在你爬取网页的时候,最普遍的事情就是在页面源码中提取需要的数据,我们有几个库可以帮你完成这个任务: BeautifulSoup是python中一个非常流行 ...
- Scrapy笔记05- Item详解
Scrapy笔记05- Item详解 Item是保存结构数据的地方,Scrapy可以将解析结果以字典形式返回,但是Python中字典缺少结构,在大型爬虫系统中很不方便. Item提供了类字典的API, ...
- python之OS模块详解
python之OS模块详解 ^_^,步入第二个模块世界----->OS 常见函数列表 os.sep:取代操作系统特定的路径分隔符 os.name:指示你正在使用的工作平台.比如对于Windows ...
- python之sys模块详解
python之sys模块详解 sys模块功能多,我们这里介绍一些比较实用的功能,相信你会喜欢的,和我一起走进python的模块吧! sys模块的常见函数列表 sys.argv: 实现从程序外部向程序传 ...
- python中threading模块详解(一)
python中threading模块详解(一) 来源 http://blog.chinaunix.net/uid-27571599-id-3484048.html threading提供了一个比thr ...
- python自学笔记
python自学笔记 python自学笔记 1.输出 2.输入 3.零碎 4.数据结构 4.1 list 类比于java中的数组 4.2 tuple 元祖 5.条件判断和循环 5.1 条件判断 5.2 ...
- Python数据类型及其方法详解
Python数据类型及其方法详解 我们在学习编程语言的时候,都会遇到数据类型,这种看着很基础也不显眼的东西,却是很重要,本文介绍了python的数据类型,并就每种数据类型的方法作出了详细的描述,可供知 ...
- python引用和对象详解
python引用和对象详解 @[马克飞象] python中变量名和对象是分离的 例子 1: a = 1 这是一个简单的赋值语句,整数 1 为一个对象,a 是一个引用,利用赋值语句,引用a指向了对象1. ...
- Python中time模块详解
Python中time模块详解 在平常的代码中,我们常常需要与时间打交道.在Python中,与时间处理有关的模块就包括:time,datetime以及calendar.这篇文章,主要讲解time模块. ...
随机推荐
- PHP初入,for循环使用
一: 找出100-999之间的所有"水仙花数".所谓水仙花数是指一个三位 数,各位数字的立方和等于该数本身.(如153次方=1的3次方+5的3次方+3的3次方)并输出这些数字 想想 ...
- PS各个工具的字母快捷键和英…
原文地址:PS各个工具的字母快捷键和英文全名作者:Tycho 选框-Marquee(M) 移动-move(V) 套索-Lasso(L) 魔棒-Wand(W) 喷枪-in ...
- 201521123044 《Java程序设计》第6周学习总结
1. 本章学习总结 1.1 面向对象学习暂告一段落,请使用思维导图,以封装.继承.多态为核心概念画一张思维导图,对面向对象思想进行一个总结. 注1:关键词与内容不求多,但概念之间的联系要清晰,内容覆盖 ...
- 201521123001《Java程序设计》第6周学习总结
1. 本周学习总结 1.1 面向对象学习暂告一段落,请使用思维导图,以封装.继承.多态为核心概念画一张思维导图,对面向对象思想进行一个总结. 注1:关键词与内容不求多,但概念之间的联系要清晰,内容覆盖 ...
- request.getParameter()获取URL中文参数乱码的解决办法
这个问题耽误好长时间,URL传中文参数出现乱码,就算首次使用request接收就添加 request.setCharacterEncoding("UTf-8"); 依然报错不误. ...
- Linux入门_2-基础命令
Linux入门-基础命令 目录 日期命令date 修改时区 日历命令cal 关机启动命令halt,reboot,poweroff whoami.who.who am i.w screen ...
- 函数原型属性-JavaScript深入浅出(三)
前两次总结了JavaScript中的基本数据类型(值类型<引用类型>,引用类型<复杂值>)以及他们在内存中的存储,对内存空间有了一个简单的了解,以及第二次总结了this深入浅出 ...
- MyBatis学习(一)简介及入门案例
1.什么是MyBatis? MyBatis是一个支持普通SQL查询,存储过程,和高级映射的优秀持久层框架.MyBatis去掉了几乎所有的JDBC代码和参数的手工设置以及对结果集的检索封装.MyBati ...
- APUE 3 -- 信号 (signal)<II>: 可靠信号
一个事件可以事一个信号发送给一个进程,这个事件可以是硬件异常,可以是软件条件触发,可以是终端产生信号,也可以是一个kill函数调用.当信号产生后,内核通常会在进程表中设置某种形式的标志(flag).我 ...
- 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 ...