python迭代器也是可迭代的,怎解?

2023-06-0915:50:10编程语言入门到精通Comments843 views字数 5813阅读模式

第一次接触 Python 的时候,你可能写过类似 for i in [2, 3, 5, 7, 11, 13]: print(i)这样的语句。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/46299.html

但是,你想过 Python 在处理 for in 语句的时候,具体发生了什么吗?什么样的对象可以被for in来枚举呢?文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/46299.html

for loop和迭代器

在Python中,for循环是一种常用的迭代结构,用于遍历可迭代对象中的元素。迭代器是一种特殊的对象,它实现了迭代协议,允许按照一定的顺序逐个访问元素。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/46299.html

for循环和迭代器之间存在密切的关系,实际上,for循环是基于迭代器工作的。当使用for循环遍历可迭代对象时,Python会在内部自动创建一个迭代器对象,并使用该迭代器来逐个获取元素,直到所有元素都被访问完毕。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/46299.html

for loop里面in后面那个东西必须是一个iterable, 也就是必须是一个可迭代对象文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/46299.html

下面是一个示例,演示了for循环和迭代器之间的关系:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/46299.html

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

# 使用for循环遍历列表元素
for fruit in fruits:
    print(fruit)

# 上述代码等价于下面的迭代器方式
iterator = iter(fruits)  # 创建迭代器对象
while True:
    try:
        fruit = next(iterator)  # 获取下一个元素
        print(fruit)
    except StopIteration:
        break

在上面的示例中,我们首先使用for循环遍历了列表fruits中的元素,打印出每个水果的名称。然后,我们手动创建了一个迭代器对象iterator,并使用next()函数逐个获取元素,直到遇到StopIteration异常,表示所有元素都被访问完毕。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/46299.html

可以看到,使用for循环可以简化迭代过程,不需要显式地创建迭代器对象和处理StopIteration异常。Python的许多内置对象(如列表、元组、字典等)都是可迭代的,因此可以直接在for循环中使用它们。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/46299.html

列表文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/46299.html

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

字典文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/46299.html

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

文件操作文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/46299.html

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

除了内置的可迭代对象,你还可以自定义迭代器类,实现自己的迭代逻辑。这样,你就可以在for循环中使用自定义的迭代器来遍历特定的数据结构或实现特定的迭代行为。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/46299.html

for loop的背后核心是迭代器可迭代对象文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/46299.html

迭代器和可迭代对象

python迭代器也是可迭代的,怎解?
python迭代器也是可迭代的,怎解?

https://docs.python.org/3/glossary.html文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/46299.html

cpython本身并不总是遵循每个iterator都是iterable的要求。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/46299.html

在Python中,可迭代对象和迭代器是两个相关但不同的概念。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/46299.html

可迭代对象

可迭代对象(Iterable)是指实现了__iter__()方法的对象,或者实现了__getitem__()方法且可按照顺序访问的对象。这两者都是为了保证它可以在iter这个函数的作用下返回一个iterator(迭代器)。可迭代对象可以被迭代,也就是可以在for循环中使用。常见的可迭代对象包括列表、元组、字符串、字典、集合等。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/46299.html

判断是否是可迭代对象文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/46299.html

from collections.abc import Iterable

print(isinstance(123, Iterable)) 

print(isinstance(True, Iterable))

print(isinstance('abc', Iterable))

print(isinstance([], Iterable))

print(isinstance({}, Iterable))

print(isinstance((), Iterable))

输出结果为:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/46299.html

False
False
True
True
True
True

迭代器

迭代器(Iterator)是一种特殊的对象,它实现了迭代协议,具有__iter__()__next__()方法。迭代器用于逐个返回可迭代对象中的元素,每次调用__next__()方法都会返回下一个元素,如果没有更多元素,则引发StopIteration异常。迭代器对象还可以在迭代过程中记录迭代状态。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/46299.html

每当使用for循环来遍历一个可迭代对象时,Python会在内部自动创建一个迭代器对象,并调用其__next__()方法来逐个获取元素。因此,可以说for循环是基于迭代器工作的。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/46299.html

下面是一个示例,演示了可迭代对象和迭代器的概念:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/46299.html

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

# fruits是可迭代对象,可以在for循环中使用
for fruit in fruits:
    print(fruit)

# 创建迭代器对象
iterator = iter(fruits)

# 调用迭代器的__next__()方法获取下一个元素
print(next(iterator))  # 输出:'apple'
print(next(iterator))  # 输出:'banana'
print(next(iterator))  # 输出:'orange'
print(next(iterator))  # 引发StopIteration异常

