问题的背景:
异常系统(exceptions)最初出现在 Python 中,并建立在类的基础上。所有异常层次结构都继承自基本类 BaseException。实际上,自定义异常是从 Exception 继承而来。对于大型项目来说,开发用户自定义异常非常重要——它可以更准确地处理和报告应用程序的特定错误。
问题:
并非所有开发人员都知道 BaseException 和 Exception 之间的区别,懒于创建自己的类,使用通用的 catch 块,这导致捕获不希望出现的异常(例如,SystemExit、KeyboardInterrupt)并产生难以访问的错误。异常类通常实现得不正确——没有设置有意义的名称,也没有从正确的类继承。
解决方案:
将自定义异常写成 Exception 的子类。使用有意义的名称,以免捕获各种基本错误。不要从 BaseException 继承,只从 Exception 或其派生类继承,不要通过 except: 捕获所有内容,而不指明具体的类。
代码示例:
class MyAppError(Exception): """应用程序异常的基类""" pass class ConfigFileNotFound(MyAppError): pass try: raise ConfigFileNotFound('配置文件未找到!') except ConfigFileNotFound as e: print(f'错误:{e}')
关键特征:
通过 "except:" 捕获所有异常而不指定类型有什么危险?
这样的块会捕获甚至系统异常——KeyboardInterrupt、SystemExit,这使得在 Ctrl+C 时无法正常结束程序,并导致在关键情况下"挂起"。应该写 "except Exception:" 以跳过基本的系统事件。
可以从 BaseException 继承自己的异常吗?
在技术上是可以的,但是强烈不推荐——这样的异常难以被发现,它们绕过了标准的 Exception 处理程序,这常常会导致应用程序中的未捕获错误。
使用 ValueError 或 TypeError 而不是用户自定义异常是否正确?
在小脚本中是可以的,但在大型项目中,最好创建自己的语义上有意义的异常。这加快了上层应用程序的错误诊断和处理。
# 不正确: raise ValueError('应用程序特有的某些内容') # 好: class MyAppValueError(MyAppError): pass raise MyAppValueError('带有应用程序上下文的错误描述')
在一个大型项目中,有一个全局的 try/except: 块,捕获了系统异常。一些错误(例如,SystemExit)未记录在日志中,应用程序进入了意外状态,管理员长时间寻找症状。
优点:
缺点:
已定义自己的错误类,只使用 "except Exception: ..." 并为用户自定义异常提供单独的处理程序。
优点:
缺点: