6-iterator + generator

Posted by meomike2000 on Tue, 07 Dec 2021 12:51:36 +0100

iterator

1. Iteratable object

from collections import Interable 
print(isinstance([11,22,33],Interable))

If you want an object to be iteratable, you must override__ iter__ Method, so that it can pass the test of interoperable, but it can not be traversed by the for function at this time, so it is only an iteratable object, not an iterator.

Example code:

from collections.abc import Iterable

class ClassMate():
    def __init__(self) -> None:
        self.name = list()
    
    def add(self,name):
        self.name.append(name)
    
    def __iter__(self):
        pass

classmate = ClassMate()
print("judge ClassMate Whether it is an iteratable object:",isinstance(classmate,Iterable))

2. Iterator

Iterators must override__ iter__ Methods and__ next__ Method, where__ iter__ Method must return a reference to an object.

Iterators can be traversed using the for loop, for temp in xxx_obj execution process:

  1. Judgment object xxx_obj Yes No__ iter__ Method (iteratable object)
  2. Under the condition that the first step holds, call the iter function to get the object xxx_obj__ next__ The return value of is an iterator
  3. Automatically call the next function of the iterator, that is, the next function of the iterator__ next__ Method until a StopIteration exception is thrown.

For such a for loop

for i in seq:
    do_something_to(i)

It actually works like this

fetch_iterator = iter(q)
while True:
    try:
        i = fetch_iterator.next()
    except StopIteration:
        break
    do_something_to(i)

Iterators are very important. Dictionaries, files and lists in Python 3 are iteratable objects.

3. Write an iterative class

from collections.abc import Iterable
from collections.abc import Iterator
# The implementation will be an iteratable class

class ClassMate(object):
    def __init__(self) -> None:
        self.names = list()

    def add(self,name):
        self.names.append(name)

    def __iter__(self):
        # Returns an instance object of an iterator
        return ClassIterator(self)

class ClassIterator(object):
    def __init__(self,obj):
        self.obj = obj
        # Record the position of the previous pointer through the instance property
        self.current_num = 0

    def __iter__(self):
        pass
    def __next__(self):
        """next Method to execute this method"""
        if self.current_num < len(self.obj.names):
            # Manual cross-border prevention
            ret =  self.obj.names[self.current_num]
            self.current_num += 1
            return ret
        else:
            raise StopIteration
            # The for loop automatically catches errors

    
classmate = ClassMate()
classmate.add('Xiao Wang')
classmate.add('Xiaomei')
classmate.add('Xiao Ming')

class_iterator = iter(classmate)


print("judge classmate Is it an iteratable object",isinstance(classmate,Iterable))
print("judge class_iterator Is it an iterator",isinstance(class_iterator,Iterator))

for name in classmate:
    print(name)

# Output:
# True to determine whether classmate is an iteratable object
# Judge class_ Is iterator an iterator True
# Xiao Wang
# Xiaomei
# Xiao Ming

Execution process:

be careful:

  1. Of iteratable object A__ iter__ Returns the instance object B of an iterator. When initializing iterator B, iteratable object A needs to be passed into the initialization method;
  2. The iterator instance object passes an instance property self.current_num records the current read position
  3. You need to manually prevent out of bounds, otherwise the for statement will continue to execute__ next__, And capture the returned None. At this time, the for loop will automatically capture by throwing a StopIteration error.

4. Encapsulate into a class

The above method uses two classes, which is a bit wasteful of resources. We encapsulate them all into one class

from collections.abc import Iterable
from collections.abc import Iterator
# The implementation will be an iteratable class

class ClassMate(object):
    def __init__(self):
        self.names = list()
        self.current_count = 0

    def add(self,name):
        self.names.append(name)

    def __iter__(self):
        # Returns its own instance object
        return self
    
    def __next__(self):
        if self.current_count<len(self.names):
            ret = self.names[self.current_count]
            self.current_count+=1
            return ret
        else:
            raise StopIteration
    
classmate = ClassMate()
classmate.add('Xiao Wang')
classmate.add('Xiaomei')
classmate.add('Xiao Ming')

class_iterator = iter(classmate)


print("judge classmate Is it an iteratable object",isinstance(classmate,Iterable))
print("judge class_iterator Is it an iterator",isinstance(class_iterator,Iterator))

for name in classmate:
    print(name)

# Output:
# True to determine whether classmate is an iteratable object
# Judge class_ Is iterator an iterator True
# Xiao Wang
# Xiaomei
# Xiao Ming

5. Application of iterator

range and xrange

[supplement] the difference between range and xrange:

The difference between range and xrange: in python2, range returns a numeric value and xrange returns an iteratable object, which takes up a very small space,

In Python 3, range also returns an iteratable object.

Generating Fibonacci sequences using iterators

For example, you can use iterators to implement Fibonacci sequences

# General implementation
a = 0
b = 1
i = 0
while i<10:
    a,b = b,a+b
    print(a)
# Iterator implementation


class  Fibonacci(object):

    def __init__(self,all_num):
        self.max_num = all_num
        self.count_num = 0
        self.a = 0
        self.b = 1

    def __iter__(self):
        return self

    def __next__(self):
        if self.count_num < self.max_num:
            self.count_num += 1
            self.a,self.b = self.b,self.a+self.b
            return self.a
        else:
            raise StopIteration

fibo = Fibonacci(10)
for num in fibo:
    print(num)

List parsing

List parsing, list complehensinos, comes from Haskell language and can replace some usage scenarios of lambda, map and filter functions.

Syntax [expr for iter_val in iterable if cond_expr]

Example 1: calculate the square of a sequence member

map(lambda x:x**2,range(6))
# perhaps
[x**2 for x in range(6)]

Example 2: select odd numbers in seq sequence

filter(lambda x: x%2,seq)
# or
[x for x in seq if x%2]

Example 3: calculate the number of words in the text [list parsing supports multiple loops and multiple if statements, and the execution order is from left to right]

f = open('test.txt','r')
len([word for line in f for word in line.split()])

The generator expression is similar to the list parsing expression: (expr for iter_val in iterable if cond_expr). Because the generator expression uses "lazy evaluation", it is more effective in memory than the list parsing expression.

generator

Function implementation of generator

Start the generator by send ing

Topics: Python Back-end