Home > Blockchain >  Automatically call method after __init__ in child class
Automatically call method after __init__ in child class

Time:02-20

I have the following code:

class Parent:
    def __init__(self) -> None:
        print(f"init Parent")

class Child(Parent):
    def __init__(self) -> None:
        super().__init__()
        print(f"init Child")

    def post_init(self):
        print(f"post init")

Child()

Here I get the output:

init Parent
init Child

Is it possible to modify the code in the Parent-class to call the post_init method from the Child-class automatically after the ___init___() of the Parent and the Child-class. I would like to have the following output:

init Parent
init Child
post init

I don't want to modify the Child-class!!! I have already tried to define a decorator in the Parent-class, but I failed.

EDIT:

The Parent-class is a pattern-class and is multiple used in multiple children. So I don't want to take care every time i use the Child-class. Is it possible on using the abstract-package?

CodePudding user response:

Using __init_subclass__ to override the __init__:

class Parent:
    def __init__(self):
        print(f"init Parent")

    def __init_subclass__(cls, *args, **kwargs):
        super().__init_subclass__(*args, **kwargs)
        def new_init(self, *args, init=cls.__init__, **kwargs):
            init(self, *args, **kwargs)
            self.post_init()
        cls.__init__ = new_init

Supporting grandchildren

To only call after a grandchild class's __init__, we need an additional check to see if the __init__ being called is that of the grandchild's:

class Parent:
    def __init__(self):
        print("init Parent")

    def __init_subclass__(cls, *args, **kwargs):
        super().__init_subclass__(*args, **kwargs)
        def new_init(self, *args, init=cls.__init__, **kwargs):
            init(self, *args, **kwargs)
            if cls is type(self):
                self.post_init()
        cls.__init__ = new_init

Example usage:

class Child(Parent):
    def __init__(self):
        super().__init__()
        print("init Child")

    def post_init(self):
        print("post init")

class Grandchild(Child):
    def __init__(self):
        super().__init__()
        print("init Grandchild")


_ = Grandchild()

Output:

init Parent
init Child
init Grandchild
post init

CodePudding user response:

You can also write code like this:

class Meta(type):
    def __call__(cls, *args, **kwargs):
        instance = super().__call__(*args, **kwargs)
        instance.post_init()
        return instance

class Parent(metaclass=Meta):
    def __init__(self) -> None:
        print(f"init Parent")

class Child(Parent):
    def __init__(self) -> None:
        super().__init__()
        print(f"init Child")

    def post_init(self):
        print(f"post init")

Child()
  • Related