pete the dude
pete the dude

Reputation: 139

Python class and __iter__

What are the benefits of using the __iter__ function in a Python class?

In the code below I am just setting up two simple classes. The first class takes in a list as an argument, and I am able to loop over this list without using the __iter__ function. The second bit of code uses the __iter__ function to loop over a list.

What is the benefit of using __iter__ when there are already ways of looping over stuff in a class?

EG 1: no __iter__


class test_class:
    def __init__(self, list):
        self.container_list = list

    def print (self):
        a = self.container_list
        return a

test_list = test_class([1,2,3,4,5,6,7])

x = test_class.print(test_list)

for i in x:
    print (i)



EG 2: yes __iter__

class list_using_iter:
    def __init__(self):
        self.list = [1,2,3,4]
        self.index = -1

    def __iter__(self):
        return self

    def __next__(self):
        self.index += 1
        if self.index == len(self.list):
            raise StopIteration
        return self.list [self.index]

r = list_using_iter()
itr = iter(r)

print(next(itr))
print(next(itr))
print(next(itr))
print(next(itr))

print(next(itr)) # Raises the exception!

Upvotes: 1

Views: 6423

Answers (2)

chepner
chepner

Reputation: 531205

Your first example is not iterable, but contains an attribute that is. Your second example is iterable, but you iterate simply by "following" another iterable. Here's an example of a iterable that does more work itself:

import itertools

class Fibs:
    def __init__(self, a, b):
        self.a = a
        self.b = b

    def __iter__(self):
        a = self.a
        b = self.b
        while True:
            yield a
            a, b = b, a + b

real_fibs = Fibs(0,1)

for i in itertools.islice(real_fibs, 10):
    print(i)

Fibs.__iter__ isn't simply regurgitating values obtained from some other value's __iter__ method; it is computing and yielding new values on demand.


Actually, the preceding is an example of a class that knows how to create its own iterator, rather than having each object be iterable. Here's a version that defines next itself.

class Fibs:
    def __init__(self, a, b):
        self.a = a
        self.b = b

    def __iter__(self):
        return self

    def __next__(self):
        rv = self.a
        self.a, self.b = self.b, self.a + self.b
        return rv

Upvotes: 1

Sam Hartman
Sam Hartman

Reputation: 6499

In both cases, the looping works because of __iter__. In your first example, your print function returns a loop. The implementation of the for keyword will call __iter__ (or the corresponding slot within the C implementation since the code involved is in the C interpreter) in order to loop over the list.

In your second example you could have written

for elt in r:
    print(elt)

which would have internally called __iter__ to implement the for loop.

In general you tend to use for rather than iter and next directly. The cases where you use iter and next directly are when you're producing a callback function that will produce an iterator or when you're defining one iterator in terms of another.

In terms of when should you write your own __iter__ or return some object that does its own iteration, that all depends on what functionality you want. For example, your first class is more powerful because two people can be iterating the list at the same time. In your second class, because you store the index in the class itself, only one person can successfully use the iterator at a time. However, if you had complex enough behavior, the second approach where you define your own __iter__ might make sense.

Upvotes: 0

Related Questions