2016-06-28 81 views
1

我試圖替換派生類的__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__被稱爲(以及如何避免那),還是另一種解決問題的方式?

回答

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

感謝您的提示,但我已經試過了。它不適用於第二個繼承。第一個或第二個繼承類無法按預期工作(有時會將這兩個級別實例化)。 –

+0

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

+0

再次感謝,它看起來不錯,但我得到了「最大遞歸RuntimeError」,因爲派生的__init__調用了它們的超級__init__ –

相關問題