我想知道是否有一种Pythonic方法可以处理长时间运行的函数中的错误,这些函数可能会出现部分错误而不会影响函数继续运行的能力。
作为示例,考虑一个给出URL列表的函数,它递归地检索顶级URL路径下的资源和所有链接资源。它将检索到的资源存储在本地文件系统中,其目录结构镜像URL结构。基本上,这是页面列表的基本递归wget。
此功能可能会失败的点很多:
检索或保存任何一个资源失败只会影响该函数继续处理该资源的能力以及可能与之链接的任何子资源,但可以继续检索其他资源。
错误处理的一个简单模型是,在第一个错误上,会引发一个适当的异常以供调用者处理。这个问题是它终止了函数并且不允许它继续。错误可能已修复,函数从头开始重新启动,但这会导致重做工作,任何永久性错误都可能意味着我们永远无法完成。
我想到的几个选择是:
在Python讨论中,我经常注意到某些方法被描述为Pythonic或非Pythonic。我想知道是否有任何特别的Pythonic方法来处理上述场景类型。
Python是否包含任何模型,比异常处理的终止模型更复杂的错误处理模型,或者更复杂的电池是否使用我应该复制以保留Pythonic的错误处理模型?
注意:请不要专注于示例。我不打算在那个特定的空间里解决问题,但这似乎是一个很好的例子,大多数人都会对此有所了解。
答案 0 :(得分:6)
我不认为你在这里谈论的水平上有一个特别清晰的“Pythonic / non-Pythonic”区别。
在这个领域没有“一刀切”解决方案的一个重要原因是,你想要的确切语义将是特定于问题的。
即使支持错误处理程序也不一定涵盖所有这些所需的行为 - 简单的每次失败错误处理程序不能轻易提供中止和回滚语义,或者最终生成单个异常。 (这不是不可能的 - 你只需要处理诸如传递绑定方法或闭包作为错误处理程序的技巧)
因此,您可以做的最好的事情就是在面对错误时对典型的使用场景和理想行为进行有根据的猜测,并相应地设计您的API。
一个完全通用的解决方案将接受一个错误处理程序,该错误处理程序在发生时发生每个失败,并且最终发生“错误”处理程序,使调用者有机会决定如何处理多个错误(允许一些协议)要从各个错误处理程序传递到最终批处理错误处理程序的数据。)
但是,提供这样的通用解决方案很可能是API设计失败。 API的设计者不应该害怕对如何使用API以及如何处理错误发表意见。要记住的主要事情是不要过度设计你的解决方案:
作为一般原则,你可能得到的最好的结果是“Pythonic”错误处理将尽可能简单,但并不简单。但就此而言,这个词只是被用作“好代码”的同义词,而这并不是它的意图。
另一方面,谈论非Pythonic错误处理的实际形式可能会稍微容易一些:
def myFunction(an_arg, error_handler)
# Do stuff
if err_occurred:
if isinstance(err, RuntimeError):
error_handler.handleRuntimeError()
elif isinstance(err, IOError):
error_handler.handleIOError()
Pythonic成语是错误处理程序,如果支持的话,只是简单的callables。向他们提供他们决定如何处理这种情况所需的信息,而不是试图为他们做出太多决定。如果您希望更容易实现错误处理的常见方面,那么提供一个单独的帮助器类,使用__call__
方法执行调度,以便人们可以决定是否要使用它(或者如何当他们使用它时他们想要覆盖多少)。这不完全是特定于Python的,但是来自语言的东西,这使得传递任意可调用的烦恼(例如Java,C,C ++)变得非常困难。因此,复杂的错误处理协议绝对是进入“非Pythonic错误处理”领域的一种方式。
上述非Pythonic代码中的另一个问题是没有提供默认处理程序。迫使每个API用户做出他们可能尚未配备的决定只是糟糕的API设计。但现在我们回到了一般的“好代码”/“坏代码”领域,所以不应该用Pythonic /非Pythonic来描述差异。
答案 1 :(得分:0)
错误处理应依赖于异常和日志记录,因此对于每个错误都会引发异常并记录错误消息。
然后在任何调用者函数级别捕获异常,如果需要,记录任何其他附加错误并处理该问题。
如果问题没有得到完全处理,那么re-raise the exception again以便上层可以捕获相同的异常并执行不同的操作。
在任何这些阶段中,您都可以对某些类型的异常进行计数,以便只有在出现特定数量的问题时才能执行某些操作。