About
RSS

Bit Focus


Python: try finally with 简介

Posted at 2011-06-12 11:18:54 | Updated at 2021-09-20 01:28:32

    用 Python 做一件很平常的事情: 打开文件, 逐行读入, 最后关掉文件; 进一步的需求是, 这也许是程序中一个可选的功能, 如果有任何问题, 比如文件无法打开, 或是读取出错, 那么在函数内需要捕获所有异常, 输出一行警告并退出. 代码可能一开始看起来是这样的
def read_file():
    try:
        f = open('yui', 'r')
        print ''.join(f.readlines())
    except:
        print 'error occurs while reading file'
    finally:
        f.close()
    不过这显然无法运作, 因为 f 是在 try 块中定义的, 而在 finally 中无法引用.

    如果将 f 提取到 try 块外部, 如
def read_file():
    f = open('azusa', 'r')
    try:
        print ''.join(f.readlines())
    except:
        print 'error occurs while reading file'
    finally:
        f.close()
那么, 问题在于当打开文件失败, 抛出异常将不会被捕获.

    挫一点的方法自然是, 再套一层 try
def read_file():
    try:
        f = open('sawako', 'r')
        try:
            print ''.join(f.readlines())
        except:
            print 'error occurs while reading file'
        finally:
            f.close()
    except:
        print 'error occurs while reading file'
    当然这不仅仅是多一层缩进挫了, 连警告输出都白白多一次呢.

    正规一点的方式是, 使用 Python 引入的 with 结构来解决, 如
def readFile():
    try:
        with open('mio', 'r') as f:
            print ''.join(f.readlines())
    except:
        print 'error occurs while reading file'
    当文件打开失败时, 异常自然会被 except 到; 否则, 在 with 块结束之后, 打开的文件将自动关闭.

    除了打开文件, 还有其它这样可以用于 with 的东西么? 或者说, 怎么自定义一个什么东西, 让它能用于 with 呢?
    直接回答后一个问题吧, 秘密在于 Python 虚拟机在 with 块退出时会去寻找对象的 __exit__ 方法并调用它, 把释放资源的动作放在这个 __exit__ 函数中就可以了; 另外, 对象还需要一个 __enter__ 函数, 当进入 with 块时, 这个函数被调用, 而它的返回值将作为 as 后引用的值. 一个简单的例子是
class Test:
    def __init__(self):
        print 'init'

    def __enter__(self):
        print 'enter'
        return self

    def __exit__(self, except_type, except_obj, tb):
        print except_type
        print except_obj
        import traceback
        print ''.join(traceback.format_tb(tb))
        print 'exit'
        return True

with Test() as t:
    raise ValueError('kon!')
    执行这一段代码, 输出将会是
init
enter
<type 'exceptions.ValueError'>
kon!
  File "test.py", line 17, in <module>
    raise ValueError('kon!')

exit
    __exit__ 函数接受三个参数, 分别是异常对象类型, 异常对象和调用栈. 如果 with 块正常退出, 那么这些参数将都是 None. 返回 True 表示发生的异常已被处理, 不再继续向外抛出.

    简单的介绍到此为止, 详细的情况可以参考 PEP 343 (这数字真不错, 73).

Post tags:   Exception Handling  Python  RAII

Leave a comment:




Creative Commons License Your comment will be licensed under
CC-NC-ND 3.0


. Back to Bit Focus
NijiPress - Copyright (C) Neuron Teckid @ Bit Focus
About this site