Everywhere I look everyone keeps saying how great super() is. But, I'm leaning toward not using super() because it makes everything much more complicated than I would like. I've seen some popular examples of using super() but they never seem to show passing positional required arguments into the base class constructors.
I know of the diamond problem in Python, and super() prevents calling the base_base class twice. (in this case Class A)
Is it so bad that Class A's constructor is called twice? (i.e., Case 2)
Below is my code for the two cases.
Case 1: Using super()._ init _()
#abstractclass
class A(object):
#abstractmethod
def __init__(self, for_a, *args):
self.val_a = for_a
#abstractclass
class B(A):
#abstractmethod
def __init__(self, for_a, for_b, for_c):
super().__init__(for_a, for_c)
self.val_b = for_b
#abstractclass
class C(A):
#abstractmethod
def __init__(self, for_a, for_c):
super().__init__(for_a)
self.val_c = for_c
class D(B, C):
def __init__(self, for_a, for_b, for_c, for_d):
super().__init__(for_a, for_b, for_c)
self.val_d = for_d
class E(B):
def __init__(self, for_a, for_b, for_e, *args):
super().__init__(for_a, for_b, *args)
self.val_e = for_e
newobject1 = D(1, 2, 3, 4)
newobject2 = E(10, 11, 12, 0)
Case 2: Static - Using base._ init _(self)
#abstractclass
class A(object):
#abstractmethod
def __init__(self, for_a):
self.val_a = for_a
#abstractclass
class B(A):
#abstractmethod
def __init__(self, for_a, for_b):
A.__init__(self, for_a)
self.val_b = for_b
#abstractclass
class C(A):
#abstractmethod
def __init__(self, for_a, for_c):
A.__init__(self, for_a)
self.val_c = for_c
class D(B, C):
def __init__(self, for_a, for_b, for_c, for_d):
B.__init__(self, for_a, for_b)
C.__init__(self, for_a, for_c)
self.val_d = for_d
class E(B):
def __init__(self, for_a, for_b, for_e):
super().__init__(for_a, for_b)
self.val_e = for_e
newobject1 = D(1, 2, 3, 4)
newobject2 = E(10, 11, 12)
Is it so bad that Class A's constructor is called twice?
Absolutely, if the constructor is slow, or if it's non-idempotent, or if it acquires resources like file handles or database connections. In a more complex inheritance structure, you might also get constructors called 4 times, or 6, or as many times as there are paths up the inheritance graph from the child to the ancestor.
I'm leaning toward not using super() because it makes everything much more complicated than I would like
It's not super
making things complicated - it's multiple inheritance. Avoiding super
in multiple inheritance just gives you different, probably-worse problems than super
's problems.
I've seen some popular examples of using super() but they never seem to show passing positional required arguments into the base class constructors.
That's because positional constructor arguments don't work well with multiple inheritance. If you want to use a multiple inheritance structure like this, try making the arguments keyword-only and using **kwargs
to pass through unrecognized arguments for other classes to handle. "Python's super() considered super" has some good advice for how to make it work.