0

I want some Python class having similar methods as list (assuming it's called mylist) except that mylist accepts elements of a specific type (instances of another customed class to store some data in my case).

I read some posts on SO and knew that I need to override some methods as append, extend, insert. But I'm not sure about everything I need to override to guarantee that in all operations (e.g. append, add, extend, insert, slice...) to instances of mylist there will be no problem.

And if there is one more convenient way without needs to override so many methods?

Lee
  • 526
  • 3
  • 9

1 Answers1

4

Sometimes, things are easier without inheriting from builtin objects...:

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

Note, I haven't gone into any discussion about whether this is a good idea. Some people will tell you not to do this because python is built on "duck-typing". Others say that isinstance is perfectly fine to use -- provided that you do so responsibly. One proponent of this view (Alex Martelli) is generally regarded as a python expert. He recommends isinstance checking against abstract base classes and calls this practice "goose-typing". This practice seems to have gained some traction as the standard library is slowly adding support that would allow more robust run-time checking of these things -- Consider PEP-0484 (type hinting).

I suppose, my point is that if you use this class, don't do:

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

Do something like:

lst1 = MyList(collections.MutableSequence)
lst2 = MyList(numbers.Integral)
mgilson
  • 300,191
  • 65
  • 633
  • 696
  • `self._data.index(index, item)` should be `self._data.insert(index, item)`? – Lee Sep 24 '15 at 17:28
  • `self.extend(iterable)` be `self._data.extend(iterable)`? I don't get the last examples. Do they intend to convert the type from MutableSequence to MyList? – Lee Sep 24 '15 at 22:47
  • @Dr.Lee No. If you did `self._data.extend(iterable)`, then those elements would go into your data list un-checked. using `self.extend` makes sure that they are checked properly (although the default implementation probably isn't as efficient as you could write yourself). – mgilson Sep 24 '15 at 22:50
  • @Dr.Lee With regard to the last examples, `lst1 = MyList(list)` makes sure that ever element that you put in is a `list` (or subclass of `list`). however, as I've demonstrated here, it's pretty easy to make something that *looks* like a list using `collections.MutableSequence` -- so it's best to check for things that behave like `list` rather than checking if the thing is a `list`. (note, `isinstance([], collections.MutableSequence)` is `True`). – mgilson Sep 24 '15 at 22:52
  • I got it. Thanks a lot. – Lee Sep 24 '15 at 22:56