Книга: Practical Programming, Fourth Edition
Назад: Raising and Chaining Exceptions
Дальше: Exceptionally Important!

Defining Custom Exceptions

In the , choosing IOError as a replacement for FileNotFoundError is functional but not ideal. While it raises the level of abstraction from “file not found” to “input/output error,” that abstraction is still too low-level for, say, an enterprise-scale data management system.

A better approach is to define a custom exception type that reflects the problem in domain-specific terms. For example, you could define a new exception type called DataLoadError. Custom exceptions are typically subclasses of the built-in class Exception:

 >>>​​ ​​class​​ ​​DataLoadError(Exception):
 ...​​ ​​"""Custom exception for data loading errors."""
 ...
 >>>​​ ​​help(DataLoadError)
 Help on class DataLoadError in module __main__:
 
 class DataLoadError(builtins.Exception)
  | Custom exception for data loading errors.
  |
  | Method resolution order:
 ...

Inheritance Matters!

icon indicating an aside

Inherit custom exceptions from Exception, not BaseException. BaseException is for system-exiting errors.

The body of a custom exception class can be empty except for a docstring that documents its purpose (you may want to revisit ). Even this minimal definition provides two major benefits:

You can now rewrite the previous example in terms of this domain-specific exception:

 >>>​​ ​​def​​ ​​load_data(filename:​​ ​​str)​​ ​​->​​ ​​str:
 ...​​ ​​"""Return the contents of the file `filename`."""
 ...​​ ​​try:
 ...​​ ​​with​​ ​​open(filename)​​ ​​as​​ ​​infile:
 ...​​ ​​return​​ ​​infile.read()
 ...​​ ​​except​​ ​​FileNotFoundError​​ ​​as​​ ​​eo:
 ...​​ ​​raise​​ ​​DataLoadError(f​​'Could not load data from {filename}'​​)​​ ​​from​​ ​​eo
 ...
 >>>​​ ​​try:
 ...​​ ​​load_data(​​'missing.txt'​​)
 ...​​ ​​except​​ ​​DataLoadError​​ ​​as​​ ​​eo:
 ...​​ ​​print(eo)
 ...​​ ​​print(​​'Original cause: {eo.__cause__}'​​)
 ...
 Could not load data from missing.txt
 Original cause: [Errno 2] No such file or directory: 'missing.txt'

The functionality of the code remains the same, but its readability and maintainability improve significantly. Instead of leaking low-level implementation details (FileNotFoundError), the function communicates a higher-level concept (DataLoadError) that better matches the language of the application or domain.

Назад: Raising and Chaining Exceptions
Дальше: Exceptionally Important!