[python] Have a little understanding of iterators?

for loop and iterator

In Python, the for loop is a commonly used iterative structure for traversing the elements in an iterable object. An iterator is a special object that implements the iteration protocol, allowing elements to be accessed one by one in a certain order.

There is a close relationship between for loops and iterators. In fact, for loops work based on iterators. When using a for loop to traverse an iterable object, Python will automatically create an iterator object internally, and use the iterator to fetch elements one by one until all elements have been visited.

The thing after in in the for loop must be an iterable, that is, it must be an iterable object.

Here is an example that demonstrates the relationship between for loops and iterators:

fruits = ['apple', 'banana', 'orange']

# Use a for loop to iterate through the list elements
for fruit in fruits:
    print(fruit)

# The above code is equivalent to the following iterator method
iterator = iter(fruits) # Create an iterator object
while True:
    try:
        fruit = next(iterator) # get the next element
        print(fruit)
    except StopIteration:
        break

In the above example, we first use the for loop to traverse the elements in the list fruits and print out the name of each fruit. Then, we manually created an iterator object iterator, and used the next() function to get the elements one by one until a StopIteration exception was encountered, indicating that All elements have been visited.

As you can see, using the for loop can simplify the iteration process, without explicitly creating an iterator object and handling StopIteration exceptions. Many of Python’s built-in objects (such as lists, tuples, dictionaries, etc.) are iterable, so they can be used directly in for loops.

List

lst = [1, 2, 3]
for i in lst:
    print(i)

dictionary

d = {<!-- -->"a": 1, "b": 2}
for i in d:
    print(i)

File operations

with open(my.txt, "r") as f:
    for i in f:
        print(i)

In addition to the built-in iterable objects, you can also customize the iterator class to implement your own iteration logic. In this way, you can use a custom iterator in a for loop to traverse a specific data structure or implement a specific iteration behavior.

The core behind for loop is iterator and iterable object.

Iterators and iterable objects

https://docs.python.org/3/glossary.html

In Python, iterables and iterators are two related but different concepts.

Iterable refers to an object that implements the __iter__() method, or implements the __getitem__() method and can be accessed in order Object. Both of these are to ensure that it can return an iterator under the action of the iter function. Iterable objects can be iterated, that is, they can be used in for loops. Common iterable objects include lists, tuples, strings, dictionaries, sets, etc.

Iterator is a special object that implements the iteration protocol and has __iter__() and __next__() methods. The iterator is used to return the elements in the iterable object one by one. Each time the __next__() method is called, the next element will be returned. If there are no more elements, a StopIteration exception will be raised. Iterator objects can also record the iteration state during iteration.

Whenever a for loop is used to traverse an iterable object, Python will automatically create an iterator object internally, and call its __next__() method to obtain elements one by one. Therefore, it can be said that for loops work based on iterators.

Here is an example that demonstrates the concept of iterable objects and iterators:

fruits = ['apple', 'banana', 'orange']

# fruits are iterable objects that can be used in for loops
for fruit in fruits:
    print(fruit)

# create iterator object
iterator = iter(fruits)

# Call the iterator's __next__() method to get the next element
print(next(iterator)) # output: 'apple'
print(next(iterator)) # output: 'banana'
print(next(iterator)) # Output: 'orange'
print(next(iterator)) # raise StopIteration exception

In the above example, fruits is an iterable object, we can use it directly in the for loop to traverse the elements. At the same time, we can also use the iter() function to manually convert the iterable object into an iterator object, and use the next() function to get elements one by one. When all elements have been visited, continuing to call the next() function will cause a StopIteration exception.

It should be noted that the iterator is a one-time object, that is, during the iteration process, once the iterator returns all elements, it will be exhausted and cannot be used again. If you want to re-traverse the iterable object, you need to recreate the iterator object.

  • An iterable is more like a data saver, a container, which can have no state, and it doesn’t know where your iterator is at all. It needs to be able to generate an iterator.
  • The iterator must be stateful, but it does not need to implement a container. Of course, it must know internally what data it represents in the iterable. The iterator must have the method __next__. This method guarantees that it can return the next iterable when it is acted upon by next.

Customize an iterable object and the corresponding iterator-linked list

#! -*-conding=: UTF-8 -*-
# 2023/5/22 18:43

class NodeIter:
    def __init__(self, node):
        self.curr_node = node

    def __next__(self):
        if self. curr_node is None:
            raise StopIteration
        node, self.curr_node = self.curr_node, self.curr_node.next

        return node


class Node:
    def __init__(self, name):
        self.name = name
        self. next = None

    def __iter__(self):
        return NodeIter(self)


node1 = Node("node1")
node2 = Node("node2")
node3 = Node("node3")

node1.next = node2
node2.next = node3


if __name__ == '__main__':
    for node in node1:
        print(node.name)

If we want to use the linked list in the for loop, then we have to turn the linked list into an iterable by ourselves.

This code demonstrates how to customize an iterable object and the corresponding iterator.

First, we define a Node class, representing a node, each node has a name and a reference to the next node. The Node class implements the __iter__() method, which returns an iterator object.

Then, we define a NodeIter class as an iterator, which receives a node object as a parameter, and initializes the current node in the __init__() method. The NodeIter class implements the __next__() method, which is used to return the next node. Each time the __next__() method is called, it returns the current node as a result and updates the current node to the next node. When there are no more nodes, a StopIteration exception is thrown.

Finally, under the if __name__ == '__main__': condition, we use custom iterable objects and iterators for traversal. Through the syntax of for node in node1:, the __iter__() method of the node1 object will be called automatically to obtain the iterator, and obtained one by one through the iterator node, and print the name of the node.

Running the above code, the output is:

node1
node2
node3

This example shows how to customize iterable objects and iterators, and use them in for loops to implement custom iteration logic.

If you like this article, please follow it, or pay attention to my official account “Haige python“. I will continue to share high-quality Python articles and other related content.