在一般中,假设您有类似下面的方法。
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,否则返回一个列表(空或不)。
我的问题是,在这样的领域中最好的做法是什么,而不仅仅是列表?返回我想要的任何内容并确保我将其记录下来供用户阅读? :-)你们大多数人都做了什么:
答案 0 :(得分:56)
首先,无论你做什么都不会返回结果和错误信息。这是处理错误的一种非常糟糕的方法,会让你无休止的头痛。 如果您需要指出错误,请始终引发异常。
除非有必要,否则我通常会避免提出错误。在您的示例中,实际上不需要抛出错误。将空列表与非空列表相交并不是错误。结果只是空列表,这是正确的。但是,假设你想处理其他案件。例如,如果方法获得非列表类型。在这种情况下,最好提出异常。例外没有什么可担心的。
我的建议是查看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都可能是好的。但要保持一致非常重要。该函数应始终返回相同的类型或结构。拥有一个可以返回列表或布尔值的函数是非常令人困惑的。或者返回相同的类型,但如果出现错误,此值的含义可能会有所不同。
修改强>
OP说:
我想要回复一些事情 参数不正确。
没有什么比异常更好的错误。如果要指示参数不正确,请使用异常并输入有用的错误消息。在这种情况下返回结果只是令人困惑。在其他情况下,您可能希望指出没有发生任何事情,但这不是错误。例如,如果您有一个方法从表中删除条目,并且请求删除的条目不存在。在这种情况下,成功或失败时返回True或False可能没问题。这取决于应用程序和预期的行为
答案 1 :(得分:20)
raise an exception比返回特殊值更好。这正是设计的异常,用更健壮和结构化的错误处理机制替换错误代码。
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
来编写此代码,请参阅Find intersection of two lists?。
答案 2 :(得分:13)
我喜欢返回一个元组:
(是的,some_result)
(False,some_useful_response)
some_useful_response 对象可用于处理返回条件,也可用于显示调试信息。
注意:此技术适用于任何种类的返回值。它不应该与例外情况错误。
在接收端,您只需要解压缩:
代码,响应= some_function(...)
此技术适用于“正常”控制流:当发生某些意外输入/处理时,必须使用异常功能。
还值得注意:此技术有助于规范化函数返回。程序员和函数用户都知道会发生什么。
免责声明:我来自Erlang背景: - )
答案 3 :(得分:11)
例外肯定比状态返回更好(和更多Pythonic)。有关此问题的更多信息:Exceptions vs. status returns
答案 4 :(得分:3)
一般情况是针对特殊情况抛出异常。我希望我能记住确切的引用(或谁说出来),但你应该努力寻找能够接受尽可能多的值和类型的函数,并保持一个非常狭义的行为。这是Nadia was talking about的变体。请考虑以下函数用法:
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)抛出一个异常,因为传递False
是一个类型错误。然而,其余的都有某种意义,但它实际上取决于函数所述的契约。如果将intersect_two_lists
定义为返回两个iterables的交集,则只要您使None
成为空集<的有效表示,则除了(5)之外的所有内容都应该有效/ em>的。实现类似于:
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(...)
您还可以扩展此概念并将“policy”作为可选参数传递。除非你想拥抱Policy Based Design,否则我不建议这样做。我确实想提一下,以防你之前没有考虑过这个选项。
如果intersect_two_lists
的合同是它只接受两个非空的list
参数,那么如果合同被违反则明确并抛出异常:
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(...)
我认为故事的寓意是无论你做什么,一贯做到并明确。就个人而言,我通常倾向于在违反合同时提出异常,或者给予我真正无法使用的价值。如果给出的值是合理的,那么我会尝试做一些合理的回报。您可能还想阅读有关例外的C++ FAQ Lite entry。这个特殊条目为您提供了一些关于异常的思考的食物。
答案 5 :(得分:1)
对于集合(列表,集合,字典等),返回空集合是显而易见的选择,因为它使您的呼叫站点逻辑免于防御逻辑。更明确地讲,空集合仍然是您希望从中获得集合的函数的完美答案,您不必检查结果是否为任何其他类型,并且可以干净地继续您的业务逻辑。 >
对于非收款结果,有几种处理条件回报的方法:
None
而不是您的预期结果,但这迫使用户在其呼叫站点的所有位置添加防御性检查,从而混淆了他们试图实际执行的实际业务逻辑执行。pip install optional.py
安装它。我欢迎评论,功能要求和贡献。