python的迭代对象(iterable)和迭代器(iterator)

本文探讨Python中的可迭代对象(iterable)和迭代器(iterator)。可迭代对象如list、string、tuple和dictionary可在for loop中使用,而iterator允许通过next()函数逐个获取数据。可迭代对象可以通过__iter__方法或__getitem__方法提供迭代器,而迭代器需实现__next__方法以返回下一个可迭代对象的值。for loop的实现依赖于GET_ITER字节码,通过获取迭代器来遍历数据。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

list 呢它本身是一个有序的结构, 那你 for in 来拿的话, 可以是第零个, 第一个, 第二个这个事儿比较符合直觉, 但是我们知道 dictionary 也可以这么用, 那 dictionary 可不是按顺序的给你摆在这儿, 第一个给你摆在这儿的, 对不对, 他怎么也可以用这个 for in 的结构呢, 甚至包括在 python 中打开的文件都可以用 for in 的结构, 那么知道文件肯定是一个相对来说比较复杂的数据结构了对吧, 他肯定也不是给你标好了, 第零个, 第一个, 第二个他怎么也能这么用呢, 那我们今天就来揭示这件事背后的秘密哦, 在 for loop 实现背后呢有两个核心概念, 一个叫可迭代对象, 一个叫迭代器。

先来看官方 doc 的解释,iterable 也就是可迭代对象, 他说的是一个对象, 然后可以一个一个的返回他的成员, 他给了一些例子吧, 比如说 list string tuple 啊, 包括 dictionary 啊, 然后他还特别强调了对吧, 这个 iterable 可以在 for loop 里面使用, 当然确切的说是 for loop 里面 in 后面那个东西必须是一个 iterable, 也就是必须是一个可迭代对象

An object capable of returning its members one at a time. Examples of iterables include all sequence types (such as [`list`]( https://docs.python.org/3/library/stdtypes.html#list "list"), [`str`]( https://docs.python.org/3/library/stdtypes.html#str "str"), and [`tuple`]( https://docs.python.org/3/library/stdtypes.html#tuple "tuple")) and some non-sequence types like [`dict`]( https://docs.python.org/3/library/stdtypes.html#dict "dict"), [file objects]( https://docs.python.org/3/glossary.html#term-file-object), and objects of any classes you define with an `__iter__()` method or with a `__getitem__()` method that implements [sequence]( https://docs.python.org/3/glossary.html#term-sequence) semantics.

那我们再看 iterator, 也就是迭代器, iterator 呢是一个表示数据流的对象, 你可以使用 next 函数, 不断地从这个对象里面获取新的数据

An object representing a stream of data. Repeated calls to the iterator’s [`__next__()`]( https://docs.python.org/3/library/stdtypes.html#iterator.__next__ "iterator.__next__") method (or passing it to the built-in function [`next ()`]( https://docs.python.org/3/library/functions.html#next "next")) return successive items in the stream. When no more data are available a [`StopIteration`]( https://docs.python.org/3/library/exceptions.html#StopIteration "StopIteration") exception is raised instead. At this point, the iterator object is exhausted and any further calls to its `__next__()` method just raise [`StopIteration`]( https://docs.python.org/3/library/exceptions.html#StopIteration "StopIteration") again. Iterators are required to have an `__iter__()` method that returns the iterator object itself so every iterator is also iterable and may be used in most places where other iterables are accepted. One notable exception is code which attempts multiple iteration passes. A container object (such as a [`list`]( https://docs.python.org/3/library/stdtypes.html#list "list")) produces a fresh new iterator each time you pass it to the [`iter ()`]( https://docs.python.org/3/library/functions.html#iter "iter") function or use it in a [`for`]( https://docs.python.org/3/reference/compound_stmts.html#for) loop. Attempting this with an iterator will just return the same exhausted iterator object used in the previous iteration pass, making it appear like an empty container.

从一个 high level 的角度看呢, 一个 iterable 更像是一个数据的保存者, 一个 container, 它是可以没有状态的, 它可以完全不知道你这个 iterator 数到哪了, 但它需要有能力产生一个 iterator, 而 iterator 一定是有状态的, 但是它并不需要实现一个 container, 他当然内部肯定知道它代表这个 iterator 里面是什么数据, 但是它不用实现一个 interface, 不用实现一个接口来修改里面的数据,

从实现上看呢, 一个 iterable 要么就有这个 __iter__ 这个 method, 要不然他就需要是一个 sequence, 然后有 __get_item__ 这个 method, 这两者都是为了保证它可以在 iter 这个函数的作用下返回一个 iterator, 而 iterator 必须要有 __next__ 这个 method, 这个 method 保证它在被 next 作用的时候可以返回下一个 iterable 里面的值

从代码实现上看

如果观察一个 for loop 循环的字节码,比如这样的代码:

iterable = [1, 2, 3]
    for item in iterable:
        print(item)

它的字节码实现是这样的:

```python
    0 LOAD_NAME               0 (iterable)
    3 GET_ITER
```

GET_ITER 在 cpython 的实现中会尝试获取一个 iterator。

也就是说 python 为了实现for loop 会尝试对 a这个 iterable 对象去获取一个 iterator,然后利用这个 iterator 来迭代。

举一个实现了链表的例子:

```python
class NodeIter: # 迭代器 iterator
    def __init__(self, node):
        self.node = node

    # iterator其实不需要实现__iter__,但是 python 的官方文档规定必须实现 __iter__ 
    # 因为有人可能会写这样的代码: for it in iter(a): 也就是说他在没有搞懂原理的情况下显式地获取了一个 iterator
    # 这个例子比较极端,但是现实中真的有人会忘记自己传给for loop的是一个iterable还是一个iterator
    def __iter__(self):
        return self

    def __next__(self):
        if self.node is None:
            raise StopIteration # 迭代结束,按照规定抛出这个异常
        else:
            value = self.node.value
            node = self.node.next
            return node

class Node: # 可迭代对象 iterable
    def __init__(self, value, children=None):
        self.value = value
        self.next = None

    def __iter__(self):
        return NodeIter(self)
```

另一个例子是 pytorch 中的 Dataset,它实现了__getitem__。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值