With语句以及@contextmanager的语法解析

with 语句以及@contextmanager的语法解析

?

with语句可以通过很简单的方式来替try/finally语句。 with语句中EXPR部分必须是一个包含__enter__()__exit__()方法的对象,也就是Context Manager。使用with语句的目的:

  • 提供可靠的资源自动释放,在with代码执行前请求资源,代码运行结束后资源会释放。
  • 简化代码,代码可读性以及逻辑的简明都会提高很多。
  • 创造临时的上下文环境,例如做一个临时的网络请求并获取返回值作为上下文环境。
  • 通过contextmanager和generator创造线程操作异步所。

下述例子描述with...as语句的实现原理:

In?[15]:

?

?

?

?

?

#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

?

实现原理:

  1. 在with语句中, EXPR必须是一个包含__enter__()__exit__()方法的对象(Context Manager)。
  2. 调用EXPR的__enter__()方法申请资源并将结果赋值给VAR变量。
  3. 通过try/except确保代码块BLOCK正确调用,否则调用EXPR的__exit__()方法退出并释放资源。
  4. 在代码块BLOCK正确执行后,最终执行EXPR的__exit__()方法释放资源。

In?[16]:

?

?

?

?

?

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的实现原理:

  1. 声明contextmanager的decorator函数。参数是generator,返回值是一个接受和generator函数同样参数并且将generaor函数和参数传递到Context Manager构造函数并返回Context Manager对象的函数。绕死了!!!

    decorator函数,是接受函数作为参数,并且返回一个函数的的函数。当对函数func进行此修饰时相当于对func进行一次转变: func = decorator(func),在这里generator被修饰后变成了help(*args, **kwargs)函数

  2. __enter__()方法中使用generator的next()方法获取第一个返回值。如果gen并不是generator函数,抛出一个runtime异常。
  3. __exit__()方法中,如果存在异常将异常跑出,否则继续调用generator的next()方法。因此generator是一个具有唯一值非loop的generator因此会抛出stopiteration异常(正常预期值),否则抛出一个runtime异常。

In?[24]:

?

?

?

?

?

 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模块释放资源。下面是应用示例:

In?[25]:

?

?

?

?

?

@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语句中。

时间: 2024-11-03 00:30:11

With语句以及@contextmanager的语法解析的相关文章

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 2.1. lexer作为一个工具,完成了对SQL字符串的切割,将语句转化成一个tokens数组.3 2.2. Parser完成了SQL解析的后序部分:使用一个lexer对象作为工具,切出tokens,然后解析语义,绑定相关的系统接口.3 2.3. 关系数据和XML数据库下其抽象语法树分别为: 如图

Mysql Join语法解析与性能分析详解

一.Join语法概述 join 用于多表中字段之间的联系,语法如下: ... FROM table1 INNER|LEFT|RIGHT JOIN table2 ON conditiona table1:左表:table2:右表. JOIN 按照功能大致分为如下三类: INNER JOIN(内连接,或等值连接):取得两个表中存在连接匹配关系的记录. LEFT JOIN(左连接):取得左表(table1)完全记录,即是右表(table2)并无对应匹配记录. RIGHT JOIN(右连接):与 LEF

MYSQL 源代码 编译原理 AST和解析树 代码语法解析

MYSQL 源代码 编译原理 AST和解析树 代码语法解析 http://blog.csdn.net/wfp458113181wfp/article/details/17082355 使用AST树 分类:             antlr              2013-12-02 22:39     255人阅读     评论(0)     收藏     举报 目录(?)[+] 第五章使用AST树中间结果来计算表达式值 创建ASTS 第五章.使用AST树中间结果来计算表达式值 现在我们已

oceanbase中存储过程的实现(一)语法解析部分

原创性声明 本文出处为http://blog.csdn.net/zhujunxxxxx/article/details/39251491,版权归作者所有,如需转载请注明作者,出处! 技术背景 淘宝的开源数据库oceanbase 是一个支持海量数据的高性能分布式数据库系统,实现了数千亿条记录.数百TB数据上的跨行跨表事务,由淘宝核心系统研发部开发的. 但是现在oceanbase0.4版本是不支持很多功能的,其中包括存储过程. 开发方案 淘宝数据使用的是flex&bison来进行sql语句的解析的,

用java实现一个简易编译器-语法解析

语法和解析树: 举个例子看看,语法解析的过程.句子:"我看到刘德华唱歌".在计算机里,怎么用程序解析它呢.从语法上看,句子的组成是由主语,动词,和谓语从句组成,主语是"我",动词是"看见", 谓语从句是"刘德华唱歌".因此一个句子可以分解成 主语 + 动词 + 谓语从句: 句子-->主语+动词 + 谓语从句 主语是名词,因此有 : 主语->名词 句子里的名词有: "我", "刘德华&q

Generator函数语法解析

转载请注明出处: Generator函数语法解析 Generator函数是ES6提供的一种异步编程解决方案,语法与传统函数完全不同.以下会介绍一下Generator函数. 写下这篇文章的目的其实很简单,是想梳理一下自己对于Generator的理解,同时呢,为学习async函数做一下知识储备. Generator函数 基本概念 yield表达式 next方法 next方法的参数 yield*表达式 与Iterator接口的关系 for...of循环 作为对象属性的Generator函数 Gener

MySQL技术探索01实现SQL语法解析器

本文将介绍如何使用开源的语法和词法分析框架bison和flex来实现SQL解析器.出于技术学习的目的,本文做描述的微型SQL解析器仅能实现对微型SQL的语法解析. 1.MySQL中的SQL解析器 包括JDBC.ODBC.ADO等等关系数据库客户端应用开发框架在内的各种SDK,核心功能是帮助程序员简化各种客户端的数据库操作,同时将SQL语句通过网络形式发送给MySQL等关系数据库的服务器进程.MySQL服务器进行负责解析并执行这些SQL语句.SQL语句中的语法规则多种多样,MySQL服务器是如何实

工作中的那些坑(2)——语法解析器

工作项目里用到线性回归算法,用于计算账户的分值,表明某账户是否是有风险的账户.其中参数都配好了,代码里直接用逆波兰表达式解析即可.本来事情到这里已经结束,突然来了新的需求:账户算出来的分数较为无序,于是考虑用sigmoid函数将其映射到(0,1)区间内,在乘以系数使其显示更为直观.为了使整个表达式更将通用,要求做到同时能解析sigmoid函数,即:原来单纯解析常量.变量.运算符的逆波兰表达式已经不能直接解析新增的sigmoid函数(即表达式),另外对嵌套的情况也没做处理,所以需要重新设计一个更为

nsis安装包_示例脚本语法解析

以下是代码及解析,其中有底色的部分为脚本内容. 注释.!define.变量.!include.常量 ; Script generated by the HM NIS Edit Script Wizard. ; HM NIS Edit Wizard helper defines !define PRODUCT_NAME "signjing安装示例" !define PRODUCT_VERSION "0.0.0.1" !define PRODUCT_PUBLISHER