Переопределение метода append после наследования от списка 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.добавление (пункт).
Как я должен это сделать?
6 ответов
я внес некоторые изменения в свой класс. Кажется, это работает.
несколько предложений: не используйте 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 так много мутирующих методов, что вам придется переопределять кучу (и, вероятно, забыть некоторые).
а, обертывание список, наследовать от 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 есть и ведет себя так, как ожидалось.
не-так-секрет за этим немного магии раскрывается в traceback:_abcoll.py
(модуля реализации для абстрактных базовых классов в collections
модуль), на линии 556,осуществляет добавить по телефону insert
-- что мы есть, конечно, правильно переопределено.
этот "шаблон метода проектирования шаблона" (абсолютно драгоценный для всех видов ООП-ищите мои разговоры о шаблонах дизайна на youtube, и вы узнаете, почему; -), среди других преимуществ, дает нам "эффект точки дросселя", о котором я упоминал ранее: добавив некоторые проверки в очень немногих методах, которые вы должны реализовать, вы получите преимущество, что эти проверки применяются ко всем другим соответствующим методам (и изменяемые последовательности в Python имеют много таких;-).
неудивительно, что мы в конечном итоге с очень мощным и классический узор дизайн "за сцены", потому что вся идея этой стратегии реализации исходит прямо из бессмертной классической книги" Design Patterns "(авторы которой часто коллективно называют "бандой четырех"):предпочитают композицию объекта наследованию. Наследование (от конкретных классов) - очень жесткий механизм сцепления, полный "gotchas", как только вы пытаетесь использовать его для чего-либо, даже немного за пределами его строгих пределов; состав чрезвычайно гибкий и полезно, и наследование от соответствующих аннотация классы могут завершить картину очень красиво.
Скотт Мейерс отличный " эффективный C++",пункт 33, выразился еще сильнее: сделать нелистовые классы абстрактными. Поскольку под " не-листом "он имеет в виду" любой класс, который когда-либо унаследован от", эквивалентная формулировка будет"никогда не наследовать от конкретного класса".
Скотт пишет в контексте C++, конечно, но Павел Haahr дает точно такой же совет для 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
исключает дальнейшее расширение списка, чтобы содержать не -type
экземпляры, не предоставляя никакой выгоды.
добавлен OrderedDict информация:
за запрос в комментарии, предполагая Python 2.7 или выше коллекций.OrderedDict сделает это. На этой странице документацией, учитывая 2.4 или выше добавить один.
хотя мне нравится ответ @Alex Martini, к сожалению, это не практично для большинства ситуаций. Есть слишком много вещей как в стандартной библиотеке, так и в другом коде библиотеки, которые ожидают, что типы списков наследуются от 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 ])
вы также можете использовать build-in массив класса. Он работает только с числовыми типами, но, вероятно, является лучшим решением для таких случаев. Он оптимизирован для минимизации использования памяти.
пример:
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
для всех доступных типов посмотрите здесь