14.8 创建自定义异常
最后更新于:2022-04-01 15:40:33
## 问题
You’re building an application and would like to wrap lower-level exceptions with cus‐tom ones that have more meaning in the context of your application.
## 解决方案
Creating new exceptions is easy—just define them as classes that inherit from Exception (or one of the other existing exception types if it makes more sense). For example,if you are writing code related to network programming, you might define some customexceptions like this:
class NetworkError(Exception):passclass HostnameError(NetworkError):passclass TimeoutError(NetworkError):passclass ProtocolError(NetworkError):pass
Users could then use these exceptions in the normal way. For example:
try:msg = s.recv()except TimeoutError as e:...except ProtocolError as e:...
## 讨论
Custom exception classes should almost always inherit from the built-in Exceptionclass, or inherit from some locally defined base exception that itself inherits from Exception. Although all exceptions also derive from BaseException, you should not usethis as a base class for new exceptions. BaseException is reserved for system-exitingexceptions, such as KeyboardInterrupt or SystemExit, and other exceptions thatshould signal the application to exit. Therefore, catching these exceptions is not the
intended use case. Assuming you follow this convention, it follows that inheriting fromBaseException causes your custom exceptions to not be caught and to signal an im‐minent application shutdown!Having custom exceptions in your application and using them as shown makes yourapplication code tell a more coherent story to whoever may need to read the code. Onedesign consideration involves the grouping of custom exceptions via inheritance. Incomplicated applications, it may make sense to introduce further base classes that groupdifferent classes of exceptions together. This gives the user a choice of catching a nar‐rowly specified error, such as this:
try:s.send(msg)except ProtocolError:...
It also gives the ability to catch a broad range of errors, such as the following:
try:s.send(msg)except NetworkError:...
If you are going to define a new exception that overrides the __init__() method ofException, make sure you always call Exception.__init__() with all of the passedarguments. For example:
class CustomError(Exception):def __init__(self, message, status):super().__init__(message, status)self.message = messageself.status = status
This might look a little weird, but the default behavior of Exception is to accept allarguments passed and to store them in the .args attribute as a tuple. Various otherlibraries and parts of Python expect all exceptions to have the .args attribute, so if youskip this step, you might find that your new exception doesn’t behave quite right incertain contexts. To illustrate the use of .args, consider this interactive session with thebuilt-in RuntimeError exception, and notice how any number of arguments can be usedwith the raise statement:
>>> try:
... raise RuntimeError('It failed')
... except RuntimeError as e:
... print(e.args)
...
('It failed',)
>>> try:
... raise RuntimeError('It failed', 42, 'spam')
... except RuntimeError as e:
... print(e.args)...(‘It failed', 42, ‘spam')>>>
For more information on creating your own exceptions, see the Python documentation.