I've been assigned the following Python homework:
Implement myrange that acts like
range
using iterators. Define a functionmyrange
and define a classMyRange
.
We've just seen iterators, so I think I'm asked to use them. I am not required to write a sophisticated code, but only something that allows the range-for loop syntax.
Professor said that, roughly, an iterator is an object for which the dunder method __next__
is provided. I've seen there are similar questions here. However, none of them is defining the __next__
method in a class.
So, here's what I did: first I implemented MyRange
class and then my range
. After that, I did two tests.
Question: It works, but I'd like to be sure from you if I solved correctly the question, I mean, if this is what I was supposed to do :-) As I said, I've just seen what is an iterator.
class MyRange():
def __init__(self,data):
self.data = data
self.check()
self.index = -1
def check(self):
assert len(self.data)>0, "Not enough data"
def __iter__(self):
return self
def __next__(self):
if self.index == len(self.data)-1:
raise StopIteration
self.index= self.index+1
return self.data[self.index]
def my_range(*args):
return MyRange((args))
print("Tests using MyRange class \n")
for i in MyRange((1,2,3,4)):
print(i)
print("Tests with range for loop \n")
for i in my_range(1,2,3,4,5,"Hello"):
print(i)
r = MyRange((1,2,3,4,"Last Value"))
print(next(r))
print(next(r))
print(next(r))
print(next(r))
print(next(r))
print(next(r))
I'll show here the output, which seems the correct one:
1
2
3
4
Tests with range for loop
1
2
3
4
5
Hello
1
2
3
4
Last Value
Traceback (most recent call last):
File "Untitled3.py", line 45, in <module>
print(next(r)) #<-- Raises StopIteration
File "Untitled3.py", line 16, in __next__
raise StopIteration
StopIteration
1 Answer 1
You have an iterator
Your code implements an iterator, you are right. You have defined a __next__
method and it works well.
I would tweak your code slightly, though:
- remove the
check
because iterators can be empty and that is okay; - fix minor spacing issues around operators, etc;
- use augmented assignment to increment;
- change order of statements in
__next__
to be more idiomatic.
All in all, MyRange
would end up looking like this:
class MyRange():
def __init__(self,data):
self.data = data
self.index = -1
def __iter__(self):
return self
def __next__(self):
self.index += 1
if self.index == len(self.data):
raise StopIteration
return self.data[self.index]
In particular, the changes to __next__
are because you start by setting self.index
to the value of the index you would like to read. Then, just before reading the value from data
, you ensure you can actually use that index and raise StopIteration
if you can't.
range
is not any iterator
However, the "problem statement" says to implement an iterator that behaves like range
, and as brought to your attention in the comments, range
is a very specific iterator that can take up to 3 arguments: start
, stop
, and step
.
Take a look at the docs for the range function here.
Having said that, solving this "issue" does not need to be very difficult.
You could do something as simple as having my_range
take the three arguments,
generate the data
you need, and then feed it into MyRange
to iterate, but that seems like a waste.
What I think is more or less the intended approach, is to define MyRange
to expect the start
, stop
, and step
values, and the my_range
function is what the user calls, which then fills in the start
, stop
, and step
values that are used by default and calls MyRange
.
E.g.,
my_range(10)
would callMyRange(0, 10, 1)
;my_range(4, 12)
would callMyRange(4, 12, 1)
;my_range(0, 13, -3)
would callMyRange(0, 13, -3)
.
(In the examples above, I assumed you changed MyRange
to do what I described.)
Therefore, just for the sake of clarity: what would be happening is that the __next__
method would use the three arguments start
, stop
, and step
(and possibly other auxiliary variables you create) to compute the successive values, instead of generating all of them at once and then returning one by one.
-
\$\begingroup\$ Thanks for your answer, I got the point! So the signature of
my_range
should bemy_range(start, stop,step)
too, right? \$\endgroup\$bobinthebox– bobinthebox2021年04月26日 06:35:31 +00:00Commented Apr 26, 2021 at 6:35 -
\$\begingroup\$ @bobinthebox yes, that is my interpretation of the problem statement :) \$\endgroup\$RGS– RGS2021年04月26日 06:37:03 +00:00Commented Apr 26, 2021 at 6:37
-
\$\begingroup\$ Thanks, I'll try to fix this :-) @RGS \$\endgroup\$bobinthebox– bobinthebox2021年04月26日 06:37:53 +00:00Commented Apr 26, 2021 at 6:37
-
\$\begingroup\$ @bobinthebox good luck! But sorry, just to be clear, probably your
my_range
function also has to work with a single argument,my_range(stop)
and two arguments,my_range(start, stop)
, just likerange
does, yeah? \$\endgroup\$RGS– RGS2021年04月26日 06:39:46 +00:00Commented Apr 26, 2021 at 6:39 -
\$\begingroup\$ I agree, I should use default arguments to achieve this behaviour, right? @RGS \$\endgroup\$bobinthebox– bobinthebox2021年04月26日 07:34:42 +00:00Commented Apr 26, 2021 at 7:34
Explore related questions
See similar questions with these tags.
myrange()
is supposed to act likerange()
, shouldn't it take similar arguments:start
,stop
, andstep
? It doesn't seem like your implementation is following the instructions, but you obviously have more context than I do. A range is a device to generate integers (integers often used to index into a data collection), but you have implementedmyrange()
to take the data collection itself as an argument. Perhaps you can edit your question to clarify. \$\endgroup\$__next__
in our class. I think I can fix this point. For the moment, assuming that the classMyRange
is correct, do you think thatmy_range
is implemented correctly? \$\endgroup\$range
two different ways (1) as a function (e.g. usingyield
in a loop), and (2) as a class (e.g. having__iter__
and__next__
methods). \$\endgroup\$MyRange
andmy_range()
are incorrect. You have successfully implemented an iterable object (MyRange
), but it is not an iterable object that behaves at all likerange()
. \$\endgroup\$