Sergii
Sergii

Reputation: 611

In Python, changing inherited behavior without rewriting the parent class

In Python, I have a class ParentClass. It defines method ParentClass.process(), among the others. ParentClass is inherited by two child classes: ChildA and ChildB. This is a part of the system which goes as a pip wheel. The user should not be able to change the code of the package.

During the execution, the system creates objects of ChildA and ChildB.

Now, I want to give the user of the system ability to change the behavior of .process() method for objects of both ChildA and ChildB.

Obviously, she can do that by redefining ChildA.process() and ChildB.process() but that would be copying of identical behavior twice and overall seems like a very odd approach.

The user could possible inherit her own class from ParentClass and redefine .process() there but that means I need somehow dynamically tell ChildA and ChildB what to inherit.

Also, all classes (ParentClass, ChildA and ChildB) have other methods, apart from .process(), which need to be retained without changes.

Please help me figuring out the most convenient for the user (and the most "pythonic", probably) way of solving this.

Upvotes: 2

Views: 3032

Answers (2)

Alain T.
Alain T.

Reputation: 42129

You could use intermediate inheritance to define common overrides as an extension of the base class:

class ParentClass:
    def process(self):
        print("ParentClass.process() called from",type(self))

class ExtendedParent(ParentClass): 
    def process(self):
        super().process()
        print("Common Extension code for A and B",type(self))

class ChildA(ExtendedParent):
    pass

class ChildB(ExtendedParent):
    pass

a = ChildA()
b = ChildB()

a.process()
# ParentClass.process() called from <class '__main__.ChildA'>
# Common Extension code for A and B <class '__main__.ChildA'>

b.process()
# ParentClass.process() called from <class '__main__.ChildB'>
# Common Extension code for A and B <class '__main__.ChildB'>

If ChildA and ChildB are defined in your package and don't have a .process() method, you could instruct the user of your package to assign methods to these classes directly using a common function that is not in any class:

def extendedProcess(self):
    ParentClass.process(self) # this replaces super().process()
    print("Common Extension code for A and B",type(self))

ChildA.process = extendedProcess        
ChildB.process = extendedProcess

If ChildA and/or ChildB have an override of process() and your user wants to change the behaviour of process() in the parent class, here is one way to do it:

ParentClass_process = ParentClass.process
def extendedProcess(self):
    ParentClass_process(self) #call original code
    print("Common Extension code for A and B",type(self))    
ParentClass.process = extendedProcess  

Upvotes: 1

Patrick Haugh
Patrick Haugh

Reputation: 61063

You can use a class method to reassign process for a particular class

class ParentClass:
    def process(self):
        print("parent")
    @classmethod
    def reassign_process(cls, process):
        cls.process = process

class ChildA(ParentClass):
    def process(self):
        print("Child A")

class ChildB(ParentClass):
    def process(self):
        super().process()
        print("Child B")

p = ParentClass()
ca = ChildA()
cb = ChildB()

p.process()
# parent
ca.process()
# Child A
cb.process()
# parent
# Child B

def new_parent_process(self):
    print("New parent")

ParentClass.reassign_process(new_parent_process)

def new_childa_process(self):
    print("New child A")

ChildA.reassign_process(new_childa_process)

p.process()
# New parent
ca.process()
# New child A
cb.process()
# New parent
# Child B

Upvotes: 1

Related Questions