python关键字详解_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关键字详解_Python 中的关键字with详解相关推荐
- python协程库_python中协程的详解(附示例)
本篇文章给大家带来的内容是关于python中协程的详解(附示例),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助. 协程,又称微线程,纤程.英文名Coroutine 协程看上去也是子程序 ...
- python里的关键字有哪些_Python 中的关键字有哪些?
在Python中,具有特殊功能的标识符称为关键字.关键字是Python语言自己已经使用的了,不允许开发者自己定义和关键字相同名字的标识符.Python中的关键字如下所示: Falsedefifrais ...
- python 下划线变量_Python中的下划线详解
这篇文章讨论Python中下划线_的使用.跟Python中很多用法类似,下划线_的不同用法绝大部分(不全是)都是一种惯例约定. 一. 单个下划线直接做变量名(_) 主要有三种情况: 1. 解释器中 _ ...
- python之33个关键字详解_Python 中的关键字 with 详解
在 Python 2.5 中,with关键字被加入.它将常用的 try ... except ... finally ...模式很方便的被复用.看一个最经典的例子: with open('file.t ...
- python 内部函数传参_python中函数传参详解
一.参数传入规则 可变参数允许传入0个或任意个参数,在函数调用时自动组装成一个tuple: 关键字参数允许传入0个或任意个参数,在函数调用时自动组装成一个dict: 1. 传入可变参数: def ca ...
- python默认参数举例_Python中的默认参数详解
文章的主题 不要使用可变对象作为函数的默认参数例如 list,dict,因为def是一个可执行语句,只有def执行的时候才会计算默认默认参数的值,所以使用默认参数会造成函数执行的时候一直在使用同一个对 ...
- python中的zip函数详解_python中的 zip函数详解
python中zip()函数用法举例 定义:zip([iterable, ...]) zip()是Python的一个内建函数,它接受一系列可迭代的对象作为参数,将对象中对应的元素打包成一个个tuple ...
- python中长下划线_Python中的下划线详解
这篇文章讨论Python中下划线_的使用.跟Python中很多用法类似,下划线_的不同用法绝大部分(不全是)都是一种惯例约定. 一. 单个下划线直接做变量名(_) 主要有三种情况: 1. 解释器中 _ ...
- java 死锁 内存消耗_详解Java中synchronized关键字的死锁和内存占用问题
先看一段synchronized 的详解: synchronized 是 java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码. 一.当两个并 ...
最新文章
- php7 ext skel_基于PHP7的PHP扩展开发之一(hello word)
- bzoj2054 疯狂的馒头
- xhprof安装使用【转】
- javascript对象和json字符串之间转换的问题
- java循环语句三角形_JAVA——程序流程控制——循环语句——for循环(打印三角形)...
- 部署RocketMQ的管理工具
- gcc编译器java_「gcc编译器下载」gcc编译器下载各版本下载 - seo实验室
- matplolib绘图
- 你的计算机无法启动一键还原,电脑一开机就进入dos之家的一键还原硬盘版,无法进入系统...
- java爬虫教程:模拟用户表单登录
- ASP.NET AJAX学习笔记之:CollapsiblePanel---可折叠的面板
- adobe acrobat pro dc 无法打开PDF_pdf怎样转成word格式
- 二阶、三角、三阶、四阶、五阶魔方还原方法总结
- java 区分鼠标左键单击和双击
- 定制Android开发者专属T恤
- Linux代理服务器 Centos Nginx安装、反向代理配置、Nginx开机自启动及日志每天自动分割压缩
- 电阻电路的等效变化(Ⅱ)
- MongoDB集群和安全
- PHP论坛开发技术总结
- 微信小游戏上线流程及游戏自审自查报告模板
热门文章
- [渝粤教育] 广东-国家-开放大学 21秋期末考试基础会计10258k2
- [渝粤教育] 中国地质大学 微积分(一) 复习题 (2)
- 【渝粤教育】电大中专学前儿童科学教育 (5)作业 题库
- 【渝粤教育】电大中专测量学 (5)作业 题库
- 【渝粤题库】国家开放大学2021春2441经济数学基础1题目
- outlook邮箱邮件内容乱码_VBA:Outlook和Excel综合运用
- linux正则表达式脚本实例,PowerShell中正则表达式使用例子
- 删库跑路?不可回滚?MySQL创建和管理表,修改清空表,MySQL8新特性DDL原子化,完整详细可收藏
- Python 3实现k-邻近算法以及 iris 数据集分类应用
- 3. Recursive AutoEncoder(递归自动编码器)