在 Python 2.5 中,with关键字被加入。它将常用的 try ... except ... finally ...模式很方便的被复用。看一个最经典的例子:

with open('file.txt') as f:

content = f.read()

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

再看另外一个例子。

在发起一个数据库事务请求的时候,经常会用类似这样的代码:

db.begin()

try:

# do some actions

except:

db.rollback()

raise

finally:

db.commit()

如果将发起事务请求的操作变成可以支持with关键字的,那么用像这样的代码就可以了:

with transaction(db):

# do some actions

下面,详细的说明一下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的值,那么这个异常就不会被抛出,后面的代码会继续执行。

接下来,用两种方法来实现上面来实现上面的过程的吧。

实现上下文管理器类

第一种方法是实现一个类,其含有一个实例属性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:

db.commit()

else:

db.rollback()

了解with的执行过程后,这个实现方式是很容易理解的。下面介绍的实现方式,其原理理解起来要复杂很多。

使用生成器装饰器

在 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

总结

Python 的with表达式包含了很多 Python 特性,花点时间吃透with是一件非常值得的事情。

一些其他的例子

锁机制

@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"

引用

python之33个关键字详解_Python 中的关键字 with 详解相关推荐

  1. python里的关键字有哪些_Python 中的关键字有哪些?

    在Python中,具有特殊功能的标识符称为关键字.关键字是Python语言自己已经使用的了,不允许开发者自己定义和关键字相同名字的标识符.Python中的关键字如下所示: Falsedefifrais ...

  2. python关键字详解_Python 中的关键字with详解

    在 Python 2.5 中,with关键字被加入.它将常用的 try ... except ... finally ...模式很方便的被复用.看一个最经典的例子: with open('file.t ...

  3. python中的zip函数详解_python中的 zip函数详解

    python中zip()函数用法举例 定义:zip([iterable, ...]) zip()是Python的一个内建函数,它接受一系列可迭代的对象作为参数,将对象中对应的元素打包成一个个tuple ...

  4. java 死锁 内存消耗_详解Java中synchronized关键字的死锁和内存占用问题

    先看一段synchronized 的详解: synchronized 是 java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码. 一.当两个并 ...

  5. And解roid中Activity启动模式详

    转自: http://www.cnblogs.com/fanchangfa/archive/2012/08/25/2657012.html And解roid中Activity启动模式详 在Androi ...

  6. python中json模块博客园_Python中的Json模块详解

    Python中的Json模块详解 Json(JavaScript Object Notation)它是一种轻量级的数据交换格式,具有数据格式简单,读写方便易懂等很多优点.许多主流的编程语言都在用它来进 ...

  7. python协程库_python中协程的详解(附示例)

    本篇文章给大家带来的内容是关于python中协程的详解(附示例),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助. 协程,又称微线程,纤程.英文名Coroutine 协程看上去也是子程序 ...

  8. python中new方法详解及_Python中new方法的详解

    new_ 方法是什么? __new__方法其实就是创建对象的方法 new()方法是在类准备将自身实例化时调用. 一个类可以有多个位置参数和多个命名参数,而在实例化开始之后,在调用 init()方法之前 ...

  9. python中的super用法详解_Python中super函数用法实例分析

    本文实例讲述了python中super函数用法.分享给大家供大家参考,具体如下: 这是个高大上的函数,在python装13手册里面介绍过多使用可显得自己是高手 23333. 但其实他还是很重要的. 简 ...

最新文章

  1. Failed to instantiate one or more classes
  2. SAP MM 移动类型343不开放给业务人员之思考
  3. Android应用点击两次back退出
  4. 读书笔记《单核工作法》_3原理2,3
  5. 函数重载 覆盖 隐藏
  6. 云计算风起云涌,超融合恰逢其时!
  7. Docker系列二~自定义网桥
  8. 云小课|细数那些VMware虚拟机的恢复招式
  9. 免费linux 脚本,替换xshell,说一下一些免费的shell工具
  10. 45本数据分析、Python的书籍,包邮送到家
  11. smbclient用法
  12. NXOPEN/UG二次开发C#---获取NX的版本
  13. WIN10作为服务器操作系统可以吗,服务器可以装win10吗
  14. 年纪一大把,胡子一大堆,还能学好编程吗?今天我问了我自己
  15. 《稀缺》塞德希尔·穆来纳森 / 埃尔德·沙菲尔
  16. 让技术Leader狂点赞的Linux速成手册,到底有多强悍?
  17. 编码的奥秘:从算盘到芯片
  18. 「Adobe国际认证」运用“对象选择”工具,在PS中快速建立选区
  19. windows电脑cmd命令查看网卡的物理地址(mac地址)
  20. 用krpano 分解下载全景图

热门文章

  1. 如何提高分布式系统的可用性
  2. Android关于SQLiteOpenHelper的封装
  3. Java Web Application 自架构 四 Log4j2日志管理
  4. 关于Pulsar与Kafka
  5. 区块链教程Fabric1.0源代码分析scc(系统链码)
  6. Linux Shell脚本之向脚本传递参数
  7. Annoy搜索算法(Approximate Nearest Neighbors Oh Yeah)
  8. 《分布式系统:概念与设计》一3.5.2 IEEE 802.11无线LAN
  9. 03 Cisco IOS设备
  10. python运行完不能显示图_Python Pygame无法正确显示图像