using __iter__ and __next__ to create my own class

Viewed 61

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?

1 Answers

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.

Related