I started to write a simple implementation and the result was this monster:
from operator import lt, gt
def xrange(start, stop=None, step=1):
"""
start is included
stop is excluded
step is default +1
"""
if step == 0:
raise ValueError('xrange() arg 3 must not be zero')
op_cmp = lt if step > 0 else gt # get the right operator for comparison
if stop is None:
# in the case when you just use one argument
# then the generator count up like the normal range function
current = 0
stop = start
else:
current = start
while True:
# stop is excluded
if op_cmp(current, stop):
yield current # first value is included
current += step # then step
else:
break
But to understand it a bit better, here a stupid xrange:
import time
def xrange(stop):
current = 0
while True:
if current < stop:
yield current
# the generator is suspended here
# the state is saved in the generator
current += 1
else:
break # stops the generator
generator = xrange(4)
print(next(generator))
time.sleep(1)
print(next(generator))
time.sleep(1)
print(next(generator))
time.sleep(1)
print(next(generator))
time.sleep(1)
print(next(generator))
Output:
0
1
2
3
Error:
StopIteration Traceback (most recent call last)
<ipython-input-53-793910a0cbc2> in <module>()
8 print(next(generator))
9 time.sleep(1)
---> 10 print(next(generator))
StopIteration:
The manual call with next is what a for-loop does automatically and when a StopIteration is raised, the for-loop stops. You won't see this error.
As mentioned before
xrange
has been renamed to
range
in Python 3.
If you want to generate an output which does not fit into your memory, you should use
xrange
in
Python 2
.
Then you can iterate over the xrange object and do stuff with the numbers.
If you need a list from a range function in Python 3, you have to call
list(range(n))
.