替换__new__构造函数中创建的对象的__init__

时间:2016-06-28 12:42:28

标签: python-2.7

我正在尝试替换派生类的__init__方法。 但出于某种原因,虽然__init__显示了替换函数,但调用了原始__dict__。 如果我手动调用__init__,则调用替换的函数... 示例代码:

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__()

结果:

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...

在我的真实世界项目中,我想启动一个线程(在基类中),但是直到派生类的__init__完成后才能运行。因为这是一些重构的一部分,并且已经存在的派生类是由其他人开发的,所以我无法修改它们的代码(并在那里启动它)。

替换类中的__init__而不是实例也是不可能的,因为有时派生类会再次派生(class MyDerived2(MyDerived):

有没有人知道,为什么调用原始的__init__(以及如何避免这种情况),或者解决问题的其他方法?

1 个答案:

答案 0 :(得分:0)

与所有魔法方法一样,__init__在类上查找,而不是实例。

如果要自定义和/或覆盖魔术方法,则应使用metaclass

示例:

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()

输出(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...