Python 生成器和迭代器的区别

FreeGuideOnline 最新 2026-07-05

python

自定义一个简单的迭代器类

class CountDown: def init(self, start): self.current = start

def __iter__(self):
    return self

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

使用迭代器

counter = CountDown(3) for num in counter: print(num) # 输出 3 2 1 0


常见的可迭代对象(如列表、元组)并不是迭代器,但可以通过 `iter()` 函数获得它们的迭代器。

```python
lst = [1, 2, 3]
iterator = iter(lst)
print(next(iterator))  # 1
print(next(iterator))  # 2

什么是生成器(Generator)

生成器是一种特殊的迭代器,使用起来更简洁。Python 提供两种创建生成器的方式:

  1. 生成器函数:使用 yield 关键字代替 return
  2. 生成器表达式:类似列表推导式,但使用圆括号。

生成器同样惰性求值,每次调用 next()(或在 for 循环中)会执行到下一个 yield 语句并暂停,保存当前状态,下次调用时从暂停处继续执行。

# 生成器函数
def countdown_gen(n):
    while n >= 0:
        yield n
        n -= 1

# 生成器表达式
squares = (x * x for x in range(5))

# 使用生成器
cd = countdown_gen(3)
for num in cd:
    print(num)  # 输出 3 2 1 0

print(list(squares))  # [0, 1, 4, 9, 16]

生成器自动实现了 __iter__()__next__() 方法,因此它就是一个迭代器。可以用 isinstance 验证:

from collections.abc import Generator, Iterator
gen = (x for x in range(3))
print(isinstance(gen, Generator))  # True
print(isinstance(gen, Iterator))   # True

生成器和迭代器的关键区别

对比维度 迭代器 (Iterator) 生成器 (Generator)
创建方式 实现 __iter____next__ 的类 使用 yield 的函数或生成器表达式
代码量 需要明确维护状态(如 self.current 函数内局部变量自动保存,代码简洁
内存占用 由开发者控制,通常也是惰性求值 天然惰性求值,只在需要时产生一个值
可重用性 取决于实现,一般迭代器只能遍历一次 每个生成器对象只能遍历一次
状态管理 手动管理索引或状态变量 yield 暂停时自动保存堆栈帧状态
功能完整性 可扩展其他方法,灵活性更高 功能单一,专为惰性生成序列设计
典型用途 自定义复杂遍历逻辑,读取文件、数据库游标等 处理大数据流、无限序列、管道式数据处理

内存开销与执行模型

迭代器完全可以预先存储所有数据(虽然不推荐),但生成器强制惰性求值。下面的例子对比了列表(可迭代但非迭代器)、普通迭代器与生成器的内存占用:

import sys

# 列表:一次性分配全部内存
lst = [i for i in range(1000000)]
print(sys.getsizeof(lst))  # 约 800 万字节

# 迭代器:依然可以基于已存在的数据,内存占用取决于底层数据结构
it = iter(lst)  # 本身只是引用列表,额外内存很小

# 生成器表达式:几乎不占额外内存,按需计算
gen = (i for i in range(1000000))
print(sys.getsizeof(gen))  # 约 112 字节(与元素数量无关)

生成器函数也是同样,只有在执行到 yield 时才计算一个值,前面的局部变量暂时保留。

可重用性与单向性

大部分迭代器只能被遍历一次,因为 StopIteration 发生后,再次 next() 会直接抛出异常,除非在 __iter__ 中重新初始化状态。生成器同样一次性,遍历结束后无法重置。

gen = countdown_gen(2)
print(list(gen))  # [2, 1, 0]
print(list(gen))  # [] 因为已经耗尽

如果需要多次迭代,应该每次调用生成器函数返回一个新的生成器对象:

def gen_func():
    yield 1
    yield 2

for i in gen_func():
    print(i)  # 1 2
for i in gen_func():
    print(i)  # 1 2 (新对象,重新开始)

异常和高级特性

生成器除了 yield 产生值,还可以使用 send() 方法向生成器内部传递值,以及 throw()close() 方法控制生成器行为。这是普通迭代器所没有的。

def receiver():
    while True:
        val = yield
        print(f"Received: {val}")

gen = receiver()
next(gen)       # 预激生成器
gen.send(10)    # Received: 10
gen.send(20)    # Received: 20
gen.close()

这使得生成器不仅可以产出数据,还可以用作协程。

何时使用生成器或迭代器

  • 当需要定义一个简单的惰性序列,或者对大型数据集进行逐项处理时,首选生成器。代码更清晰,内存效率极高。
  • 如果需要封装复杂的状态逻辑,或者需要提供额外的辅助方法(比如 current()reset() 等),或者需要维护多个相互关联的状态变量,自定义迭代器类会更合适。
  • 在数据处理管道中,生成器可以像管道组件一样串联,利用生成器表达式实现流式处理:
nums = (x for x in range(100))
doubled = (x * 2 for x in nums)
filtered = (x for x in doubled if x > 50)
# 直到 list(filtered) 才真正开始计算
print(list(filtered))