令我遗憾的是,我无法弄清楚如何处理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方式处理'with'语句内部的失败,我该怎么做?
答案 0 :(得分:214)
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()
答案 1 :(得分:63)
使用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()
使用如下:
with opened_w_error("/etc/passwd", "a") as (f, err):
if err:
print "IOError:", err
else:
f.write("guido::0:0::/:/bin/sh\n")
答案 2 :(得分:49)
使用Python'with'语句
时捕获异常
在没有__future__
导入since Python 2.6的情况下,可以使用with语句。您可以将其作为early as 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中,块除外:
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
答案 3 :(得分:2)
with
语句引发的异常的可能起源区分with
语句中发生的异常非常棘手,因为它们可能起源于不同的地方。可以从以下任一位置(或其中调用的函数)引发异常:
ContextManager.__init__
ContextManager.__enter__
with
的正文ContextManager.__exit__
有关更多详细信息,请参见关于Context Manager Types的文档。
如果我们想区分这些不同的情况,仅将with
包装到try .. except
中是不够的。考虑以下示例(使用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
棘手的部分是区分源自BLOCK
和__exit__
的异常,因为逃避with
主体的异常将传递给__exit__
,后者可以决定如何处理(请参见the docs)。但是,如果__exit__
自身出现,则原始异常将被新异常替换。为了处理这些情况,我们可以在except
的主体中添加一个通用的with
子句,以存储任何可能会被忽略的潜在异常,并将其与最外层{{1 }}-如果它们相同,则意味着原点是except
或其他地方是BLOCK
(以防__exit__
通过在最外面的{{1 }}将不会被执行)。
__exit__
PEP 343 -- The "with" Statement指定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)
包装各个部分,从而区分出不同的潜在错误源:
with
这种特殊异常处理的需求应该非常少,通常将整个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)
块中就足够了。尤其是如果各种错误源由不同的(自定义)异常类型指示(需要相应地设计上下文管理器),我们可以很容易地区分它们。例如:
with
答案 4 :(得分:-3)
采用标准异常处理
try:
with open("a.txt") as f:
#business as usual
except Exception as e:
print "oops, handle exception: ", e