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

问题描述:

我试图替换派生类的__init__方法。 但由于某种原因,原来的__init__被调用,虽然__dict__显示替换函数。 如果我手动调用__init__,替换函数调用... 示例代码:在__new__构造函数中替换已创建对象的__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__被称为(以及如何避免那),还是另一种解决问题的方式?

像所有神奇的方法一样,__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... 
+0

感谢您的提示,但我已经试过了。它不适用于第二个继承。第一个或第二个继承类无法按预期工作(有时会将这两个级别实例化)。 –

+0

@AlexG。看看上面的内容;那是你在追求什么? – ecatmur

+0

再次感谢,它看起来不错,但我得到了“最大递归RuntimeError”,因为派生的__init__调用了它们的超级__init__ –