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)
在这个特定的方法中,当发现错误时,我不想在这种情况下返回空列表,因为这可能是这个特定方法调用的真正答案,我想返回一些东西来指示参数是不正确的。 所以在这种情况下,我错误地返回了错误,而另一个列表(空或不)。
我的问题是,在这样的领域,最好的做法是什么,而不仅仅是列表?返回任何我想要的,并确保我logging它供用户阅读? :-)你们大多数人做什么:
- 如果成功,你应该返回正确或错误,你会发现一个错误?
- 如果成功,你应该返回一个列表,你会发现一个错误?
- 如果成功,你应该返回一个文件句柄,你会发现一个错误?
- 等等
首先,不pipe你做什么都不会返回结果和错误信息。 这是一个非常糟糕的方式来处理错误,并会导致你无休止的头痛。 如果您需要指出错误,则始终引发exception 。
除非有必要,否则我通常会避免提出错误。 在你的例子抛出一个错误是不是真的需要。 将空列表与非空列表相交不是错误。 结果只是空的清单,这是正确的。 但是,假设你想处理其他情况。 例如,如果方法得到一个非列表types。 在这种情况下,最好提出例外。 例外是没有什么可害怕的。
我的build议是查看类似函数的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
当然,有些情况下成功或失败的成功或失败可能是好的。 但是保持一致非常重要。 该函数应该始终返回相同的types或结构。 有一个函数可以返回一个列表或一个布尔值是非常混乱的。 或者返回相同的types,但是在错误的情况下,这个值的含义可以不同。
编辑:
OP说:
我想返回一些东西来表明参数不正确。
没有什么比说例外有更好的错误。 如果您想要指示参数不正确,请使用exception并input有用的错误消息。 在这种情况下返回结果只是令人困惑。 在其他情况下,你想表示没有发生任何事情,但这不是一个错误。 例如,如果您有一个方法从表中删除条目,并且请求删除的条目不存在。 在这种情况下,在成功或失败时返回True或False可能是正确的。 这取决于应用程序和预期的行为
抛出exception比返回特殊值更好。 这正是例外所devise的,用更健壮和结构化的error handling机制取代错误代码。
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)
在这个特定的情况下,虽然我可能会放弃testing。 交叉空列表没什么问题,真的。 另外, lambda
被排除在列表理解之外。 请参阅查找两个列表的交集? 有几种方法可以在不使用lambda
情况下编写。
例外情况肯定比状态回报更好(并且更加Pythonic)。 有关更多信息,请参阅: exception与状态返回
我喜欢返回一个元组:
(真的,some_result)
(假,some_useful_response)
some_useful_response对象可用于处理返回条件或可用于显示debugging信息。
注意 :这种技术适用于任何types的返回值 。 不应该把exception情况弄错。
在接收端,你只需要解压:
代码,Response = some_function(…)
该技术适用于“正常”控制stream程:当发生某些意外的input/处理时,必须使用exceptionfunction。
另外值得注意的是:这种技术有助于规范化函数返回。 程序员和函数的用户都知道应该期待什么 。
免责声明:我来自Erlang背景:-)
一般情况下是在特殊情况下抛出exception。 我希望能够记住确切的引用(或者是谁说的),但是你应该努力去接受尽可能多的价值和types,并且保持一个非常狭义的行为。 这是Nadia正在讨论的一个变种。 考虑你的function的以下用途:
-
intersect_two_lists(None, None)
-
intersect_two_lists([], ())
-
intersect_two_lists('12', '23')
-
intersect_two_lists([1, 2], {1: 'one', 2: 'two'})
-
intersect_two_lists(False, [1])
-
intersect_two_lists(None, [1])
我期望(5)抛出一个exception,因为传递False
是一个types错误。 然而,其余的人却有某种意义,但是这依赖于合同的function。 如果intersect_two_lists
被定义为返回两个迭代的交集,那么除了(5)之外的所有东西都应该工作,只要你使None
成为空集合的有效表示。 实施将是这样的:
def intersect_two_lists(seq1, seq2): if seq1 is None: seq1 = [] if seq2 is None: seq2 = [] if not isinstance(seq1, collections.Iterable): raise TypeError("seq1 is not Iterable") if not isinstance(seq2, collections.Iterable): raise TypeError("seq1 is not Iterable") return filter(...)
我通常编写帮助函数,强制执行任何合同,然后调用它们来检查所有的前提条件。 就像是:
def require_iterable(name, arg): """Returns an iterable representation of arg or raises an exception.""" if arg is not None: if not isinstance(arg, collections.Iterable): raise TypeError(name + " is not Iterable") return arg return [] def intersect_two_lists(seq1, seq2): list1 = require_iterable("seq1", seq1) list2 = require_iterable("seq2", seq2) return filter(...)
您也可以扩展这个概念,并将其作为可选parameter passing给“策略” 。 我不会build议这样做,除非你想拥抱基于策略的devise 。 我只是想提一下,以防万一以前没有看过这个选项。
如果intersect_two_lists
的契约是它只接受两个非空的list
参数,那么如果契约被违反,则是明确的并抛出exception:
def require_non_empty_list(name, var): if not isinstance(var, list): raise TypeError(name + " is not a list") if var == []: raise ValueError(name + " is empty") def intersect_two_lists(list1, list2): require_non_empty_list('list1', list1) require_non_empty_list('list2', list2) return filter(...)
我认为, 无论你做什么 ,这个故事的寓意都是一致的,而且是明确的 。 就个人而言,我通常赞成在违反合同时提出例外,或给予我实际上不能使用的价值。 如果我给出的价值是合理的,那么我试着做一些合理的回报。 您可能还想阅读有关exception的C ++ FAQ Lite条目 。 这个特殊的条目给你提供了一些关于例外的思考 。