我不知道如何处理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'

在try/except语句中包含with也不起作用,并且不会引发异常。我能做什么,以处理失败的语句在一个Pythonic的方式?


from __future__ import with_statement

try:
    with open( "a.txt" ) as f :
        print f.readlines()
except EnvironmentError: # parent of IOError, OSError *and* WindowsError where available
    print 'oops'

如果你想要不同的处理错误从开放调用和工作代码,你可以这样做:

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

做到这一点的最佳“python”方式是利用with语句,PEP 343中的示例#6列出了该语句的背景。

@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")

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

自Python 2.6以来,with语句已经可以在没有__future__导入的情况下使用。你可以早在Python 2.5(但在这一点上,是时候升级了!)通过:

from __future__ import with_statement

这是你能纠正的最接近的东西。差不多了,但是with没有except从句:

用open("a.txt")作为f: print (f.readlines ()) Except: # <- with没有Except子句。 print(哦)

上下文管理器的__exit__方法,如果它返回False,将在它结束时重新引发错误。如果它返回True,它将抑制它。开放内置的__exit__不返回True,所以你只需要在try中嵌套它,除了block:

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

区分复合with语句引发的异常的可能来源

区分with语句中出现的异常非常棘手,因为它们可能起源于不同的地方。可以从以下位置(或其中调用的函数)引发异常:

ContextManager.__init__ ContextManager.__enter__ 身体的配合 ContextManager.__exit__

有关更多详细信息,请参阅关于上下文管理器类型的文档。

如果我们想要区分这些不同的情况,只需将with包装成一个try ..“除了”是不够的。考虑以下示例(使用ValueError作为示例,但当然它可以用任何其他异常类型代替):

try:
    with ContextManager():
        BLOCK
except ValueError as err:
    print(err)

在这里,except将捕获来自所有四个不同位置的异常,因此不允许对它们进行区分。如果我们将上下文管理器对象的实例化移到with对象之外,我们可以区分__init__和BLOCK / __enter__ / __exit__:

try:
    mgr = ContextManager()
except ValueError as err:
    print('__init__ raised:', err)
else:
    try:
        with mgr:
            try:
                BLOCK
            except TypeError:  # catching another type (which we want to handle here)
                pass
    except ValueError as err:
        # At this point we still cannot distinguish between exceptions raised from
        # __enter__, BLOCK, __exit__ (also BLOCK since we didn't catch ValueError in the body)
        pass

实际上,这只是帮助了__init__部分,但我们可以添加一个额外的哨兵变量来检查with对象的主体是否已经开始执行(即区分__enter__和其他变量):

try:
    mgr = ContextManager()  # __init__ could raise
except ValueError as err:
    print('__init__ raised:', err)
else:
    try:
        entered_body = False
        with mgr:
            entered_body = True  # __enter__ did not raise at this point
            try:
                BLOCK
            except TypeError:  # catching another type (which we want to handle here)
                pass
    except ValueError as err:
        if not entered_body:
            print('__enter__ raised:', err)
        else:
            # At this point we know the exception came either from BLOCK or from __exit__
            pass

The tricky part is to differentiate between exceptions originating from BLOCK and __exit__ because an exception that escapes the body of the with will be passed to __exit__ which can decide how to handle it (see the docs). If however __exit__ raises itself, the original exception will be replaced by the new one. To deal with these cases we can add a general except clause in the body of the with to store any potential exception that would have otherwise escaped unnoticed and compare it with the one caught in the outermost except later on - if they are the same this means the origin was BLOCK or otherwise it was __exit__ (in case __exit__ suppresses the exception by returning a true value the outermost except will simply not be executed).

try:
    mgr = ContextManager()  # __init__ could raise
except ValueError as err:
    print('__init__ raised:', err)
else:
    entered_body = exc_escaped_from_body = False
    try:
        with mgr:
            entered_body = True  # __enter__ did not raise at this point
            try:
                BLOCK
            except TypeError:  # catching another type (which we want to handle here)
                pass
            except Exception as err:  # this exception would normally escape without notice
                # we store this exception to check in the outer `except` clause
                # whether it is the same (otherwise it comes from __exit__)
                exc_escaped_from_body = err
                raise  # re-raise since we didn't intend to handle it, just needed to store it
    except ValueError as err:
        if not entered_body:
            print('__enter__ raised:', err)
        elif err is exc_escaped_from_body:
            print('BLOCK raised:', err)
        else:
            print('__exit__ raised:', err)

使用PEP 343中提到的等效形式的替代方法

PEP 343——“with”语句指定with语句的等效“非with”版本。在这里,我们可以很容易地用try…除了,从而区分不同的潜在错误来源:

import sys

try:
    mgr = ContextManager()
except ValueError as err:
    print('__init__ raised:', err)
else:
    try:
        value = type(mgr).__enter__(mgr)
    except ValueError as err:
        print('__enter__ raised:', err)
    else:
        exit = type(mgr).__exit__
        exc = True
        try:
            try:
                BLOCK
            except TypeError:
                pass
            except:
                exc = False
                try:
                    exit_val = exit(mgr, *sys.exc_info())
                except ValueError as err:
                    print('__exit__ raised:', err)
                else:
                    if not exit_val:
                        raise
        except ValueError as err:
            print('BLOCK raised:', err)
        finally:
            if exc:
                try:
                    exit(mgr, None, None, None)
                except ValueError as err:
                    print('__exit__ raised:', err)

通常一个简单的方法就可以了

这种特殊异常处理的需要应该是相当罕见的,通常在一个try…Except块就足够了。特别是当各种错误源由不同的(自定义的)异常类型表示时(上下文管理器需要相应地设计),我们可以很容易地区分它们。例如:

try:
    with ContextManager():
        BLOCK
except InitError:  # raised from __init__
    ...
except AcquireResourceError:  # raised from __enter__
    ...
except ValueError:  # raised from BLOCK
    ...
except ReleaseResourceError:  # raised from __exit__
    ...

我在我做的一个程序中使用了这个:

try:
    with open(os.path.join(basedir, "rules.txt")) as f:
        self.rules.setText(f.read())
except FileNotFoundError:
    self.rules.setText("Sorry, unable to read rules")