← 返回题目列表

Python 中可迭代对象和迭代器有什么区别?

高频 中等 第 11 / 27 题 更新于 2026/07/27
可迭代对象迭代器__iter____next__

简化版

可迭代对象是能被 iter() 转成迭代器的对象,通常实现 __iter__;迭代器是能被 next() 逐个取值的对象,必须实现 __iter____next__。列表是可迭代对象但不是迭代器,生成器对象本身就是迭代器。

详细版

判断一个对象能不能用于 for 循环,本质上看它是否可迭代:

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

for 循环背后大致会做:

it = iter(items)
while True:
    try:
        item = next(it)
    except StopIteration:
        break

区别:

对象需要能力特点
可迭代对象__iter__ 返回迭代器可以被 for 遍历
迭代器__iter__ 返回自己,__next__ 返回下一个元素保存遍历状态

示例:

lst = [1, 2, 3]
it = iter(lst)

print(next(it))  # 1
print(next(it))  # 2

列表每次调用 iter(lst) 会返回新的迭代器,所以可以重复遍历;迭代器通常会被消耗,遍历完后再遍历就没有元素了。

完整版教学

一、for 循环背后的协议

Python 的 for 循环不只服务列表,它服务所有可迭代对象:

for x in obj:
    ...

背后依赖迭代协议:

  1. 调用 iter(obj) 获取迭代器;
  2. 不断调用 next(iterator) 获取下一个值;
  3. 遇到 StopIteration 结束循环。

所以只要一个对象能提供这个协议,就能放进 for

这就是 Python 很重要的协议式编程思想:不一定要求继承某个类,只要实现约定方法即可。

二、可迭代对象是什么

可迭代对象通常实现:

__iter__()

它返回一个迭代器。

列表、元组、字符串、字典、集合都是可迭代对象:

for ch in "abc":
    print(ch)

for key in {"a": 1, "b": 2}:
    print(key)

它们本身保存数据,但不一定保存“当前遍历到哪里”的状态。遍历状态通常由迭代器保存。

三、迭代器是什么

迭代器必须实现:

__iter__()
__next__()

其中 __iter__ 返回自己,__next__ 返回下一个值。

自定义迭代器:

class Counter:
    def __init__(self, limit):
        self.current = 0
        self.limit = limit

    def __iter__(self):
        return self

    def __next__(self):
        if self.current >= self.limit:
            raise StopIteration
        value = self.current
        self.current += 1
        return value

使用:

for x in Counter(3):
    print(x)

输出 0、1、2

四、为什么列表不是迭代器

列表是可迭代对象:

lst = [1, 2, 3]
iter(lst)

但它不是迭代器:

next(lst)  # TypeError

原因是列表不负责保存当前遍历位置。每次调用 iter(lst),都会创建一个新的列表迭代器。

it1 = iter(lst)
it2 = iter(lst)
print(next(it1))  # 1
print(next(it2))  # 1

两个迭代器互不影响。

五、迭代器为什么通常只能遍历一次

迭代器保存自身状态,调用 next() 会推进状态。

it = iter([1, 2])
print(list(it))  # [1, 2]
print(list(it))  # []

第一次已经消耗完了,第二次没有剩余元素。

生成器对象也是迭代器,所以也有这个特性:

g = (x for x in range(3))
list(g)
list(g)  # []

如果需要多次遍历,要重新创建迭代器或保存结果。

六、如何设计自定义可迭代对象

更常见的是让容器对象可迭代,而不是让它自己变成迭代器:

class NumberBox:
    def __init__(self, numbers):
        self.numbers = numbers

    def __iter__(self):
        return iter(self.numbers)

这样每次遍历都会返回新的迭代器:

box = NumberBox([1, 2, 3])
list(box)
list(box)

都能得到完整结果。

七、常见误区与追问

记忆钩子:可迭代对象负责“能不能拿到迭代器”,迭代器负责“下一步产出什么”。列表是仓库,迭代器是仓库里的取货游标。

  • 误区:可迭代对象和迭代器是同一个概念。 可迭代对象实现 __iter__ 并返回迭代器;迭代器还要实现 __next__,并记录当前遍历位置。
  • 追问:为什么列表不是迭代器? 列表本身不保存某一次遍历进度,每次 iter(list) 会产生新的列表迭代器;这让同一个列表可以被多次独立遍历。
  • 误区:迭代器可以随便重复遍历。 迭代器通常是一次性游标,消耗完后继续迭代会抛 StopIteration;要重复遍历应重新创建迭代器或保存结果。
  • 追问:for 循环背后发生了什么? 先调用 iter(obj) 获取迭代器,再反复调用 next(iterator),遇到 StopIteration 正常结束循环。
  • 误区:实现 __getitem__ 就一定是标准迭代器。 旧式序列协议可让对象被迭代,但现代自定义对象更推荐明确实现 __iter__,语义更清楚。
  • 追问:生成器对象属于哪一类? 生成器对象本身就是迭代器,既可 iter(g) 返回自身,也可 next(g) 逐步产出值。

八、加强记忆

可迭代对象回答“我能不能被遍历”,迭代器回答“我当前遍历到哪里了”。iter() 拿迭代器,next() 取下一个值,StopIteration 表示结束。列表可迭代但不是迭代器,生成器对象本身就是迭代器,这是面试最常考的对比。