1

I'm trying to replace the __init__ method of a derived class. But for some reason, the original __init__ is called, although the __dict__ shows the replaced function. If I call the __init__ manually, the replaced function is called... Example code:

class TheBase(object):

    def __new__(cls, *args, **kwargs):
        newInstance = super(TheBase, cls).__new__(cls)

        newInstance._origInit = newInstance.__init__
        newInstance.__init__ = newInstance._myInit
        print "Replaced the init of {} with {} ({})".format(newInstance, newInstance._myInit, id(newInstance._myInit))
        print newInstance.__dict__
        return newInstance

    def _myInit(self, *args, **kwargs):
        print "TheBase _myInit of {} ({})".format(self, id(self.__init__))
        self._origInit(*args, **kwargs)
        self._afterInit()

    def _afterInit(self):
        print "Init has passed..."
        # Do some magic here...


class MyDerived(TheBase):
    def __init__(self, great=False):
        TheBase.__init__(self)
        print "MyDerived __init__ of {} ({})".format(self, id(self.__init__))


class MyDerived2(MyDerived):
    def __init__(self):
        MyDerived.__init__(self, great=True)
        print "MyDerived2 __init__ of {} ({})".format(self, id(self.__init__))



sd = MyDerived()

print "--------------- manual init --------------"
sd.__init__()

Result:

Replaced the init of <__main__.MyDerived object at 0x00385390> with <bound method MyDerived._myInit of <__main__.MyDerived object at 0x00385390>> (35356224)
{'__init__': <bound method MyDerived._myInit of <__main__.MyDerived object at 0x00385390>>, '_origInit': <bound method MyDerived.__init__ of <__main__.MyDerived object at 0x00385390>>}
MyDerived __init__ of <__main__.MyDerived object at 0x00385390> (35213640)
--------------- manual init --------------
TheBase _myInit of <__main__.MyDerived object at 0x00385390> (35213640)
MyDerived __init__ of <__main__.MyDerived object at 0x00385390> (35213640)
Init has passed...

In my real world project, I want to start a thread (in the base class) but this must not run until the __init__ of the derived classes have finished. Because this is part of some refactoring, and the already existing derived classes are developed by other persons, I can’t modify their code (and start the thread there).

Replacing the __init__ in the class rather than the instance is also not possible, because sometimes the derived class is derived again (class MyDerived2(MyDerived):)

Has anyone an idea, why the original __init__ is called (and how to avoid that), or another way to solve the problem?

Alex G.
  • 13
  • 4

1 Answers1

0

Like all magic methods, __init__ is looked up on the class, not the instance.

If you want to customize and/or override magic methods, you should use a metaclass.

Example:

class TheMeta(type):
    def __init__(cls, name, bases, dct):
        def _myInit(self, *args, **kwargs):
            if type(self) is cls:
                print "_myInit of {} {} ({})".format(name, self, id(self.__init__))
                cls._origInit(self, *args, **kwargs)
                cls._afterInit(self)
            else:
                cls._origInit(self, *args, **kwargs)

        def _afterInit(self):
            print "Init has passed..."
            # Do some magic here...

        cls._origInit = cls.__init__
        cls.__init__ = _myInit
        cls._afterInit = _afterInit
        super(TheMeta, cls).__init__(name, bases, dct)

class TheBase(object):
    __metaclass__ = TheMeta

class MyDerived(TheBase):
    def __init__(self, great=False):
        TheBase.__init__(self)
        print "MyDerived __init__ of {} ({})".format(self, id(self.__init__))

class MyDerived2(MyDerived):
    def __init__(self):
        MyDerived.__init__(self, great=True)
        print "MyDerived2 __init__ of {} ({})".format(self, id(self.__init__))

sd = MyDerived()
d2 = MyDerived2()

Output (online):

_myInit of MyDerived <__main__.MyDerived object at 0xb72d97ec> (3075484484)
MyDerived __init__ of <__main__.MyDerived object at 0xb72d97ec> (3075190908)
Init has passed...
_myInit of MyDerived2 <__main__.MyDerived2 object at 0xb72d98ec> (3075484484)
MyDerived __init__ of <__main__.MyDerived2 object at 0xb72d98ec> (3073212204)
MyDerived2 __init__ of <__main__.MyDerived2 object at 0xb72d98ec> (3075190908)
Init has passed...
Community
  • 1
  • 1
ecatmur
  • 152,476
  • 27
  • 293
  • 366
  • Thanks for the hint, but I've tried that. It's not working with the second inheritance. Either the first or the second inherited class doesn’t work as expected (both level are instantiated sometimes). – Alex G. Jun 28 '16 at 13:26
  • @AlexG. take a look at the above; is that what you're after? – ecatmur Jun 28 '16 at 13:37
  • Thanks again, it looked good for some time, but than I've got a "maximum recursion RuntimeError", because the derived __init__ call their super __init__ – Alex G. Jun 28 '16 at 13:43
  • And to make it even more complicated, the init's have different signatures (updated the example above) – Alex G. Jun 28 '16 at 13:47
  • @AlexG. sorry, forgot to call `_origInit` on the class instead of the instance. Fixed above. (Also showing how to only print in the most derived class.) – ecatmur Jun 28 '16 at 14:14
  • That’s it! Thanks for your help, I really appreciate it. – Alex G. Jun 29 '16 at 06:09