sec03 - Python Custom Exceptions: From Basics to Practical Use
スポンサーリンク

What are Custom Exceptions?

In Python, when some abnormality occurs during program execution, a mechanism called "Exception" is used. For example, if you try to divide by zero or open a non-existent file, Python will automatically raise an exception.

However, in real-world programming, the built-in exceptions may not always be sufficient. For instance, you may want to represent application-specific errors such as "invalid input data" or "unexpected API response."

In such cases, custom exceptions (user-defined exceptions) are useful. This allows you to define your own exception classes to handle specific situations.

Class

Classes are covered in a dedicated lecture, but once you learn the basic structure of a custom exception, you can start using them right away. First, try using the minimal custom exception introduced in this lecture.

If you later want to create more detailed custom exceptions, you can study classes and then return to this lecture.

Basic Form: The Simplest Custom Exception

All exceptions in Python are based on the Exception class. Therefore, to create a new exception, you just need to define a class that inherits from Exception.

# Basic form of a custom exception
class MyError(Exception):
    pass

# Raise a custom exception
raise MyError('This is an example of a custom exception.')
Exception has occurred: MyError
This is an example of a custom exception.
  File "C:xxxxxsec03_ctrl_flow_exception.py", line 6, in <module>
    raise MyError('This is an example of a custom exception.')
MyError: This is an example of a custom exception.

In this way, you can create a custom exception called MyError and raise it using the raise statement. It will be displayed along with the error message, just like Python's built-in exceptions.

スポンサーリンク

Catching Custom Exceptions

Custom exceptions can also be caught using the try / except construct, just like regular exceptions.

class MyError(Exception):
    pass

try:
    raise MyError('Something went wrong.')
except MyError as e:
    print('Caught a custom exception:', e)
Caught a custom exception: Something went wrong.

This way, you can handle your own exceptions in the same way as standard exceptions.

Custom Exceptions with Attributes

In addition to holding a message, you can also store related information (such as error codes or input values) as attributes.

class ValidationError(Exception):
    def __init__(self, field, message):
        self.field = field
        self.message = message
        super().__init__(f'Error in {field}: {message}')

# Example usage
try:
    raise ValidationError('Username', 'Must be at least 3 characters.')
except ValidationError as e:
    print('Error occurred in field:', e.field)
    print('Error message:', e.message)
Error occurred in field: Username
Error message: Must be at least 3 characters.

In this way, you can include information about which field caused the error. This technique is commonly used in real-world application development.

Creating a Common Parent Class

As an application grows, you may want to define multiple types of errors, such as "input errors," "database errors," or "communication errors."

In that case, it is useful to create a single parent class for all custom exceptions. This allows you to handle all application-specific errors in one place.

# Common parent class
class ProjectError(Exception):
    '''Base class for application-specific exceptions'''
    pass

# Specific errors
class InputError(ProjectError):
    pass

class DatabaseError(ProjectError):
    pass

try:
    raise InputError('Invalid input.')
except ProjectError as e:
    print('Caught by the common parent class:', e)
Caught by the common parent class: Invalid input.

By using ProjectError as the base class, you can handle any new types of errors in a unified way.

Re-raising Exceptions (Reuse of raise Statement)

Sometimes after catching an exception, you want to pass it outward again. This is called "re-raising."

class MyError(Exception):
    pass

def process():
    try:
        raise MyError('An issue occurred in internal processing.')
    except MyError as e:
        print('Caught the exception internally, but re-raising it.')
        raise  # Re-raise the exception

try:
    process()
except MyError as e:
    print('Caught the re-raised exception externally:', e)

Caught the exception internally, but re-raising it.
Caught the re-raised exception externally: An issue occurred in internal processing.

This allows you to handle the exception in an internal function while still passing the information outward.

Benefits of Using Custom Exceptions

  • Clarifies the meaning of errors (e.g., distinguish between "input error" and "communication error")
  • Allows different handling for each exception (e.g., log it or notify the user)
  • Improves code readability and maintainability
  • Allows unified handling via a common parent class

Due to these advantages, custom exceptions are almost essential for medium to large applications.

スポンサーリンク