1
class Foo(Baz, Qux):
    def __init__(self, bar, *args, **kwargs):
        super(Foo, self).__init__(bar=bar, *args, **kwargs)

class Baz:   
    def __init__(self, bar, *args, **kwargs):
        self.bar = bar

This is the structure of my classes with the Foo class inheriting the Baz class. However when I try running the code, I get the following error Foo object has no attribute bar

From what I understand, the super(childclass, self).__init__(parent, attributes) should initialize the parent class as well. Why should bar be present in the child class too?

I followed the following stack answers to implement this: Calling parent class __init__ with multiple inheritance, what's the right way?

EDIT

class Qux:   
    def __init__(self, xyz, *args, **kwargs):
        self.xyz = xyz
    
    def print_barqux(self):
        print(self.xyz)
        
class Baz:   
    def __init__(self, bar, *args, **kwargs):
        self.bar = bar
        
class Foo(Baz, Qux):
    def __init__(self, bar, xyz, *args, **kwargs):
        super(Foo, self).__init__(bar=bar, xyz=xyz)

    def printbar(self):
        super(Foo, self).print_barqux()
        
        
foo = Foo(bar="abcd", xyz="abcdef")
print(foo.printbar())

Output:

Traceback (most recent call last):
  File "./prog.py", line 21, in <module>
  File "./prog.py", line 17, in printbar
  File "./prog.py", line 6, in print_barqux
AttributeError: 'Foo' object has no attribute 'xyz'

Adding the error stacktrace with code

Swastik Udupa
  • 316
  • 3
  • 17
  • Shouldn't ```Baz``` be before ```Foo```? – ewokx May 07 '21 at 05:17
  • Does the ordering matter? Anyways, in reality they are in different files. This was just a representation. – Swastik Udupa May 07 '21 at 05:18
  • Have you tried the above code? Please provide a minimal running example to demonstrate the error as well as the full traceback error. – ewokx May 07 '21 at 05:21
  • 3
    _Does the ordering matter?_ Heck, yes. `Baz` has to be fully defined before you can reference it as a base class for `Foo`. – Tim Roberts May 07 '21 at 06:09
  • 2
    If you have multiple inheritance like this, then `super` might not be the right answer. You can specifically target `Baz` by doing `Baz.__init__(self, bar=bar, *args, **kwargs)` – Tim Roberts May 07 '21 at 06:11
  • 1
    @TimRoberts well, the whole *point* of `super` is to aid in cooperative multiple inheritance. – juanpa.arrivillaga May 07 '21 at 06:38
  • @ewong I have added a sample code with the traceback. – Swastik Udupa May 07 '21 at 07:03
  • I always get the MRO confused, but unless `Qux` calls `super().__init__` as well, `Baz` might not be initialised… – deceze May 07 '21 at 07:08
  • But Qux doesn't have any parent classes right? I thought super was only for initialising parent classes. Am I missing anything? – Swastik Udupa May 07 '21 at 07:09
  • Anyways, I tried adding `super(Qux, self).__init__()` as well as `super(Qux, self).__init__(xyz=xyz)` the error seems to persist – Swastik Udupa May 07 '21 at 07:11
  • 4
    Super calls the next class in the MRO, which might be a parent class, or the other parent of a child class. In this case, calling `super()` in `Qux` calls `__init__` in `Baz` – mousetail May 07 '21 at 07:13
  • I had a misconception wherein I thought both the parent classs where initialized through the child class itself. Slightly confusing. Makes sense. Thanks @mousetail – Swastik Udupa May 07 '21 at 07:15
  • 1
    @SwastikUdupa no `super` *calls the next class in the method resolution order* – juanpa.arrivillaga May 09 '21 at 06:11

1 Answers1

3

Every class has to call super.__init__:

class Qux:   
    def __init__(self, xyz, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.xyz = xyz
    
    def print_barqux(self):
        print(self.xyz)
        
class Baz:   
    def __init__(self, bar, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.bar = bar
        
class Foo(Baz, Qux):
    def __init__(self, bar, xyz, *args, **kwargs):
        super().__init__(bar=bar, xyz=xyz)

    def printbar(self):
        super().print_barqux()
        
        
foo = Foo(bar="abcd", xyz="abcdef")
print(foo.printbar())
Daniel
  • 42,087
  • 4
  • 55
  • 81