# 假设要处理 100 万个数字 nums = [x * 2 for x in range(1000000)]
结果:Python 会在这一瞬间,在内存中实打实地开辟一块空间,把这 100 万个数字全部算出来存进去。如果数字再大一点,内存直接爆掉(MemoryError)。
2、生成器(按需取值)
nums_gen = (x * 2 for x in range(1000000))
结果:这行代码瞬间完成,且几乎不占内存。因为它根本没开始算!只有当你调用 next(nums_gen) 或者在 for 循环中遍历它时,它才会临时算出第一个数字给你,算完就忘,等你要下一个时再算下一个。
3、迭代器(按需取值)
# 比如内置的 range(1000000) 本身就是一个迭代器 nums_iter = iter(range(1000000))
结果:同样瞬间完成,不占内存。它内部只保存了一个状态(比如“当前到了第几个”),每次你调用 next(nums_iter),它才根据状态去算出下一个值。
总结它们的共同点:
nums[0] 去查看)。1. 迭代器:纯手工打造的“定制煎饼摊”
__iter__ 和 __next__ 方法)。StopIteration 异常。2. 生成器:全自动的“智能煎饼机”
yield 关键字或生成器表达式)。__iter__ 和 __next__ 方法,并且自动帮你保存了函数的执行状态(局部变量、执行到哪一行了)。直观对比:同一个需求,两种写法
class SquareIterator: def __init__(self, max_num): self.current = 0 # ← 手动定义状态变量 self.max = max_num # ← 手动定义状态变量 def __next__(self): if self.current < self.max: # ← 手动判断"该不该停止" num = self.current ** 2 # ← 手动计算当前值 self.current += 1 # ← 手动更新状态(最关键的一行!) return num else: raise StopIteration # ← 手动抛出停止异常
核心就在这一行:
__next__() 时,你必须自己记住"上次走到哪了",然后自己推进到下一步。如果忘了写这行,迭代器就会永远返回同一个值,陷入死循环。def square_generator(max_num): current = 0 while current < max_num: yield current ** 2 # 自动保存状态,自动处理停止 current += 1 (甚至更简单:square_gen = (x**2 for x in range(5)))
self.current += 1 写在 __next__() 里,每次外部调用 next() 时,Python 从头执行这个方法,你必须自己管理"走到哪了"。current += 1 写在普通函数体里,当执行到 yield 时,Python 自动暂停并保存整个函数的执行现场(包括 current 的值、执行到了哪一行、所有局部变量)。下次调用 next() 时,Python 自动恢复到暂停的位置继续往下执行。| 迭代器 | 生成器 | |
|---|---|---|
| 状态存在哪 | 你自己用 self.xxx 存 |
Python 自动帮你存 |
| 状态怎么推进 | 你手动写 self.current += 1 |
Python 自动从 yield 之后继续执行 |
| 停止条件 | 你手动写 raise StopIteration |
Python 自动在函数 return 或结束时抛出 |
| 恢复执行 | 每次从头进 __next__ |
自动从上次暂停的地方继续 |
current += 1,而是说生成器帮你省掉了"保存和恢复执行现场"这整套最繁琐的底层工作。你写的 current += 1 只是普通的业务逻辑,而迭代器里的 self.current += 1 是在兼任状态管理器的职责。
yield和return是有区别的,yield在生成一个元素后,会记住迭代的位置并将当前的状态挂起(还记住了其它一些必要的东西),等到下一次需要元素的时候再从这里继续yield一个元素,直到所有的元素都被yield完(也可能永远yield不完)。return则是直接退出函数
总结:到底用哪个?
1. 从“使用者”的角度:确实差不多
# 用迭代器 it = iter([1, 2, 3]) next(it) # 拿一个 # 用生成器 gen = (x for x in [1, 2, 3]) next(gen) # 拿一个
2. 从“制造者”的角度:区别极其明显
如果用【迭代器】(手写状态):
class MyIter: def __init__(self): self.count = 0 # 1. 你自己建个变量记状态 def __next__(self): if self.count >= 3: # 2. 你自己判断什么时候该停 raise StopIteration # 3. 你自己抛出停止信号 self.count += 1 # 4. 你自己更新状态 return self.count # 5. 你自己返回数据
如果用【生成器】(Python 托管状态):
def my_gen(): count = 0 while count < 3: count += 1 yield count # 就这一句!Python 自动帮你保存 count,自动处理停止
爽点:你根本不需要管什么“状态保存”、“停止信号”,Python 解释器全包了。