ProgrammingBackend Python Developer

How does exception inheritance work in Python? What should be considered when creating custom exceptions, what mistakes do developers make, and how can traps related to exception handling be avoided?

Pass interviews with Hintsage AI assistant

Answer.

Background:

The exception system in Python was originally built on classes. The entire exception hierarchy inherits from the base class BaseException. In practice, custom exceptions are created from Exception. Developing custom exceptions is important for large projects — it allows for more precise error handling and signaling of application-specific errors.

Problem:

Not all developers understand the differences between BaseException and Exception, some are lazy to create their own classes, and they use generic catch blocks, which leads to the absorption of unwanted exceptions (like SystemExit, KeyboardInterrupt) and hard-to-reach bugs. Often, exception classes are implemented incorrectly — they do not have meaningful names and do not inherit from the right class.

Solution:

Write your exceptions as descendants of Exception. Use meaningful names to avoid catching basic errors indiscriminately. Do not inherit from BaseException, but only from Exception or its derivatives, and do not catch everything through except: without specifying a particular class.

Code example:

class MyAppError(Exception): """Base class for application exceptions""" pass class ConfigFileNotFound(MyAppError): pass try: raise ConfigFileNotFound('Configuration file not found!') except ConfigFileNotFound as e: print(f'Error: {e}')

Key features:

  • Custom exceptions should always be children of Exception.
  • Do not catch exceptions of type BaseException (e.g., KeyboardInterrupt) — these are system events.
  • Exceptions can be conveniently grouped in hierarchies for finer handling.

Tricky questions.

What is dangerous about catching all exceptions using "except:" without specifying a type?

Such a block catches even system exceptions — KeyboardInterrupt, SystemExit, making it impossible to terminate the program properly with Ctrl+C and leading to "hanging" in critical situations. You should write "except Exception:" to skip base system events.

Is it acceptable to inherit your exception from BaseException?

Technically yes, but it's strongly discouraged — such exceptions are hard to detect, they bypass standard Exception handlers, which often leads to uncaught errors in the application.

Is it correct to use ValueError or TypeError instead of custom exceptions?

In small scripts, it's acceptable, but in large projects, it's better to create your own semantically meaningful exceptions. This speeds up diagnostics and error handling at higher levels of the application.

# Incorrect: raise ValueError('Something specific for the application') # Good: class MyAppValueError(MyAppError): pass raise MyAppValueError('Error description with application context')

Common mistakes and anti-patterns

  • Catching exceptions without specifying a type (except:)
  • Importing third-party exceptions with unclear inheritance
  • Constructors of error classes without calling super().init (loss of message)

Real-life example

Negative case

In a large project, there was a global try/except: block that caught system exceptions. A number of errors (like SystemExit) were not logged, causing the application to enter unexpected states, and administrators spent a long time searching for symptoms.

Pros:

  • The system did not "crash" due to rare errors.

Cons:

  • Difficulty in finding the causes of failure.
  • Unexpected lockouts, inability for normal termination.

Positive case

Custom error classes were defined, only "except Exception: ..." was used everywhere with separate handlers for custom exceptions.

Pros:

  • Transparent error handling.
  • Convenient to extend and maintain.

Cons:

  • Requires a bit more code and architectural discipline.