[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))
else:
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.
Karl
--
Please do *not* send copies of replies to me.
I read the list
More information about the Tutor
mailing list