2

I'm trying to create my own class for an iterator and found such an example:

class OddNum:
  """Class to implement iterator protocol"""

  def __init__(self, num = 0):
    self.num = num

  def __iter__(self):
    self.x = 1
    return self

  def __next__(self):
    if self.x <= self.num:
      odd_num = self.x
      self.x += 2
      return odd_num
    else:
      raise StopIteration

for num in OddNum(10):
  print(num)

the output is: 1 3 5 7 9

Now, if I remove the row odd_num = self.x and change return odd_num to return self.x, I get the following output: 3 5 7 9 11

What is the difference between the 2 codes, why should I define a variable to self.x?

3
  • You are taking the value of self.x from after adding on 2 rather than before doing so. So the difference is 2. Commented Jul 9, 2020 at 14:31
  • You increment self.x before you have a chance to return the initial value. Commented Jul 9, 2020 at 14:31
  • If you want to do that, initialize self.x = -1, so that self.x += 2 produces the first odd number. Commented Jul 9, 2020 at 14:32

1 Answer 1

2

In this specific case, what is happening is that you want to return the value of self.x from somewhere in the middle of the __next__ method, before you increment the value of self.x ready for the next iteration. Therefore the variable odd_num is used to hold this value. If instead you return self.x after the self.x += 2 statement (i.e. add on 2), then you will get a different answer, as you saw.

Another possibility for you to consider is, instead of writing your own class, to implement the iterator using a generator function, if what you want to do is use the value of some variable from somewhere in the middle of the code. This is because you can put a yield statement wherever you like. So in this example, it would look something like this:

def odd_num(num=0):
    x = 1
    while x <= num:
        yield x
        x += 2
  
    
for num in odd_num(10):
    print(num)

This gives:

1
3
5
7
9

(Note the while in place of the if that you had in your __next__ method.)

Here, the yield statement is before the x += 2 statement, which is not something that could be done using return from a method (function). So there is no need to save the value in another variable to use later.

Sign up to request clarification or add additional context in comments.

5 Comments

I had no idea you posted when I updated my answer. Deleted my nearly identical answer. Nice work.
@MadPhysicist BTW, don't feel you have to delete your answer. Given the very close timings, nobody could reasonably accuse you of simply copying what I wrote.
I deleted it because our content was so nearly identical, I felt that one of us should, and I happened to like your phrasing a little better.
Thank you very much for the detailed explanation. Another question from my side: In my example, why is the self.x = 1 is given within the iter function? Why not outside (for example as a class object attribute or before the if statement in the next function)?
@rodny9 The point there is that you can generate an iterator from the class multiple times. Try doing the following in the calling code: nums = OddNum(10) followed by for n in nums: print(n) and then again: for n in nums: print(n) -- it will work because nums.__iter__ is called to generate the iterator when the for statement is executed. Now try moving the self.x = 1 into __init__ instead of __iter__ and you should see that you can iterate once, but the second time you try to iterate, the iterator is already exhausted and you don't get any more values out.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.