Andy Perez
Andy Perez

Reputation: 327

Python - How does the index in the __next__() method work?

Can someone explain to me how the __iter__() and __next__() functions handle indices? Are they base 0 or base 1?

I have been playing around with it, but I'd like to know what Python is actually doing on the back end. I tried the example class below:

>>> class my_class:
        def __init__(self, *stuff):
            self.__stuff = stuff

        def __iter__(self):
            self.__n = 0
            return iter(self.__stuff)

        def __next__(self):
            if self.__n <= len(self.__stuff):
                self.__n += 1
                return self.__stuff(self.__n)
            else:
                raise StopIteration

>>> x = my_class(1, 2, 3, 4)
>>> for each in x:
        print(each)
1
2
3
4

Unless, I'm mistaken, the first self.__n value that __next__() uses should be 1, which should produce, this:

>>> for each in x:
        print(each)
2
3
4

What am I missing? How does it know to start at self.__stuff[0]?

Upvotes: 0

Views: 961

Answers (3)

Liu Suyuan
Liu Suyuan

Reputation: 21

  • When you use my_class, it first calls the __init__, then calls the __iter__, last is the __next__.
  • In your code, when it calls __iter__, it return iter(self.__stuff),then is over, __next__ is not called. So the output is what you see.
  • If you want __next__ called, you can change your code like this(here self.__n that __next__ uses starts from 1):

    class my_class:
        def __init__(self, *stuff):
          self.__stuff = stuff
    
        def __iter__(self):
          self.__n = 0
          print('__iter__ is called')
          return self
    
        def __next__(self):
          print('__next__ is called')
          if self.__n <= len(self.__stuff):
              self.__n += 1
              return self.__stuff(self.__n)
          else:
              raise StopIteration
    
  • Tip: you can use print to help you understand what the code is doing, like print function in the code above.

Upvotes: 2

Marcus.Aurelianus
Marcus.Aurelianus

Reputation: 1518

When you call for each in x:, it do nothing with __next__() in your class definition, so it start 1 of your object attribute rather than 2.

Even it you want to call something like print(next(x)) it will give you 'TypeError: 'tuple' object is not callable', because self.__stuff(self.__n) is invalid as in self.__stuff is a tuple and self.__n is an integer. You can only call tuple[int] rather than tuple(int).

Try following code after your code mentioned it will return you desired output then raise an exception.

for each in x:    
    print(next(x))

Result:

2
3
4
raise StopIteration

Upvotes: 3

Ignacio Vazquez-Abrams
Ignacio Vazquez-Abrams

Reputation: 798676

The __iter__() method returns iter(self.__stuff) instead of self. As such, the tuple passed to __init__() is iterated over, not the object.

Upvotes: 1

Related Questions