zoukankan      html  css  js  c++  java
  • python with原型

    @Python 的 with 语句详解

     
    这篇文章主要介绍了Python 的 with 语句,本文详细讲解了with语句、with语句的历史、with语句的使用例子等,需要的朋友可以参考下
     

    一、简介

    with是从Python 2.5 引入的一个新的语法,更准确的说,是一种上下文的管理协议,用于简化try…except…finally的处理流程。with通过__enter__方 法初始化,然后在__exit__中做善后以及处理异常。对于一些需要预先设置,事后要清理的一些任务,with提供了一种非常方便的表达。

    with的基本语法如下,EXPR是一个任意表达式,VAR是一个单一的变量(可以是tuple),”as VAR”是可选的。

    复制代码 代码如下:

    with EXPR as VAR:
        BLOCK


    根据PEP 343的解释,with…as…会被翻译成以下语句:

    复制代码 代码如下:

    mgr = (EXPR)
    exit = type(mgr).__exit__  # Not calling it yet
    value = type(mgr).__enter__(mgr)
    exc = True
    try:
        try:
            VAR = value  # Only if "as VAR" is present
            BLOCK
        except:
            # The exceptional case is handled here
            exc = False
            if not exit(mgr, *sys.exc_info()):
                raise
            # The exception is swallowed if exit() returns true
    finally:
        # The normal and non-local-goto cases are handled here
        if exc:
            exit(mgr, None, None, None)


    为什么这么复杂呢?注意finally中的代码,需要BLOCK被执行后才会执行finally的清理工作,因为当EXPR执行时抛出异常,访问mgr.exit执行就会报AttributeError的错误。


    二、实现方式

    根据前面对with的翻译可以看到,被with求值的对象必须有一个__enter__方法和一个__exit__方法。稍微看一个文件读取的例子吧,注意在这里我们要解决2个问题:文件读取异常,读取完毕后关闭文件句柄。用try…except一般会这样写:

    复制代码 代码如下:

    f = open('/tmp/tmp.txt')
    try:
        for line in f.readlines():
            print(line)
    finally:
        f.close()


    注意我们这里没有处理文件打开失败的IOError,上面的写法可以正常工作,但是对于每个打开的文件,我们都要手动关闭文件句柄。如果要使用with来实现上述功能,需要需要一个代理类:

    复制代码 代码如下:

    class opened(object):

        def __init__(self, name):
            self.handle = open(name)

        def __enter__(self):
            return self.handle

        def __exit__(self, type, value, trackback):
            self.handle.close()

    with opened('/tmp/a.txt') as f:
        for line in f.readlines():
            print(line)


    注意我们定了一个名字叫opened的辅助类,并实现了__enter__和__exit__方法,__enter__方法没有参数,__exit__方法的3个参数,分别代表异常的类型、值、以及堆栈信息,如果没有异常,3个入参的值都为None。

    如果你不喜欢定义class,还可以用Python标准库提供的contextlib来实现:

    复制代码 代码如下:

    from contextlib import contextmanager

    @contextmanager
    def opened(name):
        f = open(name)
        try:
            yield f
        finally:
            f.close()

    with opened('/tmp/a.txt') as f:
        for line in f.readlines():
            print(line)


    使用contextmanager的函数,yield只能返回一个参数,而yield后面是处理清理工作的代码。在我们读取文件的例子中,就是关闭文件句柄。这里原理上和我们之前实现的类opened是相同的,有兴趣的可以参考一下contextmanager的源代码。

    三、应用场景

    废话了这么多,那么到底那些场景下该使用with,有没有一些优秀的例子?当然啦,不然这篇文章意义何在。以下摘自PEP 343。

    一个确保代码执行前加锁,执行后释放锁的模板:

    复制代码 代码如下:

    @contextmanager
        def locked(lock):
            lock.acquire()
            try:
                yield
            finally:
                lock.release()

        with locked(myLock):
            # Code here executes with myLock held.  The lock is
            # guaranteed to be released when the block is left (even
            # if via return or by an uncaught exception).


    数据库事务的提交和回滚:

    复制代码 代码如下:

    @contextmanager
            def transaction(db):
                db.begin()
                try:
                    yield None
                except:
                    db.rollback()
                    raise
                else:
                    db.commit()


    重定向stdout:

    复制代码 代码如下:

    @contextmanager
    def stdout_redirected(new_stdout):
        save_stdout = sys.stdout
        sys.stdout = new_stdout
        try:
            yield None
        finally:
            sys.stdout = save_stdout

    with opened(filename, "w") as f:
        with stdout_redirected(f):
            print "Hello world"


    注意上面的例子不是线程安全的,再多线程环境中要小心使用。


    四、总结

    with是对try…expect…finally语法的一种简化,并且提供了对于异常非常好的处理方式。在Python有2种方式来实现with语法:class-based和decorator-based,2种方式在原理上是等价的,可以根据具体场景自己选择。

    with最初起源于一种block…as…的语法,但是这种语法被很多人所唾弃,最后诞生了with,关于这段历史依然可以去参考PEP-343和PEP-340

  • 相关阅读:
    安装 windows 2008 解决 gpt 分区问题
    you have not created a boot efi partition
    echarts gauge 仪表盘去除外发光效果
    上国际网络——通过配置host
    juery 选择器 选择多个元素
    html5 <input> placeholder 属性
    PHP——字符串统一转码为GBK,自动判断是否UTF8并转码
    sublime text2 解决中文乱码
    PHP超级全局变量——Session 变量
    js为元素添加onclick事件
  • 原文地址:https://www.cnblogs.com/liunnis/p/4652567.html
Copyright © 2011-2022 走看看