With语句以及@contextmanager的语法解析
with 语句以及@contextmanager的语法解析
with语句可以通过很简单的方式来替try/finally语句。 with语句中EXPR部分必须是一个包含__enter__()和__exit__()方法的对象,也就是Context Manager。使用with语句的目的:
- 提供可靠的资源自动释放,在with代码执行前请求资源,代码运行结束后资源会释放。
- 简化代码,代码可读性以及逻辑的简明都会提高很多。
- 创造临时的上下文环境,例如做一个临时的网络请求并获取返回值作为上下文环境。
- 通过contextmanager和generator创造线程操作异步所。
下述例子描述with...as语句的实现原理:
#EXPR open('var/log/test.log)
EXPR = open('/var/log/test.log')
with EXPR as VAR:
#BLOCK START
data = VAR.read(50)
print data
#BLOCK END
test1
test2
test3
test4
test5
实现原理:
- 在with语句中, EXPR必须是一个包含
__enter__()和__exit__()方法的对象(Context Manager)。 - 调用EXPR的
__enter__()方法申请资源并将结果赋值给VAR变量。 - 通过
try/except确保代码块BLOCK正确调用,否则调用EXPR的__exit__()方法退出并释放资源。 - 在代码块BLOCK正确执行后,最终执行EXPR的
__exit__()方法释放资源。
import sys
EXPR = open('/var/log/test.log')
EXIT = type(EXPR).__exit__ #not calling it yet
VAR = type(EXPR).__enter__(EXPR)
EXC = True
try:
try:
#BLOCK START
data = VAR.read(50)
print data
#BLOCK END
except:
EXC = False
if not EXIT(EXPR, *sys.exc_info()):
raise
finally:
if EXC:
EXIT(EXPR, None, None, None)
test1
test2
test3
test4
test5
EXPR可以使用with语句的前提,必须是一个包含__enter__()和__exit()__方法的对象(Context Manager),最直接的方式是声明一个对象,在__enter__()方法里面申请资源,在__exit__()方法里面释放资源;EXPR返回此对象。
更通用和更高效的将普通的函数转变为包含__enter__()和__exit__()方法的对象的方法是:通过一个特定的decorator(@contextmanager)扩展该函数并将函数声明为非循环的单一返回值的generator。
generator可以将函数变成类似于iterator,每次调用好像通过iterator的next逐步读取,而不是一次返回。
- 比实现一个iterator简单,iterator需要实现
__init__,__iter__,__next__函数。- 比将结果一次返回(全部读取到内存中)要节省内存,通过next可以逐步获取需要的值。
@contextmanagerdecorator的实现原理:
声明contextmanager的decorator函数。参数是generator,返回值是一个接受和generator函数同样参数并且将generaor函数和参数传递到Context Manager构造函数并返回Context Manager对象的函数。绕死了!!!
decorator函数,是接受函数作为参数,并且返回一个函数的的函数。当对函数func进行此修饰时相当于对func进行一次转变: func = decorator(func),在这里generator被修饰后变成了
help(*args, **kwargs)函数在
__enter__()方法中使用generator的next()方法获取第一个返回值。如果gen并不是generator函数,抛出一个runtime异常。- 在
__exit__()方法中,如果存在异常将异常跑出,否则继续调用generator的next()方法。因此generator是一个具有唯一值非loop的generator因此会抛出stopiteration异常(正常预期值),否则抛出一个runtime异常。
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:
return
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:
# only re-raise if it's *not* the exception that was
# passed to throw(), because __exit__() must not raise
# an exception unless __exit__() itself failed. But
# throw() has to raise the exception to signal
# propagation, so this fixes the impedance mismatch
# between the throw() protocol and the __exit__()
# protocol.
#
if sys.exc_info()[1] is not value:
raise
def contextmanager(func):
def helper(*args, **kwds):
return GeneratorContextManager(func(*args, **kwds))
return helper
上述并没有涉及到资源的申请和释放,因此在generator函数里面,第一个loop即__enter__()函数调用时,进行资源的申请,第二个loop也就是__exit__()函数调用时抛出异常进入finally模块释放资源。下面是应用示例:
@contextmanager
def opening(filename):
f = open(filename) # IOError is untouched by GeneratorContext
try:
yield f
finally:
f.close() # Ditto for errors here (however unlikely)
如上所述,就是with语句以及contextmanager decorator修饰器的语法解析。标准库里有些对象已经是context manager了,例如:
- file
- thread.LockType
- threading.Lock
- threading.RLock
- threading.Condition
- threading.Semaphore
- threading.BoundedSemaphore
另外我们也可以通过contextlib.contextmanager来修饰我们的generator函数,作为context manager使用在with语句中。
With语句以及@contextmanager的语法解析的更多相关文章
- MySQL- -Join语法解析与性能分析
Mysql Join语法解析与性能分析 一.Join语法概述 join 用于多表中字段之间的联系,语法如下: ... FROM table1 INNER|LEFT|RIGHT JOIN table2 ...
- 在.NET Core中使用Irony实现自己的查询语言语法解析器
在之前<在ASP.NET Core中使用Apworks快速开发数据服务>一文的评论部分,.NET大神张善友为我提了个建议,可以使用Compile As a Service的Roslyn为语 ...
- Generator函数语法解析
转载请注明出处: Generator函数语法解析 Generator函数是ES6提供的一种异步编程解决方案,语法与传统函数完全不同.以下会介绍一下Generator函数. 写下这篇文章的目的其实很简单 ...
- JSP编译成Servlet(一)语法树的生成——语法解析
一般来说,语句按一定规则进行推导后会形成一个语法树,这种树状结构有利于对语句结构层次的描述.同样Jasper对JSP语法解析后也会生成一棵树,这棵树各个节点包含了不同的信息,但对于JSP来说解析后的语 ...
- 用java实现编译器-算术表达式及其语法解析器的实现
大家在参考本节时,请先阅读以下博文,进行预热: http://blog.csdn.net/tyler_download/article/details/50708807 本节代码下载地址: http: ...
- 用java实现一个简易编译器-语法解析
语法和解析树: 举个例子看看,语法解析的过程.句子:“我看到刘德华唱歌”.在计算机里,怎么用程序解析它呢.从语法上看,句子的组成是由主语,动词,和谓语从句组成,主语是“我”,动词是“看见”, 谓语从句 ...
- 使用 java 实现一个简单的 markdown 语法解析器
1. 什么是 markdown Markdown 是一种轻量级的「标记语言」,它的优点很多,目前也被越来越多的写作爱好者,撰稿者广泛使用.看到这里请不要被「标记」.「语言」所迷惑,Markdown 的 ...
- Boost学习之语法解析器--Spirit
Boost.Spirit能使我们轻松地编写出一个简单脚本的语法解析器,它巧妙利用了元编程并重载了大量的C++操作符使得我们能够在C++里直接使用类似EBNF的语法构造出一个完整的语法解析器(同时也把C ...
- Atitit.sql ast 表达式 语法树 语法 解析原理与实现 java php c#.net js python
Atitit.sql ast 表达式 语法树 语法 解析原理与实现 java php c#.net js python 1.1. Sql语法树 ast 如下图锁死1 2. SQL语句解析的思路和过程3 ...
随机推荐
- Python交互式编程导论----事件驱动编程
传统的编程是如下线性模式的: 开始--->代码块A--->代码块B--->代码块C--->代码块D--->......--->结束 每一个代码块里是完成各种各样事情 ...
- [转]简单理解Socket
简单理解Socket 转自 http://www.cnblogs.com/dolphinX/p/3460545.html 题外话 前几天和朋友聊天,朋友问我怎么最近不写博客了,一个是因为最近在忙着公 ...
- mac上用teamviewer远程windows输入问题
问题: 在mac上用teamviewer远程windows时,碰到shift,=等按键无效的问题. 解决办法: 远程桌面连接服务器时,键盘不能正常打字.此时把windows自带的小键盘打开后,此时再用 ...
- C--变量的命名规则
规则1.标识符最好采用英文单词或其组合,不允许使用拼音. 规则3.当标识符由多个词组成时,每个词的第1个字母大写,其余全部小写: int CurrentVal; 规则5.对在多个文件之间共同使用的 ...
- Ubuntu实现wifi热点
系统:Ubuntu14.04 方法一: 打开网络连接,新建wifi,设置SSID和密码,完成设置后保存,再连接到隐藏的wifi,选择刚建立好的连接即可 优点:简单方便 缺点:建立的是Ad-hoc模式信 ...
- mysql的多实例安装
单机多实例据说可以最大程度提高硬件使用,谁知道呢,但是以前的公司喜欢这样搞,我最近也在学习复制什么的,电脑搞不起两台虚拟机,刚好单机多实例可以解救我.下面就说说步骤. 承上文http://www.cn ...
- web初学之request,session与application
request (1)request的setAttribute()与getAttribute()方法一般都是成对出现,首先通过setAttribute()方法设置属性与属性值,然后通过getAttri ...
- 关于rank、dense_rank、ROW_NUMBER及OVER(PARTITION BY)、OVER(ORDER BY)的一些用法
CREATE TABLE t_harry ( id int NOT NULL, ) DEFAULT NULL, ChannelID ) DEFAULT NULL, TimeStamp datetime ...
- 论Segmentation fault
刚开始学c的时候,最头疼的事情是编译总是通不过,郁闷的要死,只要编译通过了,就兴奋的要死.现在,最头疼的事情是什么呢,编译没问题,但是程序跑的时候会出现Segmentation fault! 这个东西 ...
- Servlet学习五——流的分发
在上一节中有提到,流的传输,可以考虑Stream,但如果需要同时分发流和其它信息,,就需要再考虑其它方式了. 在coding中,服务端查询结果都是以gson进行传输,当需要传输一个语音并且同时需要传输 ...