Python 在重新尝试代码时,异常处理非常好
我有一些测试用例。测试用例依赖于需要时间计算的数据。为了加快测试速度,我缓存了数据,这样就不必重新计算 我现在有了查看缓存数据的Python 在重新尝试代码时,异常处理非常好,python,exception,exception-handling,code-formatting,Python,Exception,Exception Handling,Code Formatting,我有一些测试用例。测试用例依赖于需要时间计算的数据。为了加快测试速度,我缓存了数据,这样就不必重新计算 我现在有了查看缓存数据的foo()。我不能提前知道它将看到什么,因为这在很大程度上取决于测试用例 如果一个测试用例由于找不到正确的缓存数据而失败,我不希望它失败——我希望它计算数据,然后再试一次。我也不知道它会引发什么异常,特别是导致数据丢失的原因 我现在的代码如下所示: if cacheExists: loadCache() dataComputed = False else
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
重新构造此代码的最佳方法是什么?在进行调用之前,是否有方法告诉您是否要执行foobetter()?如果您遇到异常,应该是因为发生了意外(异常!)。不要将异常用于流控制。使用一揽子异常通常不是一个好主意。你认为那里会出现什么样的例外情况?这是一个键错误,属性错误,类型错误 一旦确定了要查找的错误类型,您可以使用类似于
hasattr()
或中的操作符或许多其他东西来测试您的情况,然后再处理异常
这样,您就可以清理逻辑流,并将异常处理保存到真正发生故障的地方 > P>我不同意现有答案中的关键建议,基本上归结为处理Python中的异常,如C++或java,这不是Python中的首选样式,通常有一个好的老想法:“最好是请求原谅而不是许可”。(尝试一个操作并处理异常(如果有),而不是通过彻底的初步检查来模糊代码的主要流程并导致开销)。我同意Gabriel的观点,即除了
之外,一个简单的几乎从来都不是一个好主意(除非它所做的只是某种形式的日志记录,然后是一个提升
,让异常传播)。因此,假设您有一个包含所有异常类型的元组,并且您希望以相同的方式处理这些异常类型,例如:
expected_exceptions = KeyError, AttributeError, TypeError
并始终使用除预期的例外情况外:
而不是裸除:
因此,有了这些,一个稍微不那么重复的方法就是:
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
这样的辅助函数不会强迫您以某种方式处理异常,因此您可能会忘记这样做(就像使用返回码而不是异常来指示错误一样,这些返回值很容易被错误地忽略);因此,请谨慎使用…!-)有时没有很好的方法来表示流,这很复杂。但是这里有一种方法可以只在一个位置调用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
或:
我喜欢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")
def foo(x):
引发异常(“arrgh!”)
返回0
def(x):
打印“你好”,x
返回1
def try_MUN(函数、预期的_异常、*a、**k):
ret=无
对于函数中的f:
尝试:
ret=f(*a,**k)
除预期的例外情况外,e:
打印e
其他:
打破
回程网
打印try\u many((foo,foobetter),例外,“World”)
结果是
Arrrgh!
Hello World
1
啊!
你好,世界
1.
为什么你要在第一个例外中检查一个条件?从下一次尝试开始/然后在第二个例外中,打破它以处理错误()?哦,嗯…我明白你的意思。我会尝试它不,因为如果条件为假,我将毫无例外地处理(因为它将被捕获)这根本不是Python中异常的工作方式。语言运行库本身为非异常事件抛出异常。我喜欢这个想法。我可能会同意这样的想法。呵呵,这基本上是使用循环来创建延续/转到。有趣的想法
Arrrgh!
Hello World
1