I'd like to write a simple wrapper for the python list type that forces it to start indexing at 1 instead of 0. I've got a a fairly complex program based on some discrete probability distributions of duration data, with integer-length buckets, but I don't have any durations of less than 1. Anyway, it would greatly simplify a some significant portions of my code to be able to seamlessly index starting at 1. I was using a dict at first but I found several properties of them to be too cumbersome.
I've never written a wrapper for a Python class before, much less a built-in type, but I feel like what I want to do is fairly simple. At the bare minimum, I should be able to do stuff like this:
>>> p = one_list([1,2,3,4,5])
>>> for i in range(1,6):
print i, p[i]
1 1
2 2
3 3
4 4
5 5
>>> len(p)
5
However it would be good if I could overwrite some of the other relevant built-in methods of the list class as well, such as index.
>>> len(p)
5
>>> p.index(p[-1])
5
Please share your tips as to how I might go about doing something like this. I was considering whether or not to just do it with a custom class, but that seems like it might be overkill. I also welcome any recommendations as to useful methods to overwrite.
Edit: Afterword
I'd just like to note that it was not worth the trouble to do this, and the reason I've accepted the answer below is not because I tried to implement it in the way he describes, but because he helped me realize that lists are good enough on their own.
Here's a complete (I think) implementation of a 1-based list, correctly handling slicing (including extended slices), indexing, popping, etc. It's slightly more tricky to get this right than you might think, especially the slicing and the negative indexes. In fact I'm still not 100% sure it works exactly as it should, so caveat coder.
class list1(list):
"""One-based version of list."""
def _zerobased(self, i):
if type(i) is slice:
return slice(self._zerobased(i.start),
self._zerobased(i.stop), i.step)
else:
if i is None or i < 0:
return i
elif not i:
raise IndexError("element 0 does not exist in 1-based list")
return i - 1
def __getitem__(self, i):
return list.__getitem__(self, self._zerobased(i))
def __setitem__(self, i, value):
list.__setitem__(self, self._zerobased(i), value)
def __delitem__(self, i):
list.__delitem__(self, self._zerobased(i))
def __getslice__(self, i, j):
print i,j
return list.__getslice__(self, self._zerobased(i or 1),
self._zerobased(j))
def __setslice__(self, i, j, value):
list.__setslice__(self, self._zerobased(i or 1),
self._zerobased(j), value)
def index(self, value, start=1, stop=-1):
return list.index(self, value, self._zerobased(start),
self._zerobased(stop)) + 1
def pop(self, i):
return list.pop(self, self._zerobased(i))
senderle's ExtraItemList is going to have better performance, though, because it doesn't need to adjust the indices constantly nor does it have an extra layer of (non-C!) method calls between you and the data. Wish I'd thought of that approach; maybe I could profitably combine it with mine...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With