Как создать итерируемый класс в Python, в котором только определенный тип может быть элементом?

Я хочу, чтобы некоторый класс Python имел методы, аналогичные list (при условии, что он называется mylist), за исключением того, что mylist принимает элементы определенного типа (экземпляры другого пользовательского класса для хранения некоторых данных в моем случае).

Я прочитал несколько сообщений на SO и знал, что мне нужно переопределить некоторые методы как append, extend, insert. Но я не уверен во всем, что мне нужно переопределить, чтобы гарантировать, что во всех операциях (например, добавление, добавление, расширение, вставка, нарезка...) для экземпляров mylist не будет проблем.

А если есть еще один удобный способ без необходимости переопределять столько методов?


person Lee    schedule 24.09.2015    source источник


Ответы (1)


Иногда все проще без наследования от встроенных объектов...:

from collections import MutableSequence, Iterable

class MyList(MutableSequence):
    def __init__(self, type, iterable=()):
        self._data = []
        self._type = type
        self.extend(iterable)

    def insert(self, index, item):
        if not isinstance(item, self._type):
            raise TypeError
        self._data.insert(index, item)

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

    def __getitem__(self, *args):
        return self._data.__getitem__(*args)

    def __delitem__(self, *args):
        self._data.__delitem__(*args)

    def __setitem__(self, key, value):
         if isinstance(value, collections.Iterable) and isinstance(key, slice):
             values = []
             for val in value:
                 if not isinstance(value, self._type):
                     raise TypeError
         else:
             if not isinstance(value, self._type):
                 raise TypeError
         self._data[k] = value

Обратите внимание: я не обсуждал, является ли это хорошей идеей. Некоторые люди скажут вам не делать этого, потому что Python построен на «утиной печати». Другие говорят, что isinstance совершенно нормально использовать — при условии, что вы делаете это ответственно. Один сторонник этой точки зрения (Алекс Мартелли) обычно считается экспертом по Python. Он рекомендует isinstance проверять абстрактные базовые классы и называет эту практику "гусиным набором". Эта практика, кажется, набирает обороты, поскольку стандартная библиотека постепенно добавляет поддержку, которая позволила бы более надежную проверку этих вещей во время выполнения. Рассмотрим PEP-0484 (подсказка типа).

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

lst1 = MyList(list)
lst2 = MyList(int)

Сделайте что-нибудь вроде:

lst1 = MyList(collections.MutableSequence)
lst2 = MyList(numbers.Integral)
person mgilson    schedule 24.09.2015
comment
self._data.index(index, item) должно быть self._data.insert(index, item)? - person Lee; 24.09.2015
comment
self.extend(iterable) быть self._data.extend(iterable)? Я не понимаю последние примеры. Они намерены преобразовать тип из MutableSequence в MyList? - person Lee; 25.09.2015
comment
@Dr.Lee Нет. Если бы вы сделали self._data.extend(iterable), эти элементы попали бы в ваш список данных без проверки. использование self.extend гарантирует, что они проверяются правильно (хотя реализация по умолчанию, вероятно, не так эффективна, как вы могли бы написать сами). - person mgilson; 25.09.2015
comment
@Dr.Lee Что касается последних примеров, lst1 = MyList(list) гарантирует, что любой элемент, который вы вставляете, является list (или подклассом list). однако, как я продемонстрировал здесь, довольно легко создать что-то, что выглядит как список, используя collections.MutableSequence, поэтому лучше проверять вещи, которые ведут себя как list, а не проверять, является ли вещь list. (обратите внимание, isinstance([], collections.MutableSequence) это True). - person mgilson; 25.09.2015
comment
Я понял. Большое спасибо. - person Lee; 25.09.2015