Reputation: 1648
I make a class like below:
class MyList(list):
def __init__(self, lst):
self.list = lst
I want slice functionality to be overridden in MyList
Upvotes: 28
Views: 19806
Reputation: 78
As both @Martijn Pieters and @jamylak said, you need to implement the __getitem__
method.
In general, if one wants to override the slicing functionality of it's super class, and keep using super's methods that already uses super's __getitem__
, just split it into cases, and benefit from both worlds.
For example:
import pandas as pd
class SubFrame(pd.DataFrame):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def __getitem__(self, item):
if isinstance(item, slice):
# your implementation
print("'My slicing'")
return SubFrame(super().__getitem__(item))
else:
return super().__getitem__(item)
Now you can do:
>>> sf = SubFrame({'A': (10,20,30,40,50)})
>>> sf[1:-2]
'My slicing'
A
1 20
2 30
>>> type(sf[1:-2])
<class '__main__.SubFrame'>
And []
will still call super's __getitem__
>>> sf['A'] # super's __getitem__
0 10
1 20
2 30
3 40
4 50
Name: A, dtype: int64
>>> type(sf['A'])
<class 'pandas.core.series.Series'>
Upvotes: 1
Reputation: 1122082
You need to provide custom __getitem__()
, __setitem__
and __delitem__
hooks.
These are passed a slice object when slicing the list; these have start
, stop
and step
attributes. However, these values could be None
, to indicate defaults. Take into account that the defaults actually change when you use a negative stride!
However, they also have a slice.indices()
method, which when given a length produces a tuple of (start, stop, step)
values suitable for a range()
object. This method takes care of such pesky details as slicing with a negative strides and no start or stop indices:
def __getitem__(self, key):
if isinstance(key, slice):
indices = range(*key.indices(len(self.list)))
return [self.list[i] for i in indices]
return self.list[key]
or, for your case:
def __getitem__(self, key):
return self.list[key]
because a list
can take the slice
object directly.
In Python 2, list.__getslice__
is called for slices without a stride (so only start and stop indices) if implemented, and the built-in list
type implements it so you'd have to override that too; a simple delegation to your __getitem__
method should do fine:
def __getslice__(self, i, j):
return self.__getitem__(slice(i, j))
Upvotes: 56
Reputation: 133564
class MyList(list):
def __init__(self, lst):
self.list = lst
doesn't make much sense... self
is the list object itself, and it has already been created at this point, maybe you want to override __new__
, however you probably don't need to touch that. Anyway you want to override __getitem__
like so:
def __getitem__(self, val):
if isinstance( val, slice):
# do stuff here
Upvotes: 8