我有一些测试用例。测试用例依赖于需要时间来计算的数据。为了加快测试速度,我已经缓存了数据,因此无需重新计算。
我现在有foo()
,它会查看缓存的数据。我不能提前告诉它会看到什么,因为这在很大程度上取决于测试用例。
如果测试用例失败导致它找不到正确的缓存数据,我不希望它失败 - 我希望它计算数据,然后再试一次。我也不知道特别是什么异常会导致丢失数据。
我的代码现在看起来像这样:
if cacheExists:
loadCache()
dataComputed = False
else:
calculateData()
dataComputed = True
try:
foo()
except:
if not dataComputed:
calculateData()
dataComputed = True
try:
foo()
except:
#error handling code
else:
#the same error handling code
重新构建此代码的最佳方法是什么?
答案 0 :(得分:4)
我不同意现有答案中的关键建议,这基本上归结为在Python中处理异常,就像在C ++或Java中那样 - 这不是Python中的首选样式,通常是古老的想法, “请求宽恕比允许更好”(尝试操作并处理异常,如果有的话,而不是模糊代码的主流并通过彻底的初步检查产生开销)。我同意Gabriel的说法,一个简单的except
几乎不是一个好主意(除非它所做的只是某种形式的记录,然后是raise
让异常传播)。所以,假设您有一个元组,其中包含您期望并希望以相同方式处理的所有异常类型,例如:
expected_exceptions = KeyError, AttributeError, TypeError
始终使用except expected_exceptions:
而不是except:
。
因此,如果不采用这种方法,可以根据您的需求略微重复一点:
try:
foo1()
except expected_exceptions:
try:
if condition:
foobetter()
else:
raise
except expected_exceptions:
handleError()
另一种方法是使用辅助函数来包装try / except逻辑:
def may_raise(expected_exceptions, somefunction, *a, **k):
try:
return False, somefunction(*a, **k)
except expected_exceptions:
return True, None
这样的帮助器在几种不同的情况下可能经常有用,所以在项目的“实用程序”模块中的某个地方有这样的东西是很常见的。现在,对于您的情况(没有参数,没有结果),您可以使用:
failed, _ = may_raise(expected_exceptions, foo1)
if failed and condition:
failed, _ = may_raise(expected_exceptions, foobetter)
if failed:
handleError()
我认为它更线性,因此更简单。这种通用方法的唯一问题是,诸如may_raise
之类的辅助功能不会强迫您以某种方式处理异常,因此可能只是忘记这样做(只是比如使用返回代码而不是异常来表示错误,很容易错误地忽略那些返回值);所以,请谨慎使用......! - )
答案 1 :(得分:1)
使用一揽子例外通常不是一个好主意。你期待什么样的例外?它是KeyError,AttributeError,TypeError ......
一旦确定了您正在寻找的错误类型,您就可以使用hasattr()
或in
运算符之类的内容或许多其他可在您处理之前测试您的情况的内容有例外。
这样你就可以清理你的逻辑流程,并为那些真正破坏的事物保存你的异常处理!
答案 2 :(得分:1)
有时没有很好的表达流程的方法,它只是复杂的。但是这里只能在一个地方调用foo(),并且只在一个地方进行错误处理:
if cacheExists:
loadCache()
dataComputed = False
else:
calculateData()
dataComputed = True
while True:
try:
foo()
break
except:
if not dataComputed:
calculateData()
dataComputed = True
continue
else:
#the error handling code
break
你可能不喜欢循环,YMMV ......
或者:
if cacheExists:
loadCache()
dataComputed = False
else:
calculateData()
dataComputed = True
done = False
while !done:
try:
foo()
done = True
except:
if not dataComputed:
calculateData()
dataComputed = True
continue
else:
#the error handling code
done = True
答案 3 :(得分:1)
我喜欢Alex Martelli提出的替代方法。
您如何使用函数列表作为may_raise的参数。这些函数将被执行直到成功!
这是代码
def foo(x): raise Exception("Arrrgh!") return 0 def foobetter(x): print "Hello", x return 1 def try_many(functions, expected_exceptions, *a, **k): ret = None for f in functions: try: ret = f(*a, **k) except expected_exceptions, e: print e else: break return ret print try_many((foo, foobetter), Exception, "World")
结果是
Arrrgh! Hello World 1
答案 4 :(得分:0)
有没有办法告诉你是否想在拨打电话前做foobetter()?如果你得到一个例外,那应该是因为发生了意想不到的事情(例外!)。不要使用异常进行流量控制。