【问题标题】:Nice exception handling when re-trying code重试代码时很好的异常处理
【发布时间】:2010-11-23 12:47:47
【问题描述】:

我有一些测试用例。测试用例依赖于需要时间计算的数据。为了加快测试速度,我已经缓存了数据,这样就不必重新计算了。

我现在有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

重构这段代码的最佳方式是什么?

【问题讨论】:

  • 为什么要检查第一个条件除外?从你的下一次尝试/例外开始,然后在第二次除外,将它打破到 handleError() 不是更有意义吗?
  • 哦,嗯..我明白你的意思。试试看
  • 不,因为如果条件为假,我将毫无例外地处理(因为它会被捕获)

标签: python exception exception-handling code-formatting


【解决方案1】:

我喜欢 Alex Martelli 提出的替代方法。

您如何看待使用函数列表作为 may_raise 的参数。函数会一直执行到一个成功为止!

这里是代码

定义 foo(x): 引发异常(“啊!”) 返回 0 def foobetter(x): 打印“你好”,x 返回 1 def try_many(functions, expected_exceptions, *a, **k): ret = 无 对于 f in 函数: 尝试: ret = f(*a, **k) 除了expected_exceptions,e: 打印 e 别的: 休息 返回 ret 打印 try_many((foo, foobetter), Exception, "World")

结果是

啊! 你好世界 1

【讨论】:

    【解决方案2】:

    有时没有很好的方式来表达流程,它只是很复杂。但是这里有一种只在一个地方调用 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】:

    我不同意现有答案中的关键建议,这基本上归结为在 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 之类的辅助函数不会强制您以某种方式处理异常,因此您可能只是忘记这样做(就像使用返回码而不是异常来指示错误,容易导致这些返回值被错误地忽略);所以,请谨慎使用它...!-)

    【讨论】:

    • 我喜欢这个主意。我可能会同意这样的事情。
    【解决方案4】:

    使用一揽子例外通常不是一个好主意。你在那里期待什么样的异常?是KeyError、AttributeError、TypeError吗……

    一旦您确定了您要查找的错误类型,您就可以使用hasattr()in 运算符之类的东西,或者在处理异常之前测试您的状况的许多其他东西。

    这样你就可以清理你的逻辑流程,并为真正坏掉的事情保存你的异常处理!

    【讨论】:

      【解决方案5】:

      有没有办法在拨打电话之前判断您是否要执行 foobetter() ?如果您遇到异常,那应该是因为发生了意外(异常!)。不要使用异常进行流量控制。

      【讨论】:

      • 这根本不是 Python 中异常的工作方式。语言运行时本身会为非异常事件抛出异常。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-10-25
      • 2021-08-21
      • 1970-01-01
      • 2016-07-14
      • 1970-01-01
      • 1970-01-01
      • 2011-11-11
      相关资源
      最近更新 更多