我应该总是在`except`语句中指定异常类型吗?


93

使用PyCharm IDE时,使用except:无异常类型会触发IDE提醒此异常子句为Too broad

我应该忽略这个建议吗?还是总是特定于异常类型的Pythonic?


如果您想了解更多有关答案的信息,请Google吞下例外。您可以在它们旁边进行其他各种有趣的操作,也可以不进行操作。代码气味是另一种。
Tony Hopkinson

Answers:


89

指定显式异常类型几乎总是更好。如果使用裸except:子句,则最终可能会捕获到您期望捕获的异常以外的异常-这可能会隐藏错误或使程序无法按预期运行时更难以调试。

例如,如果要在数据库中插入行,则可能要捕获一个异常,该异常表明该行已存在,因此可以进行更新。

try:
    insert(connection, data)
except:
    update(connection, data)

如果指定了裸except:,您还将捕获到一个套接字错误,指示数据库服务器已倒塌。最好只捕获您知道如何处理的异常-程序在异常发生时失败通常比继续执行但以奇怪的意外方式执行更好。

您可能希望使用裸机的一种情况except:是始终需要运行的程序的顶层,例如网络服务器。但是然后,您需要非常小心地记录异常,否则将无法找出问题所在。基本上,执行此操作的程序中最多只能有一个位置。

其推论这一切是你的代码不应该这样做raise Exception('some message'),因为它迫使客户端代码使用except:(或except Exception:这是几乎一样糟糕)。您应该定义一个特定于您要发信号的问题的异常(也许从某些内置的异常子类(例如ValueErrorTypeError)继承)。或者,您应该引发特定的内置异常。这使您的代码用户可以小心地捕获他们想要处理的异常。


7
+1非常正确。这个示例更加有趣:except:还捕获了(以及其他许多东西)NameErrorAttributeError,因此,如果您在try块中拼错了某些内容(例如,insert_one由于有人未如愿以偿地重视一致性,实际上会调用“插入”函数),总是默默地尝试update()

1
那么,当您需要确保不会将异常抛出到当前调用站点之上时,该怎么办呢?即,我已经捕获了所有可以预见的异常,现在我需要补充一点,“如果我没有想到抛出该异常,则需要在它杀死正在运行的执行上下文之前对其进行记录”(例如main())?
亚当·帕金

我在“一个案例...”开头的段落中就是这种情况。有时确实需要它,但是任何给定的程序实际上只应该只有一个地方可以做到这一点。而且您需要小心,以使其在日志中清楚地表明正在发生的事情,否则您将有一段沮丧的时间试图弄清为什么程序无法正确处理事件/请求/任何事情。
babbageclunk 2015年

3
@delnan:比那还糟。except Exception:将捕获NameErrorAttributeError太。是什么让裸except:如此糟糕的是,它捕获没有业务被抓,如东西SystemExit(当你打电话提出exit或者sys.exit,现在你已经防止预期的出口)和KeyboardInterrupt(同样,如果用户命中Ctrl-C,您可能不希望保持运行只是为了sp他们)。只有后者才是真正有意义的捕获对象,应明确捕获它。至少except Exception:让这两个正常传播。
ShadowRanger 2015年

38

您不应忽略口译员给您的建议。

摘自《PEP-8 Python样式指南》:

捕获异常时,请尽可能提及特定的异常,而不要使用裸露的except:子句。

例如,使用:

 try:
     import platform_specific_module 
 except ImportError:
     platform_specific_module = None 

单独的except:子句将捕获SystemExit和KeyboardInterrupt异常,这使得使用Control-C中断程序更加困难,并且可以掩盖其他问题。如果要捕获所有表示程序错误的异常,请使用Exception:除外(裸除等效于BaseException:除外)。

一个好的经验法则是将裸'except'子句的使用限制为两种情况:

如果异常处理程序将打印输出或记录回溯;至少用户会意识到发生了错误。如果代码需要执行一些清理工作,但随后让异常通过提高向上传播。试试...最终可能是处理这种情况的更好方法。



9

并不特定于Python。

异常的全部要点是尽可能在引起问题的地方进行处理。

因此,您保留可能在特殊情况下可能引发问题和解决方案的代码。

问题是您不知道一段代码可能引发的所有异常。您所知道的就是,如果这是一个“找不到文件”异常,则可以将其捕获并提示用户获取执行该功能或取消该功能的文件。

如果您尝试使用catch,那么无论您的文件例程出现什么问题(只读,权限,UAC,不是真正的pdf等),每个文件都会放入未找到catch的文件中,并且您的用户尖叫“但它在那里,此代码是废话”

现在有几种情况,您可能会抓住一切,但是应该有意识地选择它们。

它们会被捕获,撤消某些本地操作(例如,创建或锁定资源,(例如,打开磁盘上的文件以进行写入),然后再次引发该异常,以在更高级别进行处理)

另一个是您,您不管它为什么出错。例如打印。您可能会遇到一个麻烦,那就是您的打印机出现问题,请解决该问题,不要因此而终止应用程序。如果您的代码使用某种时间表执行一系列单独的任务,那将是徒劳的,因为其中一个任务失败,您将希望整个事情死掉。

注意:如果执行上述操作,则我不建议您进行某种程度的异常日志记录,例如,尝试捕获日志结尾足够高。


我要说的是平衡。您必须尽早捕获异常以能够从异常中恢复,而又要足够迟才知道如何处理异常。这就是Java异常处理会造成这种破坏的原因,因为您必须在每个步骤中重新包装异常,并且会丢失信息。
dhill

2
+1表示“您不在乎它为什么会出错”。我在一行代码中的多个地方使用它,从URL解析日期/时间。第三方日期/时间解析库并未列出它可以抛出的所有异常(除了标准ValueError之外,我还发现了OverflowError和TypeError,但可能还有更多),无论如何,我真的不在乎为什么引发了一个异常,我只想向用户提供一条合理的错误消息,指出日期/时间出了点问题。
Michael Rodby


3

始终指定的异常类型,也有很多种类,你不想抓,像SyntaxErrorKeyboardInterruptMemoryError等等。


2
是否会except Exception:避免使用我们不想捕获的上述类型?
HorseloverFat

except Exception很好
Ulrich Eckhardt

4
@HorseloverFat:except Exception捕获SyntaxError并且MemoryError因为它是它们的基类。KeyboardInterruptSystemExit(由引​​发sys.exit())未被捕获(它们是直接的BaseException子类)
jfs

听起来不那么理想-最好指定得更精确。
HorseloverFat

3

这是我在没有类型的情况下使用的地方

  1. 快速而肮脏的原型

这是我的代码中未经检查的异常的主要用途

  1. 顶层main()函数,在这里记录每个未捕获的异常

我总是添加它,以便生产代码不会溢出堆栈跟踪

  1. 在应用层之间

我有两种方法可以做到:

  • 做到这一点的第一种方法:当高层调用较低层的函数时,它将调用封装在类型化的例外中,以处理“顶层”较低层的异常。但是我添加了一个通用的except语句,以检测较低级功能中未处理的较低级异常。

我更喜欢这种方式,我发现更容易检测应该适当捕获哪些异常:当较高级别的日志记录了较低级别的异常时,我可以更好地“查看”问题

  • 第二种方法:较低层的每个顶层函数都将其代码包装在通用类中,除了它捕获特定层上所有未处理的异常。

一些同事更喜欢这种方式,因为在“所属”的较低级别的函数中保留了较低级别的异常。


-14

试试这个:

try:
    #code
except ValueError:
    pass

如果有人遇到此问题,我会从此链接获得答案签

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.