Python中错误和成功返回值的最佳实践
一般来说,假设你有一个像下面这样的函数。
def intersect_two_lists(self, list1, list2):
if not list1:
self.trap_error("union_two_lists: list1 must not be empty.")
return False
if not list2:
self.trap_error("union_two_lists: list2 must not be empty.")
return False
#http://bytes.com/topic/python/answers/19083-standard
return filter(lambda x:x in list1,list2)
在这个特定的函数中,当发现错误时,我不想返回一个空列表,因为这可能是这个函数调用的真实结果。我想返回一些东西来表示参数不正确。所以在这种情况下,我选择返回 False,而在没有错误的情况下返回一个列表(无论是空的还是有内容的)。
我的问题是,在这种情况下,最佳的做法是什么,不仅仅是针对列表?我可以随便返回任何东西,并确保有文档让用户阅读吗?:-) 你们大多数人是怎么做的:
- 如果成功时应该返回 True 或 False,但你捕获到了错误,该怎么处理?
- 如果成功时应该返回一个列表,但你捕获到了错误,该怎么处理?
- 如果成功时应该返回一个文件句柄,但你捕获到了错误,该怎么处理?
- 等等
7 个回答
我喜欢返回一个元组:
(True, some_result)
(False, some_useful_response)
这里的 some_useful_response 对象可以用来处理返回的状态,或者用来显示调试信息。
注意:这种方法适用于任何类型的 返回值。不要把它和 异常情况混淆了。
在接收这些返回值的时候,你只需要解包一下:
Code, Response = some_function(...)
这种方法适用于“正常”的控制流程:当出现一些意外的输入或处理时,应该使用异常处理功能。
还有一点值得注意:这种方法有助于规范化函数的返回值。程序员和函数的使用者都知道会得到什么。
免责声明:我来自Erlang背景 :-)
与其返回一个特殊的值,不如抛出一个异常。这是异常存在的目的,目的是用一种更强大、更有结构的方式来处理错误,而不是简单地用错误代码。
class IntersectException(Exception):
def __init__(self, msg):
self.msg = msg
def __str__(self):
return self.msg
def intersect_two_lists(self, list1, list2):
if not list1:
raise IntersectException("list1 must not be empty.")
if not list2:
raise IntersectException("list2 must not be empty.")
#http://bytes.com/topic/python/answers/19083-standard
return filter(lambda x:x in list1,list2)
不过在这个特定的情况下,我可能会直接放弃这些测试。其实,两个空列表相交并没有什么问题。而且现在大家不太推荐使用lambda
,更倾向于使用列表推导式。你可以看看如何找到两个嵌套列表的交集?,里面有几种不使用lambda
的方法。
首先,无论你做什么,都不要同时返回结果和错误信息。这是一种很糟糕的错误处理方式,会让你头疼不已。如果你需要表示发生了错误,最好是抛出一个异常。
我通常会避免抛出错误,除非真的有必要。在你的例子中,抛出错误其实并不需要。把一个空列表和一个非空列表进行交集运算并不是错误,结果只是一个空列表,这也是正确的。不过,假设你想处理其他情况,比如方法接收到的不是列表类型。在这种情况下,抛出异常会更好。异常并不可怕。
我建议你查看一下Python库中类似的函数,看看Python是如何处理这些特殊情况的。比如,可以看看集合中的交集方法,它通常比较宽容。这里我尝试将一个空集合和一个空列表进行交集运算:
>>> b = []
>>> a = set()
>>> a.intersection(b)
set([])
>>> b = [1, 2]
>>> a = set([1, 3])
>>> a.intersection(b)
set([1])
只有在必要时才会抛出错误:
>>> b = 1
>>> a.intersection(b)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'int' object is not iterable
当然,有些情况下返回True或False来表示成功或失败也是可以的。但保持一致性非常重要。函数应该始终返回相同类型或结构。如果一个函数可能返回列表或布尔值,那就会让人感到困惑。或者返回相同类型,但在错误情况下这个值的含义可能会不同。
编辑:
提问者说:
我想返回一些东西来表示参数不正确。
没有什么比抛出异常更能表示错误了。如果你想表示参数不正确,那就使用异常,并附上有用的错误信息。在这种情况下返回结果只会让人困惑。还有其他情况,你可能想表示没有发生任何事情,但这并不是错误。比如,如果你有一个方法是从表中删除条目,而请求删除的条目不存在。在这种情况下,返回True或False来表示成功或失败可能是可以的。这取决于应用程序和预期的行为。