在Python的'with'语句中捕获异常

409 投票
6 回答
329811 浏览
提问于 2025-04-15 11:05

我搞不清楚怎么处理Python中'with'语句的异常。如果我有一段代码:

with open("a.txt") as f:
    print f.readlines()

我真的想处理“找不到文件”的异常,以便可以做点什么。但是我不能写

with open("a.txt") as f:
    print f.readlines()
except:
    print 'oops'

也不能写

with open("a.txt") as f:
    print f.readlines()
else:
    print 'oops'

with放在try/except语句里也不管用,异常没有被抛出。我该怎么做才能以Python的方式在with语句中处理失败呢?

6 个回答

82

在使用Python的'with'语句时捕获异常

'with'语句从Python 2.6开始就可以使用,不需要额外的__future__导入。其实在Python 2.5的时候就可以用这个功能了(不过现在是时候升级了!)

from __future__ import with_statement

你现在的代码离正确只差一步,但with语句本身并没有except这个部分:

with open("a.txt") as f: 
    print(f.readlines())
except:                    # <- with doesn't have an except clause.
    print('oops')

上下文管理器的__exit__方法,如果返回False,在结束时会重新抛出错误。如果返回True,则会抑制这个错误。内置的open函数的__exit__方法不会返回True,所以你需要把它放在一个try和except的代码块里:

try:
    with open("a.txt") as f:
        print(f.readlines())
except Exception as error: 
    print('oops')

还有一点标准的注意事项:不要使用裸except:,因为它会捕获BaseException以及所有其他可能的异常和警告。至少要具体到Exception,对于这个错误,可能需要捕获IOError。只捕获你准备好处理的错误。

所以在这种情况下,你可以这样做:

>>> try:
...     with open("a.txt") as f:
...         print(f.readlines())
... except IOError as error: 
...     print('oops')
... 
oops
107

最“Pythonic”的做法是利用with语句,这在PEP 343的示例#6中有介绍,里面讲了这个语句的背景。

from contextlib import contextmanager

@contextmanager
def opened_w_error(filename, mode="r"):
    try:
        f = open(filename, mode)
    except IOError, err:
        yield None, err
    else:
        try:
            yield f, None
        finally:
            f.close()

用法如下:

with opened_w_error("/etc/passwd", "a") as (f, err):
    if err:
        print "IOError:", err
    else:
        f.write("guido::0:0::/:/bin/sh\n")
350

这个解决方案会把 with 块中的代码放在 try-except 语句之外。

try:
    f = open('foo.txt')
except FileNotFoundError:
    print('error')
else:
    with f:
        print f.readlines()

撰写回答