Alb
Alb

Reputation: 1330

In python, how to have two classes share implementation of a method that calls another method with specific implementations for each class

Suppose I have a parent class Parent, with abstract method load that is implemented in its childs classes. Now, I have two child classes childA and childB, both inheriting from Parent. The implementation of load for childA and childB identical. But this implementation of load calls another method, get_paths, that is specific to childA and childB. Parent is something like:

from abc import ABC, abstractmethod
class Parent(ABC):
    def __init__(self, *params):
        # set up the params

    @abstractmethod
    def load(self, *params):
        # loads files, according to each children

    def process(self, *params):
        # process the loaded files

And then the children:

class childA(Parent):
    def __init__(self, *paramsP, *paramsA):
        super().__init__(*paramsP)
        # Set up the specific paramsA

    def get_paths(self, *params):
        # specific implementation to childA

    def load(self, *params):
        # Calls the self.get_paths method, identical to childB.load
class childB(Parent):
    def __init__(self, *paramsP, *paramsB):
        super().__init__(*paramsP)
        # Set up the specific paramsB

    def get_paths(self, *params):
        # specific implementation to childB

    def load(self, *params):
        # Calls the self.get_paths method, identical to childA.load

How can I reuse the code from load in both childA and childB, but have the get_paths implementation be specific to each class? I tried two approaches:

(1) Have childB inherit from childA instead, and override the implementation of get_paths. In this approach, I have:

class childB(childA):
    def __init__(self, *paramsP, *paramsB):
        super().__init__(*paramsP)
        # Set up the specific paramsB

    def get_paths(self, *params):
        # specific implementation to childB

I called it as:

b = childB(...)
b.load(...)

However, when I call `childB.load`, the `get_paths` called inside of it is `childA.get_paths`. I noticed that because I was getting an error when running `b.load`, so tried putting a break point with `ipdb` inside of `childB.get_paths`, but execution did not stop. But when I put the breaking point inside of `childA.get_paths`, it did break.

(2) Implement another class, `AB`, with the following definition:

```python
class AB(Parent):
    def __init__(self, *paramsP):
        super().__init__(*paramsP)

    @abstract method
    def get_paths(self, *params):
        # To be implemented in childA and childB

    def load(self, *params):
        # Implements the load from childA and childB

and then have childA and childB both inherit from AB instead, and implement their specific get_paths. However, this did not work as well, as I get the error: TypeError: Can't instantiate abstract class ChildB with abstract methods _AB__get_paths.

It is important to add I cannot implement load in Parent because there are other classes ( childC, childD, ...) with unique load implementations.. I just want to reuse this code between childA and childB

So, what is the correct way to solve this?

Upvotes: 1

Views: 602

Answers (1)

Slam
Slam

Reputation: 8572

Unless you want Parent to be inherited with abstract .load, just put implementation into Parent.

If .load is common only for this two childs – you can inherit from Parent with 3rd child like LoadMixin and inherit both Parent and mixin


One approach would be:

class LoadableChild(Parent):
    def load(self, *params): ...

class childA(LoadableChild):
    def get_paths(self, *params): ...

class childB(LoadableChild):
    def get_paths(self, *params): ...

Another is:

class LoadBase:
    def load(self, *params): ...

class childA(LoadBase, Parent):
    def get_paths(self, *params): ...

class childB(LoadBase, Parent):
    def get_paths(self, *params): ...

Note the order of inheritance in later approach, if you inherit parent as first superclass, there is no easy way around:

  • if your mixin inherits Parent – there's no unambiguous MRO
  • if mixin inherits object – there's instantiation error on abstract .load.

It's matter of preference, I'd say, for me personally first approach is cleaner

Upvotes: 1

Related Questions