我是否应该始终在“except”语句中指定异常类型?[关闭]
- 2025-01-10 08:46:00
- admin 原创
- 99
问题描述:
当使用 PyCharm IDE 时,使用except:
没有异常类型的会触发 IDE 的提醒,提示此异常子句是Too broad
。
我应该忽略这个建议吗?还是总是指定异常类型是 Pythonic 的做法?
解决方案 1:
指定显式异常类型几乎总是更好的选择。如果您使用裸except:
子句,您最终可能会捕获除您期望捕获的异常之外的异常 - 这可能会隐藏错误,或者当程序没有按照您的预期执行时,使调试程序变得更加困难。
例如,如果您正在向数据库中插入一行,您可能想要捕获一个异常,该异常表明该行已存在,以便您可以进行更新。
try:
insert(connection, data)
except:
update(connection, data)
如果您指定了一个 bare except:
,您还会捕获一个套接字错误,该错误表明数据库服务器已崩溃。最好只捕获您知道如何处理的异常 - 程序在发生异常时失败通常比继续运行但以奇怪的意外方式运行要好。
您可能需要使用裸机的情况之一except:
是在需要始终运行的程序的顶层,例如网络服务器。但是,您需要非常小心地记录异常,否则将无法找出问题所在。基本上,程序中最多只能有一个地方执行此操作。
所有这些的必然结果是,您的代码永远不应该这样做raise Exception('some message')
,因为它会迫使客户端代码使用except:
(或,except Exception:
这几乎同样糟糕)。您应该针对要发出信号的问题定义一个特定的异常(可能从某个内置异常子类(如ValueError
或TypeError
)继承)。或者您应该引发一个特定的内置异常。这使您的代码的用户能够小心地捕获他们想要处理的异常。
解决方案 2:
您不应忽视翻译给您的建议。
来自Python 的PEP-8样式指南:
捕获异常时,尽可能提及具体异常,而不是使用裸
except:
子句。例如,使用:
try: import platform_specific_module except ImportError: platform_specific_module = None
裸
except:
子句将捕获 SystemExit 和 KeyboardInterrupt 异常,这使得使用 Control-C 中断程序变得更加困难,并且可以掩盖其他问题。如果您想捕获所有表示程序错误的异常,请使用except Exception:
(裸子句相当于except BaseException:
)。一个好的经验法则是将裸“except”子句的使用限制在两种情况下:
如果异常处理程序将打印出或记录回溯;至少用户将知道发生了错误。
如果代码需要做一些清理工作,但然后让异常向上传播
raise
。try...finally
可能是处理这种情况的更好的方法。
解决方案 3:
这并不特定于 Python。
异常的全部意义在于尽可能在问题发生的地方处理问题。
因此,您要将在特殊情况下可能触发问题的代码和解决方案“放在一起”。
问题是你无法知道一段代码可能抛出的所有异常。你所能知道的只是,如果是文件未找到异常,那么你可以捕获它并提示用户获取一个可以捕获的异常或取消该功能。
如果你用 try catch 来解决这个问题,那么无论你的文件例程中存在什么问题(只读、权限、UAC、不是真正的 pdf 等),每个人都会陷入文件未找到的捕获中,而你的用户会尖叫“但它在那里,这段代码是垃圾”
现在,有几种情况你可能会感染所有疾病,但应该有意识地进行选择。
它们是捕获、撤消某些本地操作(例如创建或锁定资源(例如打开磁盘上的文件进行写入),然后再次抛出异常,以便在更高级别处理)
另一个你不在乎为什么出错。例如打印。你可能会有一个陷阱,说你的打印机出了点问题,请解决它,不要因此而终止应用程序。同样,如果你的代码使用某种计划执行了一系列单独的任务,你不会希望整个程序因为其中一个任务失败而终止。
注意如果您执行上述操作,我强烈建议您使用某种异常日志记录,例如 try catch log end。
解决方案 4:
始终指定异常类型,有很多类型您不想捕获,例如SyntaxError
,KeyboardInterrupt
等等MemoryError
。
解决方案 5:
以下是我使用 except without type 的地方
快速而粗糙的原型设计
这是我的代码中未检查异常的主要用途
顶层 main() 函数,我在这里记录每个未捕获的异常
我总是添加这个,以便生产代码不会泄露堆栈跟踪
应用层之间
我有两种方法可以做到这一点:
第一种方式是:当较高级别的层调用较低级别的函数时,它会将调用包装在类型化的 except 中,以处理“顶部”较低级别的异常。但我添加了一个通用 except 语句,以检测较低级别函数中未处理的较低级别异常。
我更喜欢这种方式,我发现更容易检测哪些异常应该被适当捕获:当较低级别的异常被较高级别的异常记录时,我可以更好地“看到”问题
第二种方法是:每个较低层级的顶层函数都将其代码包装在通用的 except 中,以便捕获该特定层上所有未处理的异常。
一些同事更喜欢这种方式,因为它将较低级别的异常保留在它们“属于”的较低级别的函数中。
解决方案 6:
您还可以使用它来捕获 Control-C,因此除非您再次“抛出”它,否则不要这样做。但是,在这种情况下,您应该使用“finally”。