引发异常与返回函数中无?


85

在Python中的用户定义函数中,更好的做法是:raise异常或return None?例如,我有一个在文件夹中查找最新文件的函数。

def latestpdf(folder):
    # list the files and sort them
    try:
        latest = files[-1]
    except IndexError:
        # Folder is empty.
        return None  # One possibility
        raise FileNotFoundError()  # Alternative
    else:
        return somefunc(latest)  # In my case, somefunc parses the filename

另一个选择是保留异常并在调用者代码中处理它,但我认为处理aFileNotFoundError比处理a更清楚IndexError。还是用其他名称重新引发异常是不好的形式吗?



3
我倾向于提出异常,因此我被迫在调用函数中处理该异常。如果我忘记检查调用函数中的输出是否为None,则可能是潜在的错误。如果返回None,则希望调用函数中的下一行将引发AttributeError。但是,如果将返回的值添加到字典中,然后在另一个源文件中调用100个函数,则会引发AttributeError,您会发现查找该值为何为None的乐趣。
IceArdor 2014年

通常,我还避免使用具有特殊含义或对一个函数具有多个签名的值(它可以返回字符串或无)。
IceArdor 2014年

Answers:


91

这实际上是语义问题。什么foo = latestpdf(d) 平均值

没有最新文件完全合理吗?然后确定,只返回None。

您是否希望总是找到最新文件?引发异常。是的,重新提出一个更合适的例外是可以的。

如果这只是应该应用于任何目录的通用函数,我将做一个前一个并返回None。如果该目录是(例如)要成为包含应用程序已知文件集的特定数据目录,则将引发异常。


3
需要考虑的另一点:如果引发异常,可以附加一条消息,但是在返回时我们不能这样做None
kawing-chiu

9

在回答您的问题之前,我会提出一些建议,因为它可能会为您解答。

  • 始终以描述性方式命名您的函数。 latestpdf对任何人来说几乎没有什么意义,但查看您的函数latestpdf()可获得最新的pdf。我建议你命名getLatestPdfFromFolder(folder)

我这样做后,就清楚应该返回什么内容。如果没有pdf,则会引发异常。但是,在那里等待更多..

  • 明确定义功能。由于尚不清楚somefuc应该做什么,并且(显然)它与获取最新pdf的关系也不明显,因此建议您将其删除。这使代码更具可读性。

for folder in folders:
   try:
       latest = getLatestPdfFromFolder(folder)
       results = somefuc(latest)
   except IOError: pass

希望这可以帮助!


或者get_latest_pdf_from_folder。确实,Pep8:“函数名应小写,必要时用下划线分隔单词,以提高可读性。”
PatrickT

7

我通常更喜欢在内部处理异常(即在被调用函数内部进行try / except,可能返回None),因为python是动态类型的。总的来说,我认为这是一种判断调用方式,但是在动态类型的语言中,有一些小因素会提示扩展规模,以免将异常传递给调用者:

  1. 不会向任何调用您的函数的人通知可能引发的异常。知道您正在寻找什么样的异常(以及应该避免的一般性例外),成为一种艺术形式。
  2. if val is None比容易一些except ComplicatedCustomExceptionThatHadToBeImportedFromSomeNameSpace。严重的是,我讨厌不得不记住from django.core.exceptions import ObjectDoesNotExist在所有django文件的顶部都只是为了处理一个非常普通的用例而键入。在静态类型的世界中,让编辑器为您完成。

不过,老实说,它始终是一个判断调用,而您所描述的情况(被调用函数收到它无法帮助的错误)是重新引发有意义的异常的极好理由。您有完全正确的想法,但除非有例外,否则您将在堆栈跟踪中提供比

AttributeError: 'NoneType' object has no attribute 'foo'

如果您返回未处理的“无”,则打扰者将看到呼叫返回的结果,十分之九。

(所有这些使我希望python异常在cause默认情况下具有属性,就像在Java中一样,它使您可以将异常传递到新的异常中,以便您可以抛弃所有想要的东西,而不会丢失问题的原始来源。)


对于可能的异常,未定义,因此很难捕获的论点对于Python是非常有效的论点。
snorberhuis

4

使用python 3.5的输入

返回None时的示例函数为:

def latestpdf(folder: str) -> Union[str, None]

当引发异常时将是:

def latestpdf(folder: str) -> str 

选项2似乎更具可读性和pythonic

(+选项,如前所述,为异常添加注释。)


4
Union[str, None]应该是Optional[str]
Georgy

2
速记,但您是对的,它更具可读性。没有编辑,因此两个选项都在这里。
阿萨夫

2可能更易读,但(不幸的是?)类型提示并不表示可能引发异常。我最近发现1会帮助您捕获更多错误,因为您不得不处理无返回值。
jonespm

2

一般而言,如果发生了无法恢复的灾难性事件(即您的函数处理了无法连接的某些互联网资源),则应该抛出异常;如果函数确实返回了某些内容,则应返回None。但没有适合返回的内容(例如,如果您的函数尝试匹配字符串中的子字符串,则为“ None”)。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.