Some understandings of iterators

Posted by djot on Tue, 27 Aug 2019 12:24:53 +0200

Iterable Objects and Iterators

_Iterative objects are not necessarily iterators, but iterators are always iterative objects. In Python, list sequence types, etc., we can traverse the data by for...in. We call it Iterable. The iterator must also implement _next_() as long as it implements _iter_() or _getitem_().

from collections.abc import Iterable, Iterator
my_list = [1, 2, 3]
my_itorlist = iter(my_list)
print(isinstance(my_list, Iterator))
print(isinstance(my_itorlist, Iterator))
pass

We can see that list is not an iterator type. The iter() function can turn an iterator object into an iterator object.


_After transforming an iterative object into an iterator object, we can access elements sequentially through next(). The following figure shows the transformation of iteratable objects to iterators and the method of accessing elements. After accessing all elements, a StopIteration exception is thrown.

my_list = [1,2,3]
my_itorlist = iter(my_list)
print(next(my_itorlist))
print(next(my_itorlist))
print(next(my_itorlist))
print(next(my_itorlist))

Why do we need iterators?

Fibonacci sequence refers to such a sequence: 1, 1, 2, 3, 5, 8, 13, 21, 34. When we encounter the problem that Fibonacci sequence needs to print the first 100 digits, the most common solution is to use the following while loop.

def fib(index):
    re_list = []
    n,a,b = 0,0,1
    while n<index:
        re_list.append(b)
        a,b = b, a+b
        n += 1
    return re_list

It is undeniable that this is a better solution, but in the face of printing a large number of Fibonacci sequences, we will face a problem of greatly increasing memory overhead. It's impossible for us to store all 10 million data in a list. The advantages of the iterator are demonstrated. The iterator returns only the next number, and does not record a single data. It calculates when we need to print. Using iterators to solve the Fibolacci sequence problem, we can face a large amount of data without worrying about memory usage.

class Fb:

    def __init__(self, n):
        self.n = n #Number of prints
        self.current = 0
        self.a = 0
        self.b = 1

    def __next__(self):
        if self.current < self.n:
            """
            a=b
            b=a+b
            """
            self.a, self.b = self.b, self.a + self.b
            self.current += 1
            return self.a
        else:
            raise StopIteration

    def __iter__(self):
        return self


if __name__ == '__main__':
    fib_print = Fb(1000)
    for num in fib_print:
        print(num)

Custom Iterator

By customizing an iterator, we can better understand what an iterator is.

from collections.abc import Iterator#Abstract base classes for importing iterators

class Company(object):
    def __init__(self, employee_list):
        self.employee = employee_list

    def __iter__(self):
        return LevyIterator(self.employee)



class LevyIterator(Iterator):#Custom Iterator
    def __init__(self,employee_list):
        self.iter_employee = employee_list
        self.index = 0

    def __next__(self):
        try:
            next_obj = self.iter_employee[self.index]
        except IndexError:#Capture Index exceptions
            raise StopIteration#Abnormal StopIteration throwing an iterator
        self.index +=1
        return next_obj
if __name__ == "__main__":
    company = Company(["tom", "bob", "jane"])
    my_itor = iter(company) #To become an iterator object
    while True:
        try:
            print (next(my_itor))
        except StopIteration:
            break

    # for item in  company:
    #     print(item)

In the above code, while.. break code in main is the part of our iteration output, which is the essence of for.. in output: first, the object is transformed into an iterator type through obj. ITER (), and then the element is output through continuous next(obj).

Topics: Python