Метакласс не инициализирует новый класс - PullRequest
0 голосов
/ 12 июня 2018

все!У меня большая проблема с моим кодом.Я пытаюсь написать простой код как простую структуру для понимания метапрограммирования.И у меня следующая проблема.

doc1 = Document()
doc1.id = 20
doc2 = Document()
print (doc2.id) # show 20, but it's wrong!!! It should be 0

Это мой список кодов

class BaseModel(type):
def __new__(cls, name, bases, attrs,  **kwargs):
    instance = super().__new__

    parents = [b for b in bases if isinstance(b, BaseModel)]
    if not parents:
        return instance(cls, name, bases, attrs)

    module_ = attrs.pop('__module__')
    attrs_ = {'__module__': module_}

    classcell_ = attrs.pop('__classcell__', None)
    if classcell_ is not None:
        attrs_['__classcell__'] = classcell_

    for key, value in attrs.items():
        if not isinstance(value, Field):
            continue

        print (value.__dict__)
        attrs_[key] = value

    instance = instance(cls, name, bases, attrs_, **kwargs)

    return instance


class Model(metaclass=BaseModel):
    pass

class Field:
    def __init__(self, value=0):
        self._value = value

    def __get__(self, instance, owner):
        return self._value

    def __set__(self, instance, value):
        self._value = value

class Document(Model):
    id = Field()

Что происходит?Я думаю, что основной проблемой является новый метод в метаклассе.Я прав?Как это исправить?

1 Ответ

0 голосов
/ 13 июня 2018

То, что вы пытаетесь достичь с помощью своего метакласса, сбивает с толку, хотя и не является источником проблемы.Реализация уникальных идентификаторов не требует метакласса, для этого нужен только хорошо продуманный дескриптор.

В вашем случае ваш Field дескриптор отслеживает одно значение вместо существующих пар экземпляров и идентификаторов.

Чтобы это исправить, вам нужно хранить слабые ссылки на уже принятые значения.

import weakref, itertools

class UniqueId:
    def __init__(self):
        self._values = weakref.WeakKeyDictionary()

    def __get__(self, instance, owner):
        if instance is None:
            return self
        else:
            return self._values[instance]

    def __set__(self, instance, value):
        if value not in self._values.values():
            self._values[instance] = value
        else:
            raise ValueError('Unique id {} already taken'.format(value))

    def get_unique_id(self):
        existing_ids = set(self._values.values())
        return next(i for i in itertools.count() if i not in existing_ids)

class Model:
    def __new__(cls, *args, **kwargs):
        instance = super().__new__(cls, *args, **kwargs)

        for name, attr in cls.__dict__.items():
            if isinstance(attr, UniqueId):
                setattr(instance, name, attr.get_unique_id())

        return instance

class Document(Model):
    id = UniqueId()

Пример

doc1 = Document()
print(doc1.id) # 0

doc2= Document()
print(doc2.id) # 1

doc2.id = 0 # ValueError: Unique id 0 already taken
...