python with详解_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=Truetry:try:

VAR= value #如果有 as VAR

BLOCKexcept:

exc=Falseif not exit(mgr, *sys.exc_info()):raise

finally:ifexc:

exit(mgr, None, None, None)

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

这个过程有几个细节:

如果上下文管理器中没有 __enter()__ 或者 __exit()__ 中的任意一个方法,那么解释器会抛出一个 AttributeError 。

在 BLOCK 中发生异常后,如果 __exit()__ 方法返回一个可被看成是 True 的值,那么这个异常就不会被抛出,后面的代码会继续执行。

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

importsystry:

blockexcept:

info=sys.exc_info()

print(info)

或者以如下的形式:

importsys

tp,val,td= sys.exc_info()

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

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

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

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

importsystry:#text.txt not exist

fp = open('text.txt','rb')

data=fp.write()except:print(sys.exc_info())

以上代码返回的数据值为:(, FileNotFoundError(2, 'No such file or directory'), )

实现上下文管理器类:

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

classtransaction(object):def __init__(self, db):

self.db=dbdef __enter__(self):

self.db.begin()def __exit__(self, type, value, traceback):if type isNone:

self.db.commit()else:

self.db.rollback()

使用生成器装饰器

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

from contextlib importcontextmanager

@contextmanagerdeftransaction(db):

db.begin()try:yielddbexcept:

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() 。

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

defcontextmanager(func):def helper(*args, **kwargs):return GeneratorContextManager(func(*args, **kwargs))returnhelperclassGeneratorContextManager(object):def __init__(self, gen):

self.gen=gendef __enter__(self):try:returnself.gen.next()exceptStopIteration:raise RuntimeError("generator didn't yield")def __exit__(self, type, value, traceback):if type isNone:try:

self.gen.next()exceptStopIteration:pass

else:raise RuntimeError("generator didn't stop")else:try:

self.gen.throw(type, value, traceback)raise RuntimeError("generator didn't stop after throw()")exceptStopIteration:returnTrueexcept:if sys.exc_info()[1] is notvalue:raise

锁机制:

@contextmanagerdeflocked(lock):

lock.acquired()try:yield

finally:

lock.release()

#标准输出重定向

@contextmanagerdefstdout_redirect(new_stdout):

old_stdout=sys.stdout

sys.stdout=new_stdouttry: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/

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值