Was going to comment, but it's easier to write code as an answer, so...
I would NOT write a class that redefines slicing, unless it's VERY clear. I have a class that represents ints with bit slicing. In my contexts, '4:2' is very clearly inclusive, and ints don't already have any use for slicing, so it's (barely) acceptable (imho, and some would disagree).
For lists, you have the case that you'll do something like
list1 = [1,2,3,4,5]
list2 = InclusiveList([1,2,3,4,5])
and later on in your code
if list1[4:2] == test_list or list2[4:2] == test_list:
and that is a very easy mistake to make, since list already HAS a well-defined usage.. they look identical, but act differently, and so this will be very confusing to debug, especially if you didn't write it.
That doesn't mean you're completely lost... slicing is convenient, but after all, it's just a function. And you can add that function to anything like this, so this might be an easier way to get to it:
class inc_list(list):
def islice(self, start, end=None, dir=None):
return self.__getitem__(slice(start, end+1, dir))
l2 = inc_list([1,2,3,4,5])
l2[1:3]
[0x3,
0x4]
l2.islice(1,3)
[0x3,
0x4,
0x5]
However, this solution, like many others, (besides being incomplete... i know) has the achilles' heel in that it's just not as simple as the simple slice notation... it's a little more simple than passing the list as an argument, but still harder than just [4:2]. The only way to make that happen is to pass something different to the slice, that could be interepreted differently, so that the user would know on reading it what they did, and it could still be as simple.
One possibility... floating point numbers. They're different, so you can see them, and they aren't too much more difficult than the 'simple' syntax. It's not built-in, so there's still some 'magic' involved, but as far as syntactic sugar, it's not bad....
class inc_list(list):
def __getitem__(self, x):
if isinstance(x, slice):
start, end, step = x.start, x.stop, x.step
if step == None:
step = 1
if isinstance(end, float):
end = int(end)
end = end + step
x = slice(start, end, step)
return list.__getitem__(self, x)
l2 = inc_list([1,2,3,4,5])
l2[1:3]
[0x2,
0x3]
l2[1:3.0]
[0x2,
0x3,
0x4]
The 3.0 should be enough to tell any python programmer 'hey, something unusual is going on there'... not necessarily what is going on, but at least there's not surprise that it acts 'weird'.
Note that there's nothing unique about that to lists... you could easy write a decorator that could do this for any class:
def inc_getitem(self, x):
if isinstance(x, slice):
start, end, step = x.start, x.stop, x.step
if step == None:
step = 1
if isinstance(end, float):
end = int(end)
end = end + step
x = slice(start, end, step)
return list.__getitem__(self, x)
def inclusiveclass(inclass):
class newclass(inclass):
__getitem__ = inc_getitem
return newclass
ilist = inclusiveclass(list)
or
@inclusiveclass
class inclusivelist(list):
pass
The first form is probably more useful though.