实现特殊元类。在继承类中设为非。

2024-06-25 07:03:27 发布

您现在位置:Python中文网/ 问答频道 /正文

我有个任务:

Implement metaclass "ModelCreator", that allows to declare class fields in the following form:

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

Where StringField - some object that indicates, that this field is text field. So there must be a class, whose constructor receives named argument "name" and stores it in corresponding attribute (with type check and cast)

So you can type something like this:

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

The class should allow inheritance and should verify the types such way, that you cannot write a number to text field.

这是我的实现,但是继承类有问题,它的“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()

这个密码打印出来了

Hello
World Hello

但它应该(按任务)打印

Hello
World None

问题是:

Why the type(st) returns " type 'type' "? (I thought it is instance not a class) Why the type(sc) returns " class 'main.ModelCreator' "?

How to Nonify the value of "name" field in the "Student" class, so it will only be saved in "st" (cause it is now somehow contained even in "sc")?


Tags: thenameinselfdefaultifnamesvalue
1条回答
网友
1楼 · 发布于 2024-06-25 07:03:27

这段代码有点复杂,但它所做的并不是你让它做的。在

它所做的,除了所需的描述符(即:包含__get____set__方法的类)和通常的元类机制之外,它还将一个__new__方法插入到在多个方面出错的类中。在

{cd4{a>对于一个新的,{cd4}类型的调用被分配到一个新的cd3}类型的调用中,{cd4}将返回一个新的new方法末尾的调用应该是object.__new__-或者更好,使用一种机制,在其__mro__上调用下一个类中的__new__(但这并不是一件小事,因为您必须在围绕您所插入的new方法的元类__new__代码中找到)。在

总之,如果您希望使用这个元类的类成为自己的“类工厂”,那么在那里调用type才是有意义的——这将返回整个新类,不仅包含声明的字段,而且包含发送的默认值。调用类型是您看到type(st)返回{}的原因,这是您的第一个问题。在

那么,它仍然是错误的:在每个实例中调用的new类方法将default属性设置为描述符(即“field”)—并且该默认值将应用于同一类的其他实例化或继承自该类的其他类。您应该在调用您的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)

也就是说,现在(2017年)您不应该浪费时间在研究Python2.7中的这种高级机制上—Python2的上一个版本是在2010年,它将在2020年停止维护—这些机制已经改进,并且在3.x系列中得到了更好的改进。在Python3.6中,有了__set_name__描述符功能和新的__init_subclass__机制,您甚至不需要为这里的预期结果使用自定义元类。在

相关问题 更多 >