I want to print out all the odd numbers given in the call: o = Odds(10)
, the output should be 1, 3, 5, 7, 9
but I'm doing something wrong in the commented area, it should be relatively simple but I can't see it.
class Odds:
def __init__(self, arg):
self.num = arg
def __iter__(self):
return OddsIterator(self.num)
class OddsIterator:
def __init__(self, arg):
self.high = arg
self.low = 1
def __next__(self):
if self.low <= self.high:
if (self.low % 2) == 0: #somethings wrong around here
self.low += 1
else:
self.low += 1
return self.low - 1
raise StopIteration
Your if
branch doesn't return anything , so you always reach raise StopIteration
at that point. Say low
is 2 and high
is 10:
if self.low <= self.high: # true, 2 <= 10
if (self.low % 2) == 0: # true, 2 is even
self.low += 1
# else branch is skipped, so we come to the next line
raise StopIteration # iterator ends
You need to create larger steps in __next__
, because that method always returns something. Instead of not returning when a number is even, you need to return the next number in the sequence, always, so increment self.low
by 2 each time, and the first time make sure you start with an odd number:
def __next__(self):
if self.low > self.high:
raise StopIteration
if self.low % 2 == 0:
self.low += 1 # ensure we get an odd number first
retval = self.low
self.low += 2
return retval
I inverted testing for the StopIteration
condition here to make it clearer that the function always returns something when there is still a value to return from the iterable.
Just to re-iterate, __next__
always returns something, you can't expect it to not return something, so it'll at the very least return None
. The iterator is not counting . Python doesn't go lets ask what the result is for 1, then for 2, then for 3 . Python simply asks what the next value in the sequence of odd numbers is . After 1, comes 3, but Python doesn't know that, your code needs to produce that .
Your specific problem is that the first even number will fall through to the StopIteration
line, rendering your sequence rather short: { 1 }
.
It seems to me that generating all the odd numbers should be substantially easier. Start with -1
then, on every call:
That can be achieved with:
class Odds:
def __init__(self, arg):
self.num = arg
def __iter__(self):
return OddsIterator(self.num)
class OddsIterator:
def __init__(self, arg):
self.high = arg
self.curr = -1
def __next__(self):
self.curr += 2
if self.curr > self.high:
raise StopIteration
return self.curr
o = Odds(10)
for i in o:
print (i)
Having said that, I'm not sure I'd implement it the same way you did. It seems unnecessary to have two separate classes when you can combine them into a single one:
class Odds:
def __init__(self, end):
self.curr = -1
self.lim = end
def __next__(self):
self.curr += 2
if self.curr > self.lim:
raise StopIteration
return self.curr
def __iter__(self):
return self
o = Odds(10)
for i in o:
print (i)
In addition, you can make it far more general by providing start
and step
values as well:
class XFor:
def __init__(self, start, end, step):
self.curr = start - step
self.lim = end
self.step = step
def __next__(self):
self.curr += self.step
if self.curr > self.lim:
raise StopIteration
return self.curr
def __iter__(self):
return self
o = XFor(1,10,2)
for i in o:
print (i)
Although it's then encroaching dangerously on what range()
does so I'd just use that. Unless your intent is self-education, in which case have fun.
How about using a simple generator for this purpose, unless you need the Odds class for other purposes -
def odds(num):
for i in range(1, num + 1):
if i % 2 != 0:
yield i
print(list(odds(11)))
If you do want to use class Odds
for specific reason -
class Odds:
def __init__(self, num):
self.high = num
def __iter__(self):
return odds(self.high)
print(list(Odds(10))
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.