2017-05-26 58 views
2

Nonify場我有一個任務:實施特殊的元類。在繼承

貫徹元類「ModelCreator」,即允許聲明類 字段的格式如下:

class Student(object): 
    __metaclass__ = ModelCreator 
    name = StringField() 

StringField - 一些對象表示,這個字段是 文本字段。所以必須有一個類,它的構造函數接收相應的屬性( 類型檢查和鑄造) 命名參數「名稱」,並將其存儲

所以,你可以輸入這樣的事情:

s = Student(name = 'abc') 
print s.name 

的類應該允許繼承,應該驗證類型, 方式,你不能寫一個數字文本字段。

這是我實現,但有一個與繼承類它的問題「名稱」字段是不是空的(因爲我希望它是)它臨危從以前的類名稱值。

class StringField(object): 
    def __init__(self, my_type, default=None): 
     self.type = my_type 
     self.name = None 
     if default: 
      self.default = default 
     else: 
      self.default = my_type() 

    def __set__(self, instance, value): 
     if isinstance(value, self.type): 
      setattr(instance, self.name, value) 
     else: 
      raise TypeError("value must be type of {}".format(self.type)) 

    def __get__(self, instance, owner): 
     return getattr(instance, self.name, self.default) 

    def __del__(self): 
     raise AttributeError("you can`t remove this attribute {}".format(self.name)) 


class ModelCreator(type): 
    def __new__(mcs, name, bases, diction): 
     socket = [] 
     for key, value in diction.iteritems(): 
      if isinstance(value, StringField): 
       value.name = "_{}".format(key) 
       socket.append(value.name) 

     def new(cls, *args, **kwargs): 
      for names in kwargs: 
       if '_{}'.format(names) in diction['__slots__']: 
        if isinstance(kwargs[names], diction[names].type): 
         diction[names].default = kwargs[names] 
        else: 
         raise TypeError('attr has other type') 
      return type(name, bases, diction) 

     diction["__slots__"] = socket 
     diction["__new__"] = new 
     return super(ModelCreator, mcs).__new__(mcs, name, bases, diction) 


class Student(object): 
    __metaclass__ = ModelCreator 
    name = StringField(str) 


class School(Student): 
    second_name = StringField(str) 


def main(): 
     st = Student(name = "Hello") 
     print st.name 
     st.name = "Vlad" 
     sc = School(second_name = "World") 
     print sc.second_name, sc.name 


if __name__ == '__main__': 
    main() 

此代碼打印

你好
世界你好

但應(按任務)打印

你好
世界無

的問題是:

爲什麼類型(ST)的回報 「型 '型'?」 (我認爲這是不實例類) 爲什麼類型(SC)的回報「類‘主要 .ModelCreator’」?

如何Nonify「名稱」字段的值,在「學生」類,所以(因爲它現在在某種程度上,即使在「SC」載),它只會被保存在「ST」?

回答

2

此代碼有點複雜 - 但它沒有做任何事情比你告訴它做什麼。

而且它做什麼,除了必要的描述符(即:包含__get____set__方法的類)和往常一樣元類機制被堵塞在__new__方法是錯誤的幾種方法的類。

原因之一,分配給類__new__結束其使用硬編碼調用type.執行new方法 - 這是所有的最錯誤的事情 - 作爲類型返回一個新的類 - 不是一個實例。在插入new方法的結束通話應該是object.__new__ - 或者更好的是,使用一種機制,調用隔壁班的__new____mro__(但不會說是微不足道的 - 因爲你必須找到在周圍的new方法,則需要封堵)元類__new__代碼。

無論如何 - 如果您希望使用這個元類的類自己成爲「類工廠」,那麼在那裏調用type纔有意義 - 它將不僅返回聲明字段的全部新類,還會返回已發送的類 - 在默認情況下。和呼叫類型就是爲什麼你看到type(st)返回type - 這是你的第一個問題。

然後,它仍然是錯了:new類方法,這就是所謂在每個instantation,設置默認屬性的descritor(即「田」) - 這默認將適用於同一類的所有其他實例 - 或從其繼承的其他類。您應該設置默認值,如果有的話,就調用你的StringField類 - 以及將成爲該類的__new__的方法,設置實例的值。

如果您首先調用超類__new__來獲取實際實例,然後通過傳入的關鍵字參數進行循環,並使用setattr作爲設置屬性的機制,那麼可以這樣做。使用setattr將確保正確調用StringField __set__方法。

因此,有怪異的很多事情在這段代碼中,而是試圖修復它通過重新書面方式的元類__new__是或多或少走:

def __new__(mcs, name, bases, diction): 
    socket = set() 
    # mechanism to inherit classes that make use of sockets: 
    for base in bases: 
     if hasattr(base, '__slots__'): 
      socket.update(base.__slots__) 
    for key, value in diction.iteritems(): 
     if isinstance(value, StringField): 
      value.name = "_{}".format(key) 
      socket.add(value.name) 


    def __new__(cls, *args, **kwargs): 
     # A working __new__ mechanism that respects inheritance. 
     for supercls in cls.__mro__[1:]: 
      if '__new__' in supercls.__dict__: 
       # don't pass args and kwargs up. 
       # unless really there is distinct mechanism 
       # to use args and kwargs than the StringField 
       # class. 
       # otherwise this would break any `__init__` 
       # method you put in your classes. 
       instance = supercls.__new__(cls) 
       break # the last class in __mro__ is object which always has '__new__' 

     for names in kwargs: 
      if '_{}'.format(names) in cls.__slots__: 
       # no need to typecheck here. StringField's __set__ does that 
       setattr(instance, kwargs[names]) 
     return instance 

    diction["__slots__"] = list(socket) 
    diction["__new__"] = __new__ 
    return super(ModelCreator, mcs).__new__(mcs, name, bases, diction) 

這就是說,你真的不應該浪費你在這一點上學習在Python 2.7等先進的機制時(2017年) - Python 2中最後一個版本是在2010年,這將是保養的2020年 - 這些機制有所改善,得到了在3.x系列好了很多。在Python 3.6,與__set_name__描述符的功能和新__init_subclass__機制,你甚至不需要使用自定義元類爲你在這裏預期的結果。