I came across an interesting bit of code in a QC review and was surprised by its behavior. I am curious if it is documented anywhere.
for i in range(0, my_array.max(), 3)[:]:
# other code here
I was curious about the need for the [:]
after range
, so I tested it:
>>> range(0, 10, 3)
range(0, 10, 3)
>>> range(0, 10, 3)[:]
range(0, 12, 3)
The actual sequence defined by these ranges is identical, but I do not see this slicing behavior documented anywhere in the Python range documentation, so I was curious what is actually going on here.
Slicing range() function in Python Depending on how many arguments the user is passing to the function, the user can decide where that series of numbers will begin and end as well as how big the difference will be between one number and the next.
Python supports slice notation for any sequential data type like lists, strings, tuples, bytes, bytearrays, and ranges.
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.
The slicing operation doesn't raise an error if both your start and stop indices are larger than the sequence length. This is in contrast to simple indexing—if you index an element that is out of bounds, Python will throw an index out of bounds error. However, with slicing it simply returns an empty sequence.
For a moment let's pretend that range
still returned a list
. Slicing the range
object returns a range
object which would act as if you were slicing the underlying list. Instead of doing this with a list though, the range
object is able to take care of it in constant time using arithmetic.
>>> range(0, 90, 2)[10:23]
range(20, 46, 2)
>>> list(range(0, 90, 2)[10:23])
[20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44]
When you do something like:
range(0, 10, 3)[:]
Python slices it with arithmetic.
My assumption is that when determining the final element it rounds up. It tries to compute the zeroth element in the range to start with. This will be start + step * 0 = 0
.
Then Python tries to get the ending element. There are (10 - 0) // 3 + 1 = 4
elements in the range, so the ending element is start + step * n_elements = 0 + 3 * 4 = 12
.
I think a few things are mixed here.
range
produces slicing behavior, because slicing with non-default indexes makes sense: >>> list(range(10, 20)[3:7])
[13, 14, 15, 16]
some_list[:]
is equivalent to something like [x for x in some_list]
.[:]
for the range object (or the actual list, if it's Python 2) which makes no sense. The generated range object / list is not referenced anywhere else anyway.list
, tuple
, range
" (emph. mine). So it's documented, but few people ever read it.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