Дескрипторы Python 2 со списком

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

До сих пор я сделал несколько дескрипторов, таких как CharField и IntegerField, которые отлично работают.

Однако дескриптор списка я не могу заставить работать.

Базовый класс поля:

class Field(object):
    def __init__(self, type_, name, default=None, required=False):
        self.type = type_
        self.name = "_" + name
        self.required = required
        self._default = default

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

    def __set__(self, instance, value):
        raise NotImplementedError

    def __delete__(self, instance):
        raise AttributeError("Can't delete attribute")

    @property
    def default(self):
        return self._default

    @default.setter
    def default(self, value):
        self._default = value if value else self.type()

Мой класс CharField:

class CharField(Field):
    def __init__(self, name, default=None, min_length=0, max_length=0, strip=False):
        super(CharField, self).__init__(unicode, name, default=default)
        self.min_length = min_length
        self.max_length = max_length
        self.strip = strip

    def __set__(self, instance, value):
        if not isinstance(value, (unicode, str)):
            raise TypeError("{} must be a string or unicode".format(self.name))
        if self.strip:
            value = value.strip()
        if self.min_length and len(value) < self.min_length:
            raise ValueError("{} must have a minimum length of {}".format(self.name, self.min_length))
        if self.max_length and len(value) > self.max_length:
            raise ValueError("{} must have a maximum length of {}".format(self.name, self.max_length))
        setattr(instance, self.name, value)

И затем класс ListField:

class ListField(Field):
    def __init__(self, name, value_type):
        super(ListField, self).__init__(list, name, default=[])
        self.value_type = value_type

    def __set__(self, instance, value):
        if not isinstance(value, list):
            raise TypeError("{} must be a list".format(self.name))
        setattr(instance, self.name, value)

    def append(self, value):
        if not isinstance(value, self.value_type):
            raise ValueError("Value is list {} must be of type {}".format(self.name, self.value_type))

Итак, в конструкторе я передаю имя и тип значения, что должно гарантировать, что каждый элемент в списке должен быть определенного типа.

Как я должен быть в состоянии сделать это?


person Johan Vergeer    schedule 18.01.2017    source источник
comment
в чем проблема с вашей текущей реализацией?   -  person hansaplast    schedule 18.01.2017
comment
Можете ли вы сравнить тип элемента списка значений по команде isinstance(element, self.value_type) ? Если да, то вы можете добавить: if not all([isinstance(element, self.value_type) for element in value]): raise TypeError("{} elements type must be {}".format(self.name, self.value_type))   -  person Frodon    schedule 18.01.2017
comment
@hansaplast Проверка типа не работает при добавлении значения в список   -  person Johan Vergeer    schedule 18.01.2017
comment
@JohanVergeer: это работает: a = ListField('a', list), затем a.append([1]) работает, но a.append(1) вызывает ошибку   -  person hansaplast    schedule 18.01.2017


Ответы (1)


Благодаря вашим ответам и еще нескольким исследованиям я заставил его работать.

class ListField(Field):
    def __init__(self, name, value_type):
        super(ListField, self).__init__(list, name, default=[])
        self.value_type = value_type

    def __set__(self, instance, value):
        if not isinstance(value, list):
            raise TypeError("{} must be a list".format(self.name))
        setattr(instance, self.name, value)

    def __iter__(self):
        for item in self.default:
            yield item

    def __len__(self):
        return len(self.default)

    def __getitem__(self, item):
        return self.default[item]

    def append(self, value):
        if not isinstance(value, self.value_type):
            raise TypeError("Value is list {} must be of type {}".format(self.name, self.value_type))
        self.default.append(value)

В отличие от других полей, это можно переопределить без возникновения ошибки, но пока этого достаточно.

person Johan Vergeer    schedule 18.01.2017