【问题标题】:In case of an exception during a loop: How to return the intermediate result before passing on the exception?如果在循环期间发生异常:如何在传递异常之前返回中间结果?
【发布时间】:2020-09-23 04:14:54
【问题描述】:
def store(self) -> list:
    result = []
    for url in self.urls():
        if url.should_store():
            stored_url = self.func_that_can_throw_errors(url)
            if stored_url: result.append(stored_url)
    return result

前言:不是实际的方法名称。选择愚蠢的名字来强调

在循环过程中可能会发生错误。在这种情况下,我希望store() 返回中间结果,并且仍然引发原始异常以便在不同的地方进行处理。

做类似的事情

try:
    <accumulating results ... might break>
except Exception:
    return result
    raise

很遗憾没有做到这一点,因为通常不会达到 raise stmt(因此返回一个空列表)。

你们有关于如何丢失中间结果的建议吗?

非常感谢 - 干杯!

【问题讨论】:

  • 据我所知,这是不可能的,你不能既抛出异常又返回一个值,控制流明智你必须做一个或另一个。您可能会做一些事情,比如返回一个标记的联合 - 一种情况是异常值,另一种是实际值(可能是“迄今为止的结果”和异常的第三种情况)。或者创建一个自定义异常,其中还包含“迄今为止的结果”。
  • 在 Python 中,您可能会返回一个包含异常和“迄今为止的结果”的元组,而不是标记的联合。尽管该函数的任何调用者都应该注意检查返回值的形式是否是什么。我不知道 Python reg 中的惯用语是什么。那个。
  • Python 中最好的方法可能是总是返回一个元组,让第一部分总是(中间/完整)结果(如果有),第二部分总是抛出异常(如果有)。这确实要求函数的调用者始终必须小心检查各种可能性 reg。函数的返回值,比如只有一个结果,只有一个异常,既是结果又是异常。
  • 啊,让我想起了 Go,它似乎很常见,结果是 err = func() 语句。在这种特殊情况下对我来说不利的是,我需要调整 store() 的每次调用,并且由于我希望无论如何都会抛出原始异常,因此调用语句必须以某种方式确保任何给定的异常是在处理(中间)结果后实际提出。嗯,很高兴有这样的对话。我觉得还有其他问题出现...谢谢! :))
  • 是的...要使用中间结果,我必须用 try-except 包围每个 .store() 语句,以便处理其中的结果。

标签: python list exception accumulator


【解决方案1】:

这不可能像你想象的那样。您不能引发异常并返回值。

所以我认为你所要求的是一种解决方法。在那里,我看到了两种可能性:

  1. 根据实际返回值返回标志/异常:

返回标志:

except Exception:
    return result, False

False 是指示出现问题的标志

返回异常:

except Exception as e:
    return result, e
  1. 既然看起来 store 是某个类的方法,您可以引发异常并通过第二次调用检索中间结果,如下所示:
def store(self):
    result = []
    try:
        # something
    except Exception:
        self.intermediary_result = result
        raise

def retrieve_intermediary(self):
    return self.intermediary_result

【讨论】:

  • 好主意。尚未考虑将结果存储在实例本身中 - 很好看!
  • 我最终选择了使用这个。更像OO,也不需要大的改变。谢谢阿塔洛斯!
  • 很高兴为您提供帮助:)
【解决方案2】:

鉴于我对 Python 的了解有限,我能想到的最佳答案是始终返回一对,其中第一部分是结果,第二部分是可选的异常值。

def store(self) -> list:
    '''
    TODO: Insert documentation here.

    If an error occurs during the operation, a partial list of results along with
    the exception value will be returned.

    :return A tuple of [list of results, exception]. The exception part may be None.
    '''

    result = []

    for url in self.urls():

        if url.should_store():
            try:
                stored_url = self.func_that_can_throw_errors(url)
            except Exception as e:
                return result, e

            if stored_url: result.append(stored_url)

    return result, None

也就是说,正如您所提到的,如果您在代码中的多个位置有此调用,则必须小心在所有相关位置进行更改,并可能更改处理方式。类型检查在那里可能会有所帮助,尽管我对 Python 的类型提示的了解非常有限。

【讨论】:

  • 看起来很合理。关于异常处理,如果您返回异常 e,您可能需要确保在引发 e 时保留堆栈跟踪。请参阅stackoverflow.com/questions/8760267/… 结合它,元组解决方案也是一个不错的解决方案。
【解决方案3】:

同时,我的想法是只使用一个累加器,这似乎是目前“最快”的修复方法,在调用 store() 的项目中更改量最少。 (中间)结果在任何地方都不需要(假设它是可选的)。所以...

我想和你分享:

    def store(self, result_accu=None) -> list:
        if result_accu is None:
            result_accu = []
        for url in self.urls():
            if url.should_store():
                stored_url = self.func(url)
                if stored_url: result_accu.append(stored_url)
        return result_accu

仍然返回一个列表,但可以通过在 accu 列表上的引用访问中间结果。 将参数设为可选可以让项目中的大多数语句保持原样,因为结果并不需要任何地方。

store() 是某种命令,其中大部分数据完整性工作已经在其中完成。结果现在很好。

但是你们也让我注意到无论如何处理中间结果都需要做一些工作。谢谢! @attalos @MelvinWM

【讨论】:

  • 同时我选择了 attalos more OO 解决方案,见上文。
猜你喜欢
  • 2018-12-30
  • 1970-01-01
  • 1970-01-01
  • 2010-10-30
  • 1970-01-01
  • 2023-03-24
  • 1970-01-01
  • 2011-01-08
  • 1970-01-01
相关资源
最近更新 更多