I'm using Python 3.3. I want to get a slice
object and use it to make a new range
object.
It goes something like that:
>>> class A: def __getitem__(self, item): if isinstance(item, slice): return list(range(item.start, item.stop, item.step)) >>> a = A() >>> a[1:5:2] # works fine [1, 3] >>> a[1:5] # won't work :( Traceback (most recent call last): File "<pyshell#18>", line 1, in <module> a[1:5] # won't work :( File "<pyshell#9>", line 4, in __getitem__ return list(range(item.start, item.stop, item.step)) TypeError: 'NoneType' object cannot be interpreted as an integer
Well, the problem is obvious here - range
doesn't accept None
as a value:
>>> range(1, 5, None) Traceback (most recent call last): File "<pyshell#19>", line 1, in <module> range(1, 5, None) TypeError: 'NoneType' object cannot be interpreted as an integer
But what is not obvious (to me) is the solution. How will I call range
so it will work in every case? I'm searching for a nice pythonic way to do it.
range() v slice() But one is iterable and the other is not. One can be used as a list indices, one cannot. They both conform to the start , stop , step interface. You can slice a range but you can't range a slice.
The slice() method returns a portion of an iterable as an object of the slice class based on the specified range. It can be used with string, list, tuple, set, bytes, or range objects or custom class object that implements sequence methods __getitem__() and __len__() methods.
List slicing uses [:] and Range uses (,) One is using Square Brackets with colon, One is using Brackets with comma. Can anyone help me to understand the two, and what should I be aware of when I deal with these type? Maybe an example? thank you so much!
There's an easier way to do this (at least in 3.4, I don't have 3.3 at the moment, and I don't see it in the changelog).
Assuming your class already has a known length you can just slice a range of that size:
>>> range(10)[1:5:2] range(1, 5, 2) >>> list(range(10)[1:5:2]) [1, 3]
If you don't know the length a priori you'll have to do:
>>> class A: def __getitem__(self, item): if isinstance(item, slice): return list(range(item.stop)[item]) >>> a = A() >>> a[1:5:2] [1, 3] >>> a[1:5] [1, 2, 3, 4]
Try
class A: def __getitem__(self, item): ifnone = lambda a, b: b if a is None else a if isinstance(item, slice): if item.stop is None: # do something with itertools.count() else: return list(range(ifnone(item.start, 0), item.stop, ifnone(item.step, 1))) else: return item
This will reinterpret .start
and .step
appropriately if they are None
.
Another option could be the .indices()
method of a slice. It is called with the number of entries and reinterprets None
to the appropriate values and wraps negative values around the given length parameter:
>>> a=slice(None, None, None) >>> a.indices(1) (0, 1, 1) >>> a.indices(10) (0, 10, 1) >>> a=slice(None, -5, None) >>> a.indices(100) (0, 95, 1)
It depends what you intend to do with negative indices...
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