1

I have the following simplified class structure

class A():
    def foo(self):
        self.bar()

    def bar(self):
        print("A bar")

class B(A):
    def bar(self):
        print("B bar")

class C(A):
    def bar(self):
        print("C bar")

Now I also have a modified version of A

class ModA():
    def foo(self):
        print("hi")
        self.bar()

I now also want a additional versions of B and C that have ModA as parent class instead of A.

class ModB(ModA):
    def bar(self):
        print("B bar")

class ModC(ModA):
    def bar(self):
        print("C bar")

How do I define ModB and ModC without duplicating their entire definition except for the first line that defines the parents?

Deb
  • 1,098
  • 10
  • 22
  • You want to duplicate `B` and `C`? – ywbaek Apr 26 '20 at 13:37
  • 5
    It is impossible to answer without knowing what your "slight changes" might be, but in general, look at multiinheritance - `class ModB(ModA, B):` might be what you need. – Błotosmętek Apr 26 '20 at 13:38
  • 1
    `ModB` can inherit from `ModA` **and** `B` but you need to get the order correct, `ModB(ModA,B)` or `ModB(B,ModA)`, so that the mro will do what you need but as previously commented it is impossible to tell. Sounds like it is getting complicated - maybe it needs to be flattened a bit. If you make a toy example that you can include as an [mre] It will be easier for someone to respond. – wwii Apr 26 '20 at 13:49
  • Is there anything common to all six classes? Maybe that stuff needs to be its own class which can be used as a mixin class, are there other commonalties that can be put in another class? [MRO](https://www.python.org/download/releases/2.3/mro/). [What is a mixin, and why are they useful?](https://stackoverflow.com/questions/533631/what-is-a-mixin-and-why-are-they-useful) – wwii Apr 26 '20 at 13:53
  • @Błotosmętek That does indeed what I needed. I included a minimal example. As @wwii mentions I was worried which order was needed and if it could even handle my case e.g. that `class ModB(ModA, B)` would use again the base `A.bar` instead of `B.bar` but making a MWE showed me that your order still calls `B.bar`. Feel free to include it as answer and I will accept it. – Deb Apr 26 '20 at 14:51
  • In your revision should `class ModA():` be `class ModA(A)`? – wwii Apr 27 '20 at 16:15

2 Answers2

1

It is impossible to answer without knowing what your "slight changes" might be, but in general, look at multi-inheritance - class ModB(ModA, B): might be what you need.

class ModB(ModA, B):
    pass
>>> b = ModB()
>>> b.foo()
hi
B bar
Deb
  • 1,098
  • 10
  • 22
Błotosmętek
  • 12,717
  • 19
  • 29
1

Based on your toy example here is one way to do it.

class FooBase:
    def foo(self):
        self.bar()
class FooMod:
    def foo(self):
        print("hi")
        self.bar()

class A(FooBase):
    def bar(self):
        print("A bar")

class B(FooBase):
    def bar(self):
        print("B bar")

class C(FooBase):
    def bar(self):
        print("C bar")

class ModA(FooMod,A):
    pass
class ModB(FooMod,B):
    pass
class ModC(FooMod,C):
    pass

class BadModA(A,FooMod):
    pass

for thing in [A,B,C,ModA,ModB,ModC,BadModA]:
    thing = thing()
    print(thing.foo())
    print('**********')

You have to be careful how you order the base classes notice BadModA doesn't work because it finds the foo method in FooBase before looking in FooMod. You can see that in their Method Resolution Order's

In [15]: ModA.__mro__
Out[15]: [__main__.ModA, __main__.FooMod, __main__.A, __main__.FooBase, object]

In [16]: BadModA.__mro__
Out[16]: (__main__.BadModA, __main__.A, __main__.FooBase, __main__.FooMod, object)

While this works it seems like you could get yourself in trouble. you always have to remember which order to use - maybe someone will comment.


Here is another way, it flattens the structure out a bit, five bases and you mix them up

class FooBase:
    def foo(self):
        self.bar()
class FooMod:
    def foo(self):
        print("hi")
        self.bar()

class Abar:
    def bar(self):
        print("A bar")
class Bbar:
    def bar(self):
        print("B bar")
class Cbar():
    def bar(self):
        print("C bar")

class A(FooBase,Abar):
    pass
class B(FooBase,Bbar):
    pass
class C(FooBase,Cbar):
    pass

class ModA(FooMod,Abar):
    pass
class ModB(FooMod,Bbar):
    pass
class ModC(FooMod,Cbar):
    pass
wwii
  • 23,232
  • 7
  • 37
  • 77