这篇文章主要介绍了Python 中的with关键字使用详解的相关资料,在Python中,with关键字是一个替你管理实现上下文协议对象的好东西,需要的朋友可以参考下

">

在 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中为什么推荐使用with_Python中的with关键字使用详解相关推荐

  1. python类继承中构造方法_第8.3节 Python类的__init__方法深入剖析:构造方法与继承详解...

    第8.3节Python类的__init__方法深入剖析:构造方法与继承详解 一.    引言 上两节介绍了构造方法的语法及参数,说明了构造方法是Python的类创建实例后首先执行的方法,并说明如果类没 ...

  2. python跨函数调用变量_对python中不同模块(函数、类、变量)的调用详解

    首先,先介绍两种引入模块的方法. 法一:将整个文件引入 import 文件名 文件名.函数名( ) / 文件名.类名 通过这个方法可以运行另外一个文件里的函数 法二:只引入某个文件中一个类/函数/变量 ...

  3. python中ndim是什么_Numpy中ndim、shape、dtype、astype的用法详解

    本文介绍numpy数组中这四个方法的区别ndim.shape.dtype.astype. 1.ndim ndim返回的是数组的维度,返回的只有一个数,该数即表示数组的维度. 2.shape shape ...

  4. python语言中split-python中的split()函数和os.path.split()函数使用详解

    Python中有split()和os.path.split()两个函数: split():拆分字符串.通过指定分隔符对字符串进行切片,并返回分割后的字符串列表. os.path.split():将文件 ...

  5. python中的json函数_python中装饰器、内置函数、json的详解

    装饰器 装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象. 先看简单例子: def run(): time.sleep(1 ...

  6. python中的异常分类_列举 5 个 Python 中的异常类型以及其含义【面试题详解】

    今天爱分享给大家带来列举 5 个 Python 中的异常类型以及其含义[面试题详解],希望能够帮助到大家. BaseException +-- SystemExit +-- KeyboardInter ...

  7. 站长在线Python教程精讲:在Python函数中的关键字参数详解

    欢迎你来到站长在线的站长学堂学习Python知识,本文学习的是<在Python函数中的关键字参数详解>.本文的主要内容有:关键字参数的含义和关键字参数的应用举例. 目录 1.关键字参数的含 ...

  8. python编译器怎么运行不在路径中的py文件_对python当中不在本路径的py文件的引用详解...

    众所周知,如果py文件不在当前路径,那么就不能import,因此,本文介绍如下两种有效的方法: 方法1: 修改环境变量,在~/.bashrc里面进行修改,然后source ~/.bashrc 方法2: ...

  9. ios开发读取剪切板的内容_iOS中管理剪切板的UIPasteboard粘贴板类用法详解

    一.自带剪切板操作的原生UI控件在iOS的UI系统中,有3个控件自带剪切板操作,分别是UITextField.UITextView与UIWebView.在这些控件的文字交互处进行长按手势可以在屏幕视图 ...

最新文章

  1. HUNAN 11560 Yangyang loves AC(二分+贪心)
  2. JS中格式化数据保留两位小数
  3. 2005年1月-2008年10月雅思A类(学术类)作文 TASK 2 考题汇总(10月4日更新)
  4. c语言多次调用函数 只给出最后一次,一个方法连续调用多次,但方法体里面的代码只保证最后一次的执行,之前连续调用的方法体都不执行...
  5. 支付系统源码商业版 完美可运营
  6. Linux操作系统知识点总结
  7. java查看eth转账状态,eth转账确认查询
  8. html pc页面连接到微信,PC上对限制在微信客户端访问的html页面进行调试
  9. linux 硬件raid 坏道,Linux服务器磁盘坏道的修复过程
  10. CS144——Lab0——networking warmup
  11. 关于MTTF、MTBF、MTRF
  12. IntelliJ IDEA 最新注册码(截止到2019年12月12日)
  13. Android 版灵动岛插件上线;iPhone 15或将改名,并改用USB-C接口;​Swift 5.7 发布|极客头条
  14. mysql sus bench_测量性能 (Benchmarking)
  15. 气体灭火系统和自动喷水灭火系统之区别
  16. 盐城工学院计算机考研,厉害!盐城工学院海生学院毕业生考研成功率近六成
  17. 认识USB Type-C Type-CB Type-A 接口
  18. 2020年计算机科学与技术学校排名,2020年全国计算机科学与技术专业大学排名
  19. 手写Promisify函数
  20. Hadoop运行原理之Spill, Shuffle

热门文章

  1. java读c二进制文件_如何使用JAVA读取C / Matlab创建的二进制文件
  2. oracle key的含义,v$session SERIAL#字段的含义
  3. 怎样能确保计算机安全,如何确保电脑安全
  4. react同步请求_React中setState同步更新策略
  5. linux18配置静态ip,ubuntu18配置静态IP地址
  6. 项目管理最佳实践方法_项目管理最佳实践,企业如何进行有效的项目管理
  7. Oracle行转列语法总结大全
  8. JDK源码解析之 Java.lang.Object
  9. 广度优先搜索练习之神奇的电梯
  10. 汇编中的函数调用与递归