Python迭代器按协议逐个产出元素:可迭代对象提供 iter(),迭代器响应 next(),耗尽时抛出 StopIterationfor 循环内部就是在做这件事。列表、字符串、字典都可迭代。自己的类只要实现 __iter__ / __next__,也能被 for 遍历。

手动取迭代器

iter(序列) 得到迭代器,再反复 next。取完之后继续 next 就会触发 StopIteration

nums = [1, 2, 3]
it = iter(nums)
print(next(it))
print(next(it))
print(next(it))
# print(next(it))   # StopIteration

可迭代与迭代器

  • 可迭代对象:能交给 iter() 并得到迭代器的对象,例如列表、元组、字符串、字典、集合。
  • 迭代器:实现了 __next__(以及通常还有 __iter__)的对象,负责真正吐出下一个值。
print(iter([1, 2]))
print(iter("hi"))
print(iter({"a": 1}))

对字典做 iter 时,默认遍历的是键。要遍历值或键值对,用 iter(d.values()) / iter(d.items())

for 的等价展开

下面这段 whilefor 的机制写清楚了:取迭代器、取下一个、在 StopIteration 处结束:

nums = [10, 20, 30]
it = iter(nums)
while True:
    try:
        val = next(it)
        print(val, end=" ")
    except StopIteration:
        break
print()

自定义可迭代类

__iter__ 返回自身(或另一个迭代器对象),在 __next__ 里推进状态,结束时抛出 StopIteration。下面的倒计时即可被 for 使用:

class Countdown:
    def __init__(self, n):
        self.n = n

    def __iter__(self):
        return self

    def __next__(self):
        if self.n <= 0:
            raise StopIteration
        self.n -= 1
        return self.n + 1

for x in Countdown(3):
    print(x, end=" ")
print()

按需取值

迭代器不必事先造出全部元素。range 就是典型:即便跨度很大,也只在迭代时产出数字,适合直接交给 sum 这类消费方:

r = range(10000000)
print(sum(r))
print(type(r))

常见错误

  • 迭代器一般只能走一遍。再遍历需要重新 iter(...) 或重新构造。
  • 字典默认迭代键,不要默认以为在迭代值。
  • 自定义 __next__ 结束时必须抛 StopIteration,否则 for 不会正常停下。