简体   繁体   中英

How to inherit and extend a list object in Python?

I am interested in using the python list object, but with slightly altered functionality. In particular, I would like the list to be 1-indexed instead of 0-indexed. Eg:

>> mylist = MyList()
>> mylist.extend([1,2,3,4,5])
>> print mylist[1]

output should be: 1

But when I changed the __getitem__() and __setitem__() methods to do this, I was getting a RuntimeError: maximum recursion depth exceeded error. I tinkered around with these methods a lot but this is basically what I had in there:

class MyList(list):
    def __getitem__(self, key):
        return self[key-1]
    def __setitem__(self, key, item):
        self[key-1] = item

I guess the problem is that self[key-1] is itself calling the same method it's defining. If so, how do I make it use the list() method instead of the MyList() method? I tried using super[key-1] instead of self[key-1] but that resulted in the complaint TypeError: 'type' object is unsubscriptable

Any ideas? Also if you could point me at a good tutorial for this that'd be great!

Thanks!

Use the super() function to call the method of the base class, or invoke the method directly:

class MyList(list):
    def __getitem__(self, key):
        return list.__getitem__(self, key-1)

or

class MyList(list):
    def __getitem__(self, key):
        return super(MyList, self).__getitem__(key-1)

However, this will not change the behavior of other list methods. For example, index remains unchanged, which can lead to unexpected results:

numbers = MyList()
numbers.append("one")
numbers.append("two")

print numbers.index('one')
>>> 1

print numbers[numbers.index('one')]
>>> 'two'

Instead, subclass integer using the same method to define all numbers to be minus one from what you set them to. Voila.

Sorry, I had to. It's like the joke about Microsoft defining dark as the standard.

You can avoid violating the Liskov Substitution principle by creating a class that inherits from collections.MutableSequence, which is an abstract class. It would look something like this:

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)

There is one problem here (though there might be more). If you index your new list like this:

l = MyList()
l[0]

you will actually call:

self._inner_list[-1]

which will get you the last element. So you must do additional checking in the methods and make sure you keep the reverse indexing, if you want to have that feature for your list.

EDIT:

Here is the new code, which I believe should not have any problems.

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)
class ListExt(list):
    def extendX(self, l):
        if l:
            self.extend(l)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM