Reputation: 139
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
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
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