4

I'm trying to get the order of __init__ executions right. The hierarchy consists of two branches. The left branch implements some abstract methods of the right branch. In the right branch there is also a split. Now I'd like them to execute: left first top to bottom, and then right top to bottom.

Is there a way to achieve that in Python 3?

So basically the following should print the numbers in order (which it doesn't):

from abc import abstractmethod, ABCMeta

class L1:
    def __init__(self):
        super().__init__()
        print(1)

class R1(metaclass=ABCMeta):
    def __init__(self):
        super().__init__()
        print(3)

    @abstractmethod
    def m(self):
        pass

class L2(L1):
    def __init__(self):
        super().__init__()
        print(2)

    def m(self):
        pass

class R2a(R1):
    def __init__(self):
        super().__init__()
        print(4)

class R2b(R1):
    def __init__(self):
        super().__init__()
        print(4)

class X(L2, R2a):
    pass

class Y(L2, R2b):
    pass

x = X()
print("--")
y = Y()

Unfortunately I cannot change the order of L2 and R2 since L2 implements abstract methods of R2 (there will be errors if I change that).

Is it possible?

(Do you know a good way to think about MRO to solve such kind of problem easy?)

EDIT: I thought I doesn't matter, but I want to note that in my real structure, there is actually a diamond on the L side like L2a(L1), L2b(L1) and L2(L1a, L2a). That's to completely split some functionality.

2
  • You should derive you top level classes from object: class L1 (object): in order to get "new style classes". Commented Nov 9, 2012 at 12:52
  • @Ber, no, he's using Python 3 (otherwise the metaclass syntax wouldn't work) so he doesn't have to inherit explicitly from object Commented Nov 9, 2012 at 13:13

2 Answers 2

3

How about:

from abc import abstractmethod, ABCMeta

class L1:
    def __init__(self):
        self.init()
        super().__init__()
    def init(self):
        print(1)

class R1(metaclass=ABCMeta):
    def __init__(self):
        print(3)
        super().__init__()

    @abstractmethod
    def m(self):
        pass

class L2(L1):
    def __init__(self):
        super().__init__()
    def init(self):
        super().init()
        print(2)

    def m(self):
        pass

class R2a(R1):
    def __init__(self):
        super().__init__()
        print(4)


class R2b(R1):
    def __init__(self):
        super().__init__()
        print(4)

class X(L2, R2a):
    pass

class Y(L2, R2b):
    pass

x = X()
print([cls.__name__ for cls in X.mro()])

which yields

1
2
3
4
['X', 'L2', 'L1', 'R2a', 'R1', 'object']

I don't think you can change the MRO, ['X', 'L2', 'L1', 'R2a', 'R1', 'object']. But you can choose to place initialization statements before or after super().__init__().

Placing the print statements before super().__init__() gives you the print statements in the order of the MRO. Placing them afterwards, reverses the order.

You want the order partially reversed: L1 before L2, R1 before R2a.

When we reach L1, by calling self.init(), we get to start over at the beginning of the MRO chain, and call init in the desired order: L1, L2.

Sign up to request clarification or add additional context in comments.

Comments

1

I prefer to avoid the use of super:

class A:
    def __init__(self):
        print('init A')

class B:
    def __init__(self):
        print('init B')

class C(B,A):
    def __init__(self):
        A.__init__(self)
        B.__init__(self)
        print('init C')

c = C()

This prints:

init A
init B
init C

So for your specific situation just call _init_in whatever order you want. Really. just try it. Diamond or no diamond it will work out fine.

EG:

class X(L2,R2a):
    def __init__(self):
        L1.__init__(self)
        L2.__init__(self)
        R1.__init__(self)
        R2a.__init__(self)

If you need to call init for stuff higher up in the hierarchy and you are afraid of calling certain class's init functions repeatedly for certain objects then just make some boolean flags.

For example:

class L1:
    def __init__(self):
        try:
            if self.l1_initialised:
                return
        except NameError:
            self.l1_initialised = True
        blah blah blah

3 Comments

But the problem is diamond structures, where superclass would be called twice. I actually didnt mention that my L2 class also has a splitting.
@Sheena: you should use super() to make hte method resolution order (MRO) algorithm work. python.org/download/releases/2.3/mro
@Ber: thanks. looking into it... I've never had a problem with this way of doing things before though

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.