Ryabchenko Alexander
Ryabchenko Alexander

Reputation: 12390

PyLint W0231 super-init-not-called with __init__ of grandparent

I make a Child class that use methods from Parent class witch not present in GrandParent. But in the Child.__init__() class I don't want to get some effects from Parent.__init__() and instead I call call GrandParent.__init__()

class GrandParent():
    ...

class Parent(GrandParent):    
    def __init__(self):
        GrandParent.__init__(self)
        ...


class Child(Parent):
    def __init__(self):
        GrandParent.__init__(self)
        ...

With this code i got next warnings from pylint

 W0233: __init__ method from a non direct base class 'GrandParent' is called (non-parent-init-called)
 W0231: __init__ method from base class 'Parent' is not called (super-init-not-called)

What is the correct way to fix this warnings ?

(or disable this warnings for Child is proper solution ?)

class Child(Parent):
    def __init__(self):  # pylint: disable=W0233
        GrandParent.__init__()   # pylint: disable=W0231
        ...  

Upvotes: 2

Views: 7530

Answers (1)

Peter Badida
Peter Badida

Reputation: 12189

You can try multiple ways such as:

GrandParent.__init__(self)
super().super().__init__(self)
super(GrandParent, self).__init__()

but the error will still be there either as:

  • super-init-not-called
  • or bad-super-call (super(GrandParent, self)...)
  • or even as useless super call to the immediate parent.

I suppose it's meant as a warning for not initializing the immediate class in the class __mro__ by mistake if you work with inheritance and you can to turn off it with a pylint disable:

# pylint: disable=super-init-not-called

but you are messing with the __mro__ chain by such calls.

What you can do is allow cascading through the Child.__mro__ items upward i.e. Child -> Parent -> GrandParent (-> object) while skipping by class reference in an __init__:

class Parent(GrandParent):
    def __init__(self):
        super().__init__()
        if self.__class__ == Child:
            return

which does not trigger the PyLint warnings and allows the correct behavior explicitly and without skipping any required items in __mro__.

IMO it's much cleaner preserving the __mro__ which seems what PyLint's intent is as well by super-init-not-called warning. On the other hand, it's kind of tricky and hides the code in a completely different class and that will make the debugging in the future annoying if some properties from Parent will be missing and no comment/disable warning are present in Child class.

If you want to pull the methods out of the GrandParent class only, simply go with the "mixins" classes which is what Django REST Framework is using and don't write any __init__ for those (or if, then don't forget super().__init__() to continue in __mro__ chain!).

class GrandParent:
    def __init__(self):
        print("> GrandParent __init__()")

class Mixin:
    def mymethod(self):
        print("Hey")

class Parent(Mixin, GrandParent):
    def __init__(self):
        print("> Parent __init__()")
        super().__init__()

class Child(Mixin, GrandParent):
    def __init__(self):
        print("> Child __init__()")
        super().__init__()

Child().mymethod()
# > Child __init__()
# > GrandParent __init__()
# Hey

Parent().mymethod()
# > Parent __init__()
# > GrandParent __init__()
# Hey

which will basically be the same situation you have with Child -> Parent -> GrandParent but Mixin itself doesn't have (any useful) __init__ which will cause your code only from GrandParent to execute due to Mixin.__init__() being empty hence doing visually the same thing as with if self.__class__ == Child: return but without any hacks + with increased readability.

Upvotes: 2

Related Questions