在上面的示例中,fruits是一个可迭代对象,我们可以直接在for循环中使用它来遍历元素。同时,我们也可以使用iter()函数手动将可迭代对象转换为迭代器对象,并使用next()函数来逐个获取元素。当所有元素都被访问完毕时,继续调用next()函数会引发StopIteration异常。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/46299.html

需要注意的是,迭代器是一种一次性的对象,即在迭代过程中,一旦迭代器返回了所有元素,它就会耗尽,无法再次使用。如果想重新遍历可迭代对象,需要重新创建迭代器对象。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/46299.html

  • 一个iterable(可迭代对象)更像是一个数据的保存者,一个container,它是可以没有状态的,它可以完全不知道你这个iterator(迭代器)数到哪了,它需要有能力产生一个iterator(迭代器)。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/46299.html
  • iterator一定是有状态的,但是它并不需要实现一个container,它当然内部肯定知道它代表这个iterable(可迭代对象)里面是什么数据。iterator(迭代器)必须要有__next__这个method。这个method保证它在被next作用的时候可以返回下一个iterable(可迭代对象)。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/46299.html

自定义一个可迭代对象和对应的迭代器-遍历链表

#! -*-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)

如果我们想在for loop里面使用链表的话,那我们就要自己把链表变成一个iterable(可迭代对象)。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/46299.html

这段代码演示了如何自定义一个可迭代对象和对应的迭代器。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/46299.html

首先,我们定义了一个Node类,表示一个节点,每个节点具有一个名称和一个指向下一个节点的引用。Node类实现了__iter__()方法,该方法返回一个迭代器对象。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/46299.html

然后,我们定义了一个NodeIter类作为迭代器,它接收一个节点对象作为参数,并在__init__()方法中初始化当前节点。NodeIter类实现了__next__()方法,用于返回下一个节点。在每次调用__next__()方法时,它会将当前节点作为结果返回,并将当前节点更新为下一个节点。当没有更多节点时,抛出StopIteration异常。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/46299.html

最后,在if __name__ == '__main__':条件下,我们使用自定义的可迭代对象和迭代器进行遍历。通过for node in node1:的语法,会自动调用node1对象的__iter__()方法获取迭代器,并通过迭代器逐个获取节点,并打印节点的名称。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/46299.html

运行以上代码,输出结果为:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/46299.html

node1
node2
node3

这个示例展示了如何自定义可迭代对象和迭代器,并在for循环中使用它们实现自定义的迭代逻辑。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/46299.html

如果我们想从node2开始for loop,可能我们就会把上面的代码改吧改吧:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/46299.html

#!usr/bin/env python
# -*- coding:utf-8 _*-
# __author__:lianhaifeng
# __time__:2023/5/22 21:11

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__':
    it = iter(node1)
    first = next(it)

    for node in it:
        print(node.name)

运行一下:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/46299.html

Traceback (most recent call last):
  File "E:/vedio/golang/oldboy-liwenzhou-5/resources-master/resources/PythonStart/ss.py", line 39, in <module>
    for node in it:
TypeError: 'NodeIter' object is not iterable

纳尼?和我们想的不一样啊!文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/46299.html

原来,it=iter(node1)的时候返回的是iterator,而这里的iterator(迭代器)不是iterable(可迭代的)的。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/46299.html

那要怎么修改能使从node2开始for loop呢?文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/46299.html

其实很简单,那就是让iterator(迭代器)本身也是iterable(可迭代对象):文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/46299.html

#!usr/bin/env python
# -*- coding:utf-8 _*-
# __author__:lianhaifeng
# __time__:2023/5/22 21:11

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

    def __iter__(self):
        return self


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__':
    it = iter(node1)
    first = next(it)

    print(first.name)
    for node in it:
        print(node.name)

输出结果为:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/46299.html

node1
node2
node3

可以看到,已经是我们想要的了。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/46299.html

让我们再看下Python官网iterator(迭代器)的描述:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/46299.html

python迭代器也是可迭代的,怎解?

至此,我们也就明白为什么python官方文档里也要求:让每个迭代器也是可迭代的文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/46299.html

文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/46299.html
  • 本站内容整理自互联网,仅提供信息存储空间服务,以方便学习之用。如对文章、图片、字体等版权有疑问,请在下方留言,管理员看到后,将第一时间进行处理。
  • 转载请务必保留本文链接:https://www.cainiaoxueyuan.com/ymba/46299.html

Comment

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定