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()