Переопределение метода добавления после наследования из списка Python

Я хочу создать список, который может принимать только определенные типы. Таким образом, я пытаюсь наследовать из списка в Python и переопределять метод append () следующим образом:

class TypedList(list): def __init__(self, type): self.type = type def append(item) if not isinstance(item, type): raise TypeError, 'item is not of type %s' % type self.append(item) #append the item to itself (the list) 

Это вызовет бесконечный цикл, потому что тело append () вызывает себя, но я не уверен, что делать, кроме использования self.append (item).

Как я должен это делать?

  • Есть ли способ проверить, существует ли элемент в кортедже Python?
  • Удаление дубликатов из вложенного списка на основе первых 2 элементов
  • почему мой шрифт TrueType размера 11 отличается от окон?
  • Что происходит в бесконечных вложенных списках?
  • Java «Виртуальная машина» и Python «Интерпретатор»?
  • Запись в память NumPy по-прежнему загружается в оперативную память
  • Как выровнять индексы многих фреймов данных и заполнить соответствующие пропущенные значения в Pandas?
  • Python: могут ли подклассы перегружать унаследованные методы?
  • 6 Solutions collect form web for “Переопределение метода добавления после наследования из списка Python”

    Я внес некоторые изменения в ваш класс. Кажется, это работает.

    Несколько предложений: не используйте type как ключевое слово – type – встроенная функция. Доступ к переменным экземпляра Python осуществляется с помощью self. префикс. Поэтому используйте self.<variable name> .

     class TypedList(list): def __init__(self, type): self.type = type def append(self, item): if not isinstance(item, self.type): raise TypeError, 'item is not of type %s' % self.type super(TypedList, self).append(item) #append the item to itself (the list) from types import * tl = TypedList(StringType) tl.append('abc') tl.append(None) Traceback (most recent call last): File "<pyshell#25>", line 1, in <module> tl.append(None) File "<pyshell#22>", line 7, in append raise TypeError, 'item is not of type %s' % self.type TypeError: item is not of type <type 'str'> 

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

    Не лучший подход! В списках Python есть так много мутирующих методов, что вы должны были бы переопределить связку (и, вероятно, забыли бы некоторые).

    Скорее, оберните список, наследуйте от collections.MutableSequence и добавьте свои проверки в очень немногие методы «затвора», на которые MutableSequence полагается для реализации всех остальных.

     import collections class TypedList(collections.MutableSequence): def __init__(self, oktypes, *args): self.oktypes = oktypes self.list = list() self.extend(list(args)) def check(self, v): if not isinstance(v, self.oktypes): raise TypeError, v def __len__(self): return len(self.list) def __getitem__(self, i): return self.list[i] def __delitem__(self, i): del self.list[i] def __setitem__(self, i, v): self.check(v) self.list[i] = v def insert(self, i, v): self.check(v) self.list.insert(i, v) def __str__(self): return str(self.list) 

    Аргумент oktypes обычно является кортежем типов, которые вы хотите разрешить, но это нормально, чтобы пройти там один тип (и, сделав этот тип абстрактным базовым классом, ABC, вы можете легко выполнить любой тип типа, проверка вашего выбора таким образом – но это другая проблема).

    Вот пример кода, использующего этот класс:

     x = TypedList((str, unicode), 'foo', 'bar') x.append('zap') print x x.append(23) 

    выход:

     ['foo', 'bar', 'zap'] Traceback (most recent call last): File "tl.py", line 35, in <module> x.append(23) File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/_abcoll.py", line 556, in append self.insert(len(self), value) File "tl.py", line 25, in insert self.check(v) File "tl.py", line 12, in check raise TypeError, v TypeError: 23 

    Обратите внимание, в частности, что мы не переопределили append но append существует и ведет себя так, как ожидалось.

    В _abcoll.py обнаруживается не столь секретный этот бит магии: _abcoll.py (модуль реализации для абстрактных базовых классов в модуле collections ), в строке 556, реализует добавление, вызывая нашу insert – которую мы имеем , конечно, правильно переопределено.

    Этот «шаблон шаблона шаблона» (абсолютно ценный для всех видов ООП) – ищите мои разговоры о шаблонах дизайна на YouTube, и вы узнаете, почему ;-), среди других преимуществ, дает нам «эффект затухания» I упомянутое ранее: добавив некоторые проверки с помощью нескольких методов, которые вы должны реализовать, вы получаете преимущество в том, что эти проверки применяются к _all__ другим соответствующим методам (и изменчивые последовательности в Python имеют много таких ;-).

    Неудивительно, что в итоге мы получили очень мощный и классический образец дизайна «за кулисами», потому что вся идея этой стратегии реализации исходит из бессмертной классической книги «Design Patterns» (авторы которой часто называются банда из четырех ";-): предпочитает композицию объекта над наследованием . Наследование (из конкретных классов) – очень жесткий механизм сцепления, полный« gotchas », как только вы пытаетесь использовать его, чтобы что-либо сделать, даже слегка за пределами его строгие ограничения, состав чрезвычайно гибкий и полезный, и наследование от соответствующих абстрактных классов может очень хорошо завершить картину.

    Отличный «Эффективный C ++» Скотта Майерса, пункт 33 , ставит его еще сильнее: сделайте абстрактные классы нелистов . Поскольку «без листа» он означает «любой класс, который когда-либо унаследован», эквивалентная фраза «никогда не будет наследоваться от конкретного класса».

    Скотт пишет в контексте C ++, конечно же, но Пол Хаар дает точно такой же совет для Java, сформулированный как « Не подклассы конкретных классов», – и я обычно выделяю его для Python, хотя я предпочитаю «банду четырех» более мягкая фразировка, предпочитают наследование по составу (конкретному классу) (но я понимаю, что Скотт и Пол часто пишут для аудитории, которая нуждается в очень прямом и строго сформулированном совете, почти сформулированном как «заповеди», а не в совете, а не более мягко сформулированном, они могут слишком легко игнорировать во имя их удобства ;-).

    вместо self.append(item) использовать super(TypedList, self).append(item) (см. http://docs.python.org/library/functions.html#super )

    Не делая этого в первую очередь.

    Если вы не хотите что-то типа X в своем списке, почему вы его там помещаете?

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

    По той же причине вы не будете пытаться 'a string' / 2.0 , у вас есть тот же контроль над тем, что попадает в список. Поскольку список будет с радостью содержать разнородные типы, в лучшем случае TypedList переместит время выполнения TypeError с того места, где вы используете элемент вперед во времени, туда, где вы добавляете его в список. Учитывая, что утка-типизация Python явно проверяет isinstance исключает последующее расширение списка, чтобы содержать экземпляры isinstance type при этом не принося никакой пользы.

    добавлена ​​информация OrderedDict :

    По запросу в комментариях, предполагая коллекции Python 2.7 или более. ЗадействующийDict сделает это. По этой странице документации, приведенной в 2.4 или выше, вы должны добавить ее .

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

    Пример:

     from array import array array('c', 'hello world') # char array array('u', u'hello \u2641') # unicode array array('l', [1, 2, 3, 4, 5]) # long array array('d', [1.0, 2.0, 3.14]) # double array 

    Вы можете выполнять те же операции, что и с обычным списком:

     chars = array('c') chars.extend('foo') 

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

     >>> chars.extend([5,10]) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: array item must be char 

    Для всех доступных типов смотрите здесь

    Хотя мне нравится ответ @Alex Martini, к сожалению, это не практично для большинства ситуаций. В WAY слишком много вещей, как в стандартной библиотеке, так и в другом библиотечном коде, которые ожидают, что типы списка наследуются из list . Вы даже не можете сделать json.dumps() без наследования из list если не напишите свой собственный метод сериализации для своего типа.

    Я бы не согласился с @msw, потому что вы могли бы выставить что-то вроде этого в библиотеке и не иметь контроля над другим кодом.

    Класс list к сожалению, не позволяет вам переопределить одну функцию, например, в примере Алекса. Ниже перечислены все функции, которые могут потенциально добавить в список (см. Здесь для всех функций списка: https://docs.python.org/2/library/stdtypes.html#mutable-sequence-types ).

     import collections class CheckedList(list, collections.MutableSequence): def __init__(self, check_method, iterator_arg=None): self.__check_method = check_method if not iterator_arg is None: self.extend(iterator_arg) # This validates the arguments... def insert(self, i, v): return super(CheckedList, self).insert(i, self.__check_method(v)) def append(self, v): return super(CheckedList, self).append(self.__check_method(v)) def extend(self, t): return super(CheckedList, self).extend([ self.__check_method(v) for v in t ]) def __add__(self, t): # This is for something like `CheckedList(validator, [1, 2, 3]) + list([4, 5, 6])`... return super(CheckedList, self).__add__([ self.__check_method(v) for v in t ]) def __iadd__(self, t): # This is for something like `l = CheckedList(validator); l += [1, 2, 3]` return super(CheckedList, self).__iadd__([ self.__check_method(v) for v in t ]) def __setitem__(self, i, v): if isinstance(i, slice): return super(CheckedList, self).__setitem__(i, [ self.__check_method(v1) for v1 in v ]) # Extended slice... else: return super(CheckedList, self).__setitem__(i, self.__check_method(v)) def __setslice__(self, i, j, t): # NOTE: extended slices use __setitem__, passing in a tuple for i return super(CheckedList, self).__setslice__(i, j, [ self.__check_method(v) for v in t ]) 
    Python - лучший язык программирования в мире.