Как наследовать и расширять объект списка в Python?
Я заинтересован в использовании объекта списка python, но с немного измененной функциональностью. В частности, я хотел бы, чтобы список был 1-индексированным, а не 0-индексированным. Например:.
>> mylist = MyList()
>> mylist.extend([1,2,3,4,5])
>> print mylist[1]
вывод должен быть: 1
Но когда я изменил методы __getitem__()
и __setitem__()
, чтобы сделать это, я получил ошибку RuntimeError: maximum recursion depth exceeded
. Я много общался с этими методами, но это в основном то, что у меня было:
class MyList(list):
def __getitem__(self, key):
return self[key-1]
def __setitem__(self, key, item):
self[key-1] = item
Я думаю, проблема в том, что self[key-1]
сам вызывает тот же метод, который он определяет. Если да, то каким образом я могу использовать метод list()
вместо метода MyList()
? Я попытался использовать super[key-1]
вместо self[key-1]
, но это привело к жалобе TypeError: 'type' object is unsubscriptable
Любые идеи? Также, если бы вы могли указать мне на хороший учебник, это было бы здорово!
Спасибо!
Ответы
Ответ 1
Используйте функцию super()
, чтобы вызвать метод базового класса или вызвать метод напрямую:
class MyList(list):
def __getitem__(self, key):
return list.__getitem__(self, key-1)
или
class MyList(list):
def __getitem__(self, key):
return super(MyList, self).__getitem__(key-1)
Однако это не изменит поведение других методов списка. Например, индекс остается неизменным, что может привести к неожиданным результатам:
numbers = MyList()
numbers.append("one")
numbers.append("two")
print numbers.index('one')
>>> 1
print numbers[numbers.index('one')]
>>> 'two'
Ответ 2
Вместо этого, подкласс целочисленный с использованием того же метода, чтобы определить все числа, которые будут минус один из того, что вы их установили. Вуаля.
Извините, мне пришлось. Это как шутка о Microsoft, определяющая темный стандарт.
Ответ 3
Вы можете избежать нарушения принципа замены Лискова, создав класс, который наследуется от коллекций. MutableSequence, который является абстрактным классом. Он будет выглядеть примерно так:
class MyList(collections.MutableSequence):
def __init__(self, l=[]):
if type(l) is not list:
raise ValueError()
self._inner_list = l
def __len__(self):
return len(self._inner_list)
def __delitem__(self, index):
self._inner_list.__delitem__(index - 1)
def insert(self, index, value):
self._inner_list.insert(index - 1, value)
def __setitem__(self, index, value):
self._inner_list.__setitem__(index - 1, value)
def __getitem__(self, index):
return self._inner_list.__getitem__(index - 1)
Здесь есть одна проблема (хотя может быть и больше). Если вы указали свой новый список следующим образом:
l = MyList()
l[0]
вы на самом деле вызываете:
self._inner_list[-1]
который доставит вам последний элемент. Поэтому вы должны выполнить дополнительную проверку в методах и убедиться, что вы сохраняете обратную индексацию, если вы хотите иметь эту функцию для своего списка.
EDIT:
Вот новый код, который, я считаю, не должен иметь никаких проблем.
def indexing_decorator(func):
def decorated(self, index, *args):
if index == 0:
raise IndexError('Indices start from 1')
elif index > 0:
index -= 1
return func(self, index, *args)
return decorated
class MyList(collections.MutableSequence):
def __init__(self):
self._inner_list = list()
def __len__(self):
return len(self._inner_list)
@indexing_decorator
def __delitem__(self, index):
self._inner_list.__delitem__(index)
@indexing_decorator
def insert(self, index, value):
self._inner_list.insert(index, value)
@indexing_decorator
def __setitem__(self, index, value):
self._inner_list.__setitem__(index, value)
@indexing_decorator
def __getitem__(self, index):
return self._inner_list.__getitem__(index)
def append(self, value):
self.insert(len(self) + 1, value)