lettura simple

Custom Exceptions in Python

In Python, all exceptions must derive from the BaseException class, either directly or indirectly. BaseException is the root class for all exceptions, and various subclasses extend from it to represent different types of exceptions.

So, if you want to create a custom class, you need to define a subclass of `BaseException` or one of its subclasses, such as `Exception`.

Built-in Exception Hierarchy

In Python, all built-in exceptions derive from the root class `BaseException`. 

Here is an overview of the hierarchy:

  • BaseException
    • SystemExit
    • KeyboardInterrupt
    • GeneratorExit
    • Exception
      • ArithmeticError
        • FloatingPointError
        • OverflowError
        • ZeroDivisionError
      • BufferError
      • LookupError
        • IndexError
        • KeyError
      • EnvironmentError
        • IOError
        • OSError
      • AssertionError
      • AttributeError
      • EOFError
      • ImportError
      • ModuleNotFoundError
      • KeyError
      • KeyboardInterrupt
      • MemoryError
      • NameError
      • NotImplementedError
      • RuntimeError
        • RecursionError
      • SyntaxError
        • IndentationError
        • TabError
      • SystemError
      • TypeError
      • ValueError
        • UnicodeError
          • UnicodeDecodeError
          • UnicodeEncodeError
          • UnicodeTranslateError
      • Warning
        • DeprecationWarning
        • PendingDeprecationWarning
        • RuntimeWarning
        • SyntaxWarning
        • UserWarning
        • FutureWarning
        • ImportWarning
        • UnicodeWarning
        • BytesWarning
        • ResourceWarning

As you may have noticed, not all exceptions are errors; some are warnings.

Errors halt the program and display an error message, while warnings do not stop the program's execution.

Typically, Python does not show warnings by default. To display warnings, you need to compile the Python bytecode with the "-b" option.

Generally, it's preferable to inherit from Exception rather than ExceptionBase, as this allows you to handle unexpected issues, such as errors or warnings, without disrupting the normal flow of the program, like intentional exits or interruptions.

Inheriting from Exception allows you to bypass other system exceptions that depend on ExceptionBase but not on Exception, such as SystemExit, KeyboardInterrupt, and GeneratorExit.

In general, when creating a new exception, it’s crucial to inherit from the most specialized exception type to catch specific exceptions effectively.

Creating New Exceptions

When creating a new exception, you should inherit from `Exception` or one of its subclasses to ensure it can be raised and caught properly.

Here's an example of how to create and use a new exception:

class MyException(Exception):
    pass

try:
    raise MyException("This is a custom error message")
except MyException as e:
    print(f"Caught exception: {e}")

This code defines a new custom exception 'MyException' as a subclass of the Exception class.

It then raises an exception of type 'MyException' with a custom message using the raise statement in the try block.

Finally, the exception is caught by the except block, assigned to the variable 'e', and the error message is printed.

Caught exception: This is a custom error message

This way, you can define and raise a custom exception.

This class you just created is considered an exception because it is a subclass of "Exception".

print(issubclass(MyException, Exception))

True

And, of course, it is also a subclass of the root class of exceptions, which is "BaseException".

print(issubclass(MyException, BaseException))

True

The general rule when creating an exception is to inherit from the most specific exception type.

For example, this exception inherits from "ZeroDivisionError," a subclass of "Exception".

class MyException(ZeroDivisionError):
    pass

try:
    raise MyException("Division by zero")
   
except ZeroDivisionError as e:
    print(f"Caught exception: {e}")

This ensures that the custom exception will be caught by the except ZeroDivisionError clause.

This approach prevents the unintentional catching and handling of other exceptions such as FloatingPointError or OverflowError.

The except ZeroDivisionError block successfully catches the exception.

Caught exception: Division by zero

Now, try modifying the MyException class to inherit directly from "Exception".

class MyException(Exception):
    pass

try:
    raise MyException("Division by zero")
   
except ZeroDivisionError as e:
    print(f"Caught exception: {e}")

In this case, the exception is not caught because, in Python, exception handling works hierarchically, and the except clause only catches exceptions of the specified type or its subclasses.

Catching an exception with a more specific except clause will not work if the exception is not directly or indirectly a subclass of the type specified in the clause, in this case, ZeroDivisionError.

Traceback (most recent call last):
  File "/home/main.py", line 5, in <module>
    raise MyException("Division by zero")
__main__.MyException: Division by zero

Inheriting from a specialized exception type in Python is crucial to ensure exceptions are properly caught and handled.

This principle minimizes the risk of improperly handling unexpected exceptions.

Exception Inheritance

When catching an exception, you can catch all exceptions of a base class and its subclasses. For example:

class Error1(Exception):
    pass

class Error2(Error1):
    pass

class Error3(Error2):
    pass

try:
    raise Error3("Error of type 3")
except Error3 as e:
    print(f"Caught exception of type: {type(e).__name__} with message: {e}")

In this example, we have defined three classes:

  • The 'Error1' class is a subclass of 'Exception', and therefore an exception.
  • The 'Error2' class is a subclass of 'Error1'
  • The 'Error3' class is a subclass of 'Error2'

In the try block, we raised an 'Error3' exception using the raise statement, which is then caught and printed by the except block.

Python recognized the Error3 class as an exception because it is an indirect subclass of 'Exception' through 'Error2' and 'Error1'.

In other words, the 'Error3' class inherited the 'Exception' type.

Caught exception of type: Error3 with message: Error of type 3

Exceptions That Are Not Subclasses of Exception

In Python, there are three specific exceptions that do not directly inherit from Exception but instead from the base class BaseException. These exceptions are:

  • SystemExit
    This exception is used to terminate the program. When SystemExit is raised, Python exits the program without displaying an error message. You can specify an exit code, which will be returned to the shell. For example, the following statement exits the program with code 0:

    raise SystemExit(0)

  • KeyboardInterrupt
    This exception is raised when the user interrupts the program execution by pressing Ctrl-C. It allows for the interruption to be handled cleanly, enabling actions such as performing cleanup operations before the program terminates.
  • GeneratorExit
    This exception is raised when a generator is explicitly closed using the close() method. It is not propagated to the caller but is handled internally by Python to inform the generator that it has been closed. If this exception is caught within the generator, it is important to propagate it to avoid errors.

These exceptions are designed for specific purposes: terminating the program, handling user interruptions, and closing generators. Their specialized handling ensures that critical operations are performed correctly even in exceptional situations.

When you use a generic try except structure, it catches all exceptions because a generic except is equivalent to writing except BaseException.

try
    pass
except
    pass

If you only want to catch errors or warnings, it is advisable to use except Exception.

try
    pass
except Exception
    pass

In conclusion, proper exception handling and understanding the exception hierarchy are essential if you want to define custom exceptions.




Report a mistake or post a question




FacebookTwitterLinkedinLinkedin