pythonmultiple-inheritancesuper

Python initialization with multiple inheritance


I have the following class hierarchy. The goal is for the calling code to choose either a base Foo object or a Foobar object that also provides the additional Bar functionality.

class Foo:
    def __init__(self):
        self.foo = 'foo'
        
class Bar:
    def __init__(self, attr):
        self.attr = attr

    def bar(self):
        print(self.attr + 'bar')
        
class Foobar(Foo, Bar):
    def __init__(self):
        super().__init__(self.foo)

But when I try to run it:

>>> fb = Foobar()
AttributeError: 'Foobar' object has no attribute 'foo'

What's the right way to initialize Foobar? I've read a number of articles and SO posts about initialization with multiple inheritance, but none where one base constructor requires a property of the other base class.

EDIT:

My actual use case is derived from https://www.pythonguis.com/tutorials/pyside6-plotting-matplotlib/. "Bar" is actually FigureCanvasQTAgg and "Foobar" corresponds to MplCanvas. Foobar must derive from FigureCanvasQTAgg because the Foobar object will be passed to a bunch of PySide6 code that uses attributes I don't know about. I'm trying to break out the regular matplotlib code into another base class (Foo) to I can make an alternate front end that doesn't use PySide6, but there may be a different way to achieve this goal.

EDIT 2:

Looks like the whole approach may be flawed. It would certainly be less taxing for my little brain to create foo in a separate function before trying to instantiate either a Foo or a Foobar.


Solution

  • A demo for you(Just to tell why your Foobar does not have a attr attribution, don't don't do it in real code):

    class Foo:
        def __init__(self):
            self.foo = 'foo'
            
    class Bar:
        def __init__(self, attr):
            self.attr = attr
    
        def bar(self):
            print(self.attr + 'bar')
            
    class Foobar(Foo, Bar):
        def __init__(self):
            super().__init__() # Will use: Foo.__init__
            Bar.__init__(self, self.foo)
    
    Foobar().bar()
    # foobar
    

    I would rather do:

    class Foo:
        foo = "foo"
    
    
    class Bar:
        def __init__(self, attr) -> None:
            self.attr = attr
    
        def bar(self) -> None:
            print(self.attr + "bar")
    
    
    class FooBar(Foo, Bar):
        def __init__(self) -> None:
            super().__init__(self.foo)
    
    
    FooBar().bar()
    # foobar