How to subclass Python list without type problems?
Question:
I want to implement a custom list class in Python as a subclass of list
. What is the minimal set of methods I need to override from the base list
class in order to get full type compatibility for all list operations?
This question suggest that at least __getslice__
needs to be overridden. From further research, also __add__
and __mul__
will be required. So I have this code:
class CustomList(list):
def __getslice__(self,i,j):
return CustomList(list.__getslice__(self, i, j))
def __add__(self,other):
return CustomList(list.__add__(self,other))
def __mul__(self,other):
return CustomList(list.__mul__(self,other))
The following statements work as desired, even without the overriding methods:
l = CustomList((1,2,3))
l.append(4)
l[0] = -1
l[0:2] = CustomList((10,11)) # type(l) is CustomList
These statements work only with the overriding methods in the above class definition:
l3 = l + CustomList((4,5,6)) # type(l3) is CustomList
l4 = 3*l # type(l4) is CustomList
l5 = l[0:2] # type(l5) is CustomList
The only thing I don’t know how to achieve is making extended slicing return the right type:
l6 = l[0:2:2] # type(l6) is list
What do I need to add to my class definition in order to get CustomList
as type of l6
?
Also, are there other list operations other than extended slicing, where the result will be of list
type instead of CustomList
?
Answers:
You should probably read these two sections from the documentation:
Edit: In order to handle extended slicing, you should make your __getitem__
-method handle slice-objects (see here, a little further down).
Firstly, I recommend you follow Björn Pollex’s advice (+1).
To get past this particular problem (type(l2 + l3) == CustomList
), you need to implement a custom __add__()
:
def __add__(self, rhs):
return CustomList(list.__add__(self, rhs))
And for extended slicing:
def __getitem__(self, item):
result = list.__getitem__(self, item)
try:
return CustomList(result)
except TypeError:
return result
I also recommend…
pydoc list
…at your command prompt. You’ll see which methods list
exposes and this will give you a good indication as to which ones you need to override.
Possible cut-the-gordian-knot solution: subclass UserList instead of list. (Worked for me.) That is what UserList is there for.
As a slight modification to Johnsywebs answer. I would only convert to a CustomList if item is a slice. Otherwise CustomList(["ab"])[0] would give you CustomList(["a", "b"]) which is not what you want. Like this:
def __getitem__(self, item):
result = list.__getitem__(self, item)
if type(item) is slice:
return CustomList(result)
else:
return result
I want to implement a custom list class in Python as a subclass of list
. What is the minimal set of methods I need to override from the base list
class in order to get full type compatibility for all list operations?
This question suggest that at least __getslice__
needs to be overridden. From further research, also __add__
and __mul__
will be required. So I have this code:
class CustomList(list):
def __getslice__(self,i,j):
return CustomList(list.__getslice__(self, i, j))
def __add__(self,other):
return CustomList(list.__add__(self,other))
def __mul__(self,other):
return CustomList(list.__mul__(self,other))
The following statements work as desired, even without the overriding methods:
l = CustomList((1,2,3))
l.append(4)
l[0] = -1
l[0:2] = CustomList((10,11)) # type(l) is CustomList
These statements work only with the overriding methods in the above class definition:
l3 = l + CustomList((4,5,6)) # type(l3) is CustomList
l4 = 3*l # type(l4) is CustomList
l5 = l[0:2] # type(l5) is CustomList
The only thing I don’t know how to achieve is making extended slicing return the right type:
l6 = l[0:2:2] # type(l6) is list
What do I need to add to my class definition in order to get CustomList
as type of l6
?
Also, are there other list operations other than extended slicing, where the result will be of list
type instead of CustomList
?
You should probably read these two sections from the documentation:
Edit: In order to handle extended slicing, you should make your __getitem__
-method handle slice-objects (see here, a little further down).
Firstly, I recommend you follow Björn Pollex’s advice (+1).
To get past this particular problem (type(l2 + l3) == CustomList
), you need to implement a custom __add__()
:
def __add__(self, rhs):
return CustomList(list.__add__(self, rhs))
And for extended slicing:
def __getitem__(self, item):
result = list.__getitem__(self, item)
try:
return CustomList(result)
except TypeError:
return result
I also recommend…
pydoc list
…at your command prompt. You’ll see which methods list
exposes and this will give you a good indication as to which ones you need to override.
Possible cut-the-gordian-knot solution: subclass UserList instead of list. (Worked for me.) That is what UserList is there for.
As a slight modification to Johnsywebs answer. I would only convert to a CustomList if item is a slice. Otherwise CustomList(["ab"])[0] would give you CustomList(["a", "b"]) which is not what you want. Like this:
def __getitem__(self, item):
result = list.__getitem__(self, item)
if type(item) is slice:
return CustomList(result)
else:
return result