[英]Catching an exception while using a Python 'with' statement
To my shame, I can't figure out how to handle exception for python 'with' statement. 令我遗憾的是,我无法弄清楚如何处理python'with'语句的异常。 If I have a code:
如果我有一个代码:
with open("a.txt") as f:
print f.readlines()
I really want to handle 'file not found exception' in order to do somehing. 我真的想处理'文件未找到异常'以便进行处理。 But I can't write
但我不能写
with open("a.txt") as f:
print f.readlines()
except:
print 'oops'
and can't write 并且不能写
with open("a.txt") as f:
print f.readlines()
else:
print 'oops'
enclosing 'with' in a try/except statement doesn't work else: exception is not raised. 在try / except语句中包含'with'不起作用:不引发异常。 What can I do in order to process failure inside 'with' statement in a Pythonic way?
为了以Pythonic方式处理'with'语句内部的失败,我该怎么办?
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'
If you want different handling for errors from the open call vs the working code you could do: 如果您希望对打开调用与工作代码中的错误进行不同的处理,则可以执行以下操作:
try:
f = open('foo.txt')
except IOError:
print('error')
else:
with f:
print f.readlines()
The best "Pythonic" way to do this, exploiting the with
statement, is listed as Example #6 in PEP 343 , which gives the background of the statement. 使用
with
语句的最佳“Pythonic”方法在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()
Used as follows: 使用如下:
with opened_w_error("/etc/passwd", "a") as (f, err):
if err:
print "IOError:", err
else:
f.write("guido::0:0::/:/bin/sh\n")
Catching an exception while using a Python 'with' statement
使用Python'with'语句时捕获异常
The with statement has been available without the __future__
import since Python 2.6 . 自Python 2.6以来 ,with语句在没有
__future__
导入的情况下可用。 You can get it as early as Python 2.5 (but at this point it's time to upgrade!) with: 您可以早在Python 2.5中获得它(但此时需要升级!):
from __future__ import with_statement
Here's the closest thing to correct that you have. 这是你最接近纠正的事情。 You're almost there, but
with
doesn't have an except
clause: 你几乎没有,但
with
没有一个except
条款:
with open("a.txt") as f: print(f.readlines()) except: # <- with doesn't have an except clause. print('oops')
A context manager's __exit__
method, if it returns False
will reraise the error when it finishes. 上下文管理器的
__exit__
方法,如果返回False
,则在完成时将重新__exit__
错误。 If it returns True
, it will suppress it. 如果它返回
True
,它将抑制它。 The open
builtin's __exit__
doesn't return True
, so you just need to nest it in a try, except block: open
builtin的__exit__
不会返回True
,所以你只需要在try中嵌套它,块除外:
try:
with open("a.txt") as f:
print(f.readlines())
except Exception as error:
print('oops')
And standard boilerplate: don't use a bare except:
which catches BaseException
and every other possible exception and warning. 和标准样板:不要使用bare
except:
它捕获BaseException
和其他所有可能的异常和警告。 Be at least as specific as Exception
, and for this error, perhaps catch IOError
. 至少与
Exception
一样具体,对于此错误,可能会捕获IOError
。 Only catch errors you're prepared to handle. 只捕捉你准备处理的错误。
So in this case, you'd do: 所以在这种情况下,你会这样做:
>>> try:
... with open("a.txt") as f:
... print(f.readlines())
... except IOError as error:
... print('oops')
...
oops
with
statement with
陈述产生的异常的可能来源 Differentiating between exceptions that occur in a with
statement is tricky because they can originate in different places. 区分
with
语句中出现的异常是棘手的,因为它们可能来自不同的地方。 Exceptions can be raised from either of the following places (or functions called therein): 可以从以下任一位置(或其中调用的函数)引发异常:
ContextManager.__init__
ContextManager.__enter__
with
with
ContextManager.__exit__
For more details see the documentation about Context Manager Types . 有关更多详细信息,请参阅有关Context Manager类型的文档。
If we want to distinguish between these different cases, just wrapping the with
into a try .. except
is not sufficient. 如果我们希望这些不同的情况加以区分,只是在包装
with
进入try .. except
是不够的。 Consider the following example (using ValueError
as an example but of course it could be substituted with any other exception type): 请考虑以下示例(使用
ValueError
作为示例,但当然可以使用任何其他异常类型替换):
try:
with ContextManager():
BLOCK
except ValueError as err:
print(err)
Here the except
will catch exceptions originating in all of the four different places and thus does not allow to distinguish between them. 这里
except
将捕获源自所有四个不同地方的例外,因此不允许区分它们。 If we move the instantiation of the context manager object outside the with
, we can distinguish between __init__
and BLOCK / __enter__ / __exit__
: 如果我们将上下文管理器对象的实例化移到
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
Effectively this just helped with the __init__
part but we can add an extra sentinel variable to check whether the body of the with
started to execute (ie differentiating between __enter__
and the others): 实际上这只是对
__init__
部分的帮助,但是我们可以添加一个额外的sentinel变量来检查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 ). 棘手的部分是区分源自
BLOCK
和__exit__
的异常,因为转义为with
的主体的异常将传递给__exit__
,后者可以决定如何处理它(参见文档 )。 If however __exit__
raises itself, the original exception will be replaced by the new one. 但是,如果
__exit__
升起,则原始异常将被新的异常替换。 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). 为了处理这些情况,我们可以在
with
的主体中添加一个通用的except
子句来存储任何潜在的异常,否则这些异常会被忽视,并将它与最外层捕获的异常进行比较, except
后面 - 如果它们是相同的,这意味着原点是BLOCK
,否则它是__exit__
(如果__exit__
通过返回一个真值__exit__
抑制异常,那么except
根本不执行之外)。
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 -- The "with" Statement specifies an equivalent "non-with" version of the with
statement. PEP 343 - “同向”的声明规定的等效“非与”版本
with
声明。 Here we can readily wrap the various parts with try ... except
and thus differentiate between the different potential error sources: 在这里,我们可以使用
try ... except
来包装各个部分,从而区分不同的潜在错误来源:
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)
The need for such special exception handling should be quite rare and normally wrapping the whole with
in a try ... except
block will be sufficient. 需要这样的特殊的异常处理应该是很罕见的,通常包裹整个
with
在try ... except
块就足够了。 Especially if the various error sources are indicated by different (custom) exception types (the context managers need to be designed accordingly) we can readily distinguish between them. 特别是如果各种错误源由不同的(自定义)异常类型指示(上下文管理器需要相应地设计),我们可以很容易地区分它们。 For example:
例如:
try:
with ContextManager():
BLOCK
except InitError: # raised from __init__
...
except AcquireResourceError: # raised from __enter__
...
except ValueError: # raised from BLOCK
...
except ReleaseResourceError: # raised from __exit__
...
resort to the standard exception handling 诉诸于标准的异常处理
try:
with open("a.txt") as f:
#business as usual
except Exception as e:
print "oops, handle exception: ", e
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.