Reputation:
I am familiar with the difference between range()
and xrange()
. I noticed something weird with xrange()
:
>>> xrange(1,10,2)
xrange(1, 11, 2)
>>> xrange(1,10,4)
xrange(1, 13, 4)
Functionally, it is correct:
>>> for item in xrange(1,10,4):
... print item
...
1
5
9
>>>
However, as you can see, the stop value in the returned xrange
object is the next higher value after the last legal value. Any reason why?
range()
which now provides the same functionality in Python 3 as xrange
in Python 2 behaves as expected:
>>> range(1,10,4)
range(1, 10, 4)
>>> range(1,10,2)
range(1, 10, 2)
>>>
Upvotes: 1
Views: 474
Reputation: 5097
xrange(1, 10, 4)
is equivalent to xrange(1, 13, 4)
. To use your example:
>>> for item in xrange(1,13,4):
... print item
...
1
5
9
>>>
xrange
in Python 2 canonicalizes the start, stop, step
arguments. Internally, the xrange
implementation stores the triple start, step and length (number of elements in the xrange
object) instead of start, step and stop. Here is how xrange.__repr__()
is implemented [1]:
rtn = PyString_FromFormat("xrange(%ld, %ld, %ld)",
r->start,
r->start + r->len * r->step,
r->step);
[1] https://github.com/replit/empythoned/blob/master/cpython/Objects/rangeobject.c
Upvotes: 3
Reputation: 1121376
Does it really matter?
The effect is the same. Neither 10 nor 11 is included in the output of xrange()
, and xrange(1, 11, 2)
is equivalent to xrange(1, 10, 2)
.
The Python 2 range type (the result of xrange()
) stores the range length, not the end value, so to create the repr
output it calculates that end value for you. And because you used a step value, the calculation shows the result of the formula start + length * step
. For the implementation, the length is the more important value, the end
value can safely be discarded and recalculated as needed.
So, when you create xrange(1, 10, 2)
, it calculates the range length and stores that instead of the end value:
if (step > 0 && lo < hi)
return 1UL + (hi - 1UL - lo) / step;
else if (step < 0 && lo > hi)
return 1UL + (lo - 1UL - hi) / (0UL - step);
else
return 0UL;
The Python 3 Range object stores the end value in addition to the length, so you can query the object for it and display it in the repr
output.
Upvotes: 2
Reputation: 318478
The stop value of a range
or xrange
is always exclusive.
Quote from the docs (Python 2):
If
step
is positive, the last element is the largeststart + i * step
less thanstop
; ifstep
is negative, the last element is the smalleststart + i * step
greater thanstop
.
And for Python 3:
For a positive
step
, the contents of a range r are determined by the formular[i] = start + step*i
wherei >= 0
andr[i] < stop
.For a negative
step
, the contents of the range are still determined by the formular[i] = start + step*i
, but the constraints arei >= 0
andr[i] > stop
.
About the second part of your question regarding the repr()
of the xrange
:
xrange(1, 10, 4)
and xrange(1, 13, 4)
are identical and repr()
for native python objects usually returns valid python code to recreate the object. This does not need to be the exactly same python code that initially created the object.
Upvotes: 4