2017-05-19 59 views
0

我已經創建了一個元類,它覆蓋了默認的init函數以添加一個私有變量。蟒蛇 - 元類,基礎構造函數不會被調用

問題是如果我沒有明確地調用它,我自己不會調用基類的函數init

看看下面

class Base(object):       
    def __init__(self, x, y):     
     self._xvar = x       
     self._yvar = y       
     print("THIS IS THE CONSTRUCTOR", x, y) 


class Derived(Base):       
    pass 


def main():     
    derived = Derived(11, 20) 

的例子這將打印

這是構造11 20

即使派生類中從未稱

super().__init__(x, y) 

這是我的元類:

class MetaTemplateContent(type): 
    def __new__(mcs, name, base, dct): 
     # This is the original init function 
     orig_init = dct.get("__init__") 

     # this variable will hold all the functions that has a decorator 
     # If the function name is _content_wrapper it will handle static methods as well 
     decorators = [] 
     for _, value in dct.items(): 
      if isinstance(value, types.FunctionType): 
       if value.__name__ == "_content_wrapper": 
        decorators.append(value) 
      elif isinstance(value, staticmethod): 
       function = value.__func__ 
       if function.__name__ == "_content_wrapper": 
        decorators.append(function) 

     # This is our wrapper init function which will act as a stub 
     def init_wrapper(self, *args, **kwargs): 
      if orig_init: 
       orig_init(self, *args, **kwargs) 

      # This is the local variable I want to add to each instance 
      # and fill it with all the functions that has the decorator 
      self._callbacks = getattr(self, "_callbacks", []) 
      self._callbacks.extend(decorators) 

     # replace the original init with our stub 
     dct["__init__"] = init_wrapper 
     return type.__new__(mcs, name, base, dct) 

如果我要我們的基類重寫此:

class Base(object, metaclass=MetaTemplateContent): 
    def __init__(self, x, y):      
     self._xvar = x        
     self._yvar = y        
     print("THIS IS THE CONSTRUCTOR", x, y)  


class Derived(Base):        
    pass 


def main():     
    derived = Derived(11, 20) 

什麼都不會被打印,因爲基類的構造不會被調用。

添加super()。然而初始化(X,Y)的派生的構造函數就可以了:

class Derived(Base):   
    def __init__(self, x, y): 
     super().__init__(x, y) 

但這是多餘的,我知道,我已經錯過了一些重要的東西在這裏。 爲什麼不調用基類構造函數?

這是蟒蛇3.5.3

回答

2

的基類方法被稱爲在兩種情況下:

  1. 你明確地把它
  2. 子類沒有定義(即覆蓋)的方法

沒有你的元類,情況2適用。正如您已經注意到的,您的元類爲使用該元類的每個類創建了一個__init__。因此,對於您的元類,情況2不再適用,並且基類構造函數不會被調用。

換句話說,如果一個類定義了__init__,它必須明確地調用基類版本(如果需要的話)。你的元類使它每個類都定義了__init__,所以如果你想調用基地__init__你必須明確地調用它。

您可以修改您的元類,以便只有在沒有orig_init的情況下init包裝器才調用超類版本。要做到這一點,init封裝需要訪問類的,所以你需要周圍的東西掉,讓你在init封裝修補後創建類:

class MetaTemplateContent(type): 
    def __new__(mcs, name, base, dct): 
     # This is the original init function 
     orig_init = dct.get("__init__") 

     # this variable will hold all the functions that has a decorator 
     # If the function name is _content_wrapper it will handle static methods as well 
     decorators = [] 
     for _, value in dct.items(): 
      if isinstance(value, types.FunctionType): 
       if value.__name__ == "_content_wrapper": 
        decorators.append(value) 
      elif isinstance(value, staticmethod): 
       function = value.__func__ 
       if function.__name__ == "_content_wrapper": 
        decorators.append(function) 

     # make the class first 
     cls = type.__new__(mcs, name, base, dct) 

     # This is our wrapper init function which will act as a stub 
     def init_wrapper(self, *args, **kwargs): 
      if orig_init: 
       orig_init(self, *args, **kwargs) 
      else: 
       super(cls, self).__init__(*args, **kwargs) 

      # This is the local variable I want to add to each instance 
      # and fill it with all the functions that has the decorator 
      self._callbacks = getattr(self, "_callbacks", []) 
      self._callbacks.extend(decorators) 

     # replace the original init with our stub 
     cls.__init__ = init_wrapper 
     return cls 
+0

不錯的傢伙,乾杯 – Crippin