Catch exceptions in subclasses __init__ and exit

Solution for Catch exceptions in subclasses __init__ and exit
is Given Below:

Consider the code below:

class Parent():
    
    def __init__(self, ...):
        # set up logging
        # ....


class Child(Parent):
    
    def __init__(self, ...):
        super(Child, self).__init__(...)

        print(1/0)


c = Child()

The above will fail because of the 1/0. Is it possible to have some logic (ex: I want to log something) in the Parent execute when the __init__ fails? That is, I’d like to catch exceptions in the Child class’s __init__ and then do things like logging prior to the entire instance be garbage collected.

You have to make the execution of the offending code something that the parent actually runs. One way to do this is to define the code not in __init__ itself, but in a hook executed by the parent. (The dataclasses module does something similar.)

class Parent():
    
    def __init__(self, ...):
        # set up logging
        # ....
        
        try:
            self.post_init()
        except Exception:
            ...

    # Do-nothing implementation in case the child doesn't override it
    def post_init(self):
        pass

class Child(Parent):
    
    def post_init(self):
        print(1/0)
    

c = Child()

One way to do this would be by decorating each subclass’ __init__() method with something that would handle any exceptions that occur. You could do that explicitly in each subclass or automatically by defining a metaclass or by using the relatively new __init_subclass__() method which was added in Python 3.6.

That latter seems like the simplest way to make it automatic, so I’ll illustrate doing it that way:

from functools import wraps

class Parent:

    def __init__(self, *args, **kwargs):
        # set up logging...
        pass

    def logger(self, msg):
        print('logging msg:', msg)

    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__(**kwargs)
        cls.__init__ = cls.log_exceptions(cls.__init__)  # Decorate subclass' __init__.

    @staticmethod
    def log_exceptions(method):
        """ Wrap method in an exception handler that will automatically log any
            exceptions that occur executing it.
        """
        @wraps(method)
        def wrapped(self, *args, **kwargs):
            try:
                return method(self, *args, **kwargs)
            except Exception as exc:
                self.logger(exc)
        return wrapped


class Child(Parent):

    def __init__(self, *args, **kwargs):
        print(f"{type(self).__name__}.__init__() called")
        super().__init__(*args, **kwargs)  # Base class initialization.
        # other initializations...
        print(1 / 0)


c = Child(1, 2, 3)

One way of doing this could be defining a logger method in your parent, so that every methods in child classes (including __init__) can get to it by inheriting logger method.

class Parent:

    def __init__(self, *args):
        print(args, 'Parent')

    def logger(self, msg):
        print('logging', msg)


class Child(Parent):

    def __init__(self, *args):
        super().__init__(*args)

        # other initializations here

        # the code may cause problem
        try:
            print(1 / 0)
        except Exception as e:
            self.logger(e)


c = Child(1, 2, 3)

output :

(1, 2, 3) Parent
logging division by zero

Every child classes can also override logger method and add something specific in addition to parent’s logger if they want.