user6037143
user6037143

Reputation: 566

Python - Initializing parent classes

I am trying to figure how to use super() to initialize the parent class one by one based on condition.

class A:
    def __init__(self, foo):
        self.foo = foo

class B:
    def __init__(self, bar):
        self.bar == bar

class C(A,B):
    def __init__(self):
        #Initialize class A first.
        #Do some calculation and then initialize class B 

How do I use super() in class C such that it only initializes class A first, then I do some calc and call super() to initialize class B

Upvotes: 2

Views: 1081

Answers (2)

chepner
chepner

Reputation: 532418

You cannot do what you ask for in C.__init__, as super doesn't give you any control over which specific inherited methods get called, only the order in which they are called, and that is controlled entirely by the order in which the parent classes are listed.

If you use super, you need to use it consistently in all the classes. (That's why it's called cooperative inheritance.) Note this means that C cannot inject any code between the calls to A.__init__ and B.__init__.

__init__ is particularly tricky to implement correctly when using super, because a rule of super is that you have to expected arbitrary arguments to be passed, yet object.__init__() doesn't take any arguments. You need each additional argument to be "owned" by a particular root class that is responsible for removing it from the argument list.

class A:
    def __init__(self, foo, **kwargs):
        # A "owns" foo; pass everything else on
        super().__init__(**kwargs)
        self.foo = foo

class B:
    def __init__(self, bar, **kwargs):
        # B "owns" bar; pass everything else on
        super().__init__(**kwargs)
        self.bar = bar

class C(A,B):
    def __init__(self):
        # Must pass arguments expected by A and B
        super().__init__(foo=3, bar=9)

The MRO for C is [A, B, object], so the call tree looks something like this:

  1. C.__init__ is called with no arguments
  2. super() resolves to A, so A.__init__ is called with foo=3 and bar=9.
  3. In A.__init__, super() resolves to B, so B.__init__ is called with bar=9.
  4. In B.__init__, super() resolves to object, so object.__init__ is called with no arguments (kwargs being empty)
  5. Once object.__init__ returns, self.bar is set to bar
  6. Once B.__init__ returns, self.foo is set to foo
  7. Once A.__init__ returns, C.__init__ finishes up

OK, the first sentence isn't entirely true. Since neither A nor B, as currently written, use super, you might be able to assume that an appropriate use of super will simply call one parent function and immediately return.

class A:
    def __init__(self, foo):
        self.foo = foo

class B:
    def __init__(self, bar):
        self.bar == bar

class C(A,B):
    def __init__(self):
        super(A, self).__init__(foo=3)
        # Do some calculation
        super(B, self).__init__(bar=9)

I'm not entirely certain, though, that this doesn't introduce some hard-to-predict bugs that could manifest with other subclasses of A, B, and/or C that attempt to use super properly.

Upvotes: 4

Philip Tzou
Philip Tzou

Reputation: 6458

You can actually refer the base classes explicitly:

class A:
    def __init__(self, foo):
        self.foo = foo

class B:
    def __init__(self, bar):
        self.bar == bar

class C(A,B):
    def __init__(self):
        A.__init__(self, 'foovalue')
        # Do some calculation
        B.__init__(self, 'barvalue')

Upvotes: 0

Related Questions