[Tutor] subclassing list -- slicing doesn't preserve type

Karl Pflästerer sigurd at 12move.de
Tue Feb 22 13:53:44 CET 2005

On 22 Feb 2005, bvande at po-box.mcgill.ca wrote:

> I'm trying to figure out how to subclass the list built-in.
> .>>> class my_list(list):
>          def __init__(self, sequence=None):
>              list.__init__(self, sequence)
>              self.spam = 1
> .>>> mine = my_list((1,2,3,4))
> .>>> mine.append(42)
> .>>> mine
> [1, 2, 3, 4, 42]
> .>>> type(mine)
> <class '__main__.my_list'>
> .>>> damn = mine[1:3]
> .>>> type(damn)
> <type 'list'>
> .>>> damn
> [2, 3]
> .>>>
> I had thought that by subsclassing list I got all list-like properties
> for free. Append (among others) are available for my_list instances.
> But slicing returns a list, rather than a my_list. I can see that this
> is list-like in it's way, but I'd rather have be so in mine ;-)	
> I've read in the docs that UserList is discouraged, and in the
> Nutshell that the __getslice__, etc. special methods are depreciated.

But the problem is here: list() defines a __getslice__ method.

> So, how can I get slicing to preserve the my_list type? And why does
> the above class get so much for free, but not slicing?

It gets also slicing for free but think about what happens when you call
e.g. `append'; you *mutate an existing object in place* (i.e. a
destructive operation).  So you change the already existing object which
is an instance of  Mylist.  On the other hand with slicing you get a
*new* object back; you never told Python that you also wanted that new
object to be of type Mylist.

You could do it e.g. like that:

class Mylist (list):
    def __init__(self, seq=None):
        super(self.__class__, self).__init__(seq)
    def __getslice__(self, start, stop):
        return self.__class__(super(self.__class__, self).__getslice__(start, stop))
    def __getitem__(self, key):
        if isinstance(key, slice):
            return self.__class__(super(self.__class__, self).__getitem__(key))
            return super(self.__class__, self).__getitem__(key) 

For simple slices Python calls `list.__getslice__' for extended slices
list.__getitem__ gets called.  So the above handles all cases.

. >>> L = Mylist((1,2,3,4))
. >>> L[1]
. 2
. >>> L[1:2]
. [2]
. >>> type(_)
. <class '__main__.Mylist'>
. >>> L[0:4:2]
. [1, 3]
. >>> type(_)
. <class '__main__.Mylist'>
. >>> 

You must also think about the other methods which return a newly
allocated object.

Please do *not* send copies of replies to me.
I read the list

More information about the Tutor mailing list