Pythonic的错误条件上抛方法是什么?
我最近在做一个Python项目,这个项目变得有点大,里面有很多层的函数。因为早期的一些愚蠢决定,我发现需要修复很多崩溃的问题,因为底层的函数返回了我在高层函数中没有预料到的类型(通常是None)。
在我开始整理这些问题之前,我想知道在Python中,表示错误情况和在高层函数中处理这些错误的最合适的方法是什么?
我大部分时间的做法是,如果一个函数无法完成并返回预期的结果,我就返回None。这种做法有点麻烦,因为你必须在所有调用这个函数的地方都检查是否返回了None。
def lowLevel():
## some error occurred
return None
## processing was good, return normal result string
return resultString
def highLevel():
resultFromLow = lowLevel()
if not resultFromLow:
return None
## some processing error occurred
return None
## processing was good, return normal result string
return resultString
我想另一个解决方案可能是抛出异常。这样的话,调用的函数中仍然需要写很多代码来处理这个异常。
但似乎没有什么特别优雅的解决办法。其他人通常是怎么做的呢?在Objective-C中,一个常见的做法是通过引用返回一个错误参数,然后调用者去检查这个参数。
3 个回答
这不一定是特别符合Python风格的做法,但经验告诉我,遇到错误时要让它“静静地待着”。
也就是说,不要不必要地隐藏错误或者抛出不同的错误。
有时候,让被调用的函数出错比试图捕捉和隐藏各种错误情况要好。
显然,这个话题有点主观;但是如果你不隐藏错误或者抛出不同的错误,那么调试代码会简单很多,调用你函数或接口的人也更容易理解出了什么问题。
注意:这个回答并不完整——请查看评论。这个问答中提出的一些或所有答案可能应该以一种好的方式结合起来,清晰简洁地展示各种问题和解决方案。
好消息是,你已经找到了用返回值来表示错误情况的问题所在。
Exception
(异常)是处理问题的“Python风格”方法。你需要回答的问题是(我想你已经想过了):low_level
函数是否有有用的默认返回值?如果有,那就用这个返回值;如果没有,就抛出一个异常(比如 `ValueError`、`TypeError`,或者你自己定义的错误)。
然后,在调用栈的上层,也就是你知道如何处理这个问题的地方,捕获这个异常并进行处理。你不需要立刻捕获异常——如果 high_level
调用 mid-level
,而 mid-level
又调用 low_level
,那么在 mid-level
中没有任何 try/except
是可以的,让 high_level
来处理它。也许你能做的就是在程序的最上层加一个 try/except
,来捕获并记录所有未处理的错误,这也是可以的。
这其实要看你想怎么处理这个处理无法完成的情况。如果这些情况真的是普通流程中的例外,而且你需要进行一些清理工作、退出程序、发邮件等等,那么抛出异常就是你需要的做法。在这种情况下,处理这些异常的代码是必须的工作,基本上是不可避免的,虽然你可以把异常抛回上层,然后在一个地方处理这些错误,这样可以让代码看起来更整洁。
如果这些错误是可以接受的,那么一个优雅的解决方案就是使用空对象模式。与其返回None,不如返回一个模拟真实对象接口的对象,但这个对象什么也不做。这样,后续的代码就可以继续运行,而不会意识到发生了错误。这个模式的主要缺点是,它可能让你很难发现真正的错误,因为你的代码不会崩溃,但最终可能不会产生任何有用的结果。
在Python中,空对象模式的一个常见例子是,当你底层的函数没有返回任何结果时,返回一个空列表或字典。任何后续使用这个返回值并遍历元素的函数都会默默地继续执行,而不需要进行错误检查。当然,如果你要求列表至少有一个元素,那么这种方法就不适用了,你又得回到处理异常情况的老路上。