2017-05-26 59 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__机制,你甚至不需要使用自定义元类为你在这里预期的结果。