02 Python语法 (2) 迭代器(Iterator)与生成器(Generator)笔记
一、迭代器(Iterator)与可迭代对象(Iterable)
1. 迭代器的定义
一个对象如果同时实现了:
__iter__()方法,并返回自身 (return self)__next__()方法,用来返回下一个值- 当没有数据时,
__next__()抛出StopIteration异常
就叫 迭代器对象。
特点:
- 只能往后取值,不能回退
- 每次取值都会释放之前的值,节省内存
- 无法直接得知长度(除非额外计算)
2. 可迭代对象的定义
如果一个对象实现了:
__iter__()方法,并且返回一个 迭代器对象
它就是一个可迭代对象。
range() is a iterable but NOT Iterator. However, it can produce iterator.
Python 内部会做:
-
调用
iter(range(3))→ 得到一个range_iterator对象 -
这个
range_iterator有__next__()方法,for会不停调用它,直到遇到StopIteration停止循环
列表也是可迭代对象。
所有的可迭代对象,都能通过for循环遍历。
3. 两者的关系
- 迭代器一定是可迭代对象(因为它有
__iter__方法) - 但可迭代对象不一定是迭代器(可能只有
__iter__而没有__next__)
4. for 循环的本质
for x in obj 其实做了三步:
- 调用
iter(obj)→ 得到迭代器 - 不断调用
next(迭代器) - 捕获
StopIteration异常 → 停止循环
5. 迭代器Pros&Cons
Pros:
- 提供了一种通用不依赖index的迭代取值方式
- 节省内存,迭代器在内存中只占用一个数据的空间。因为每次取值都上一条数据会在内存释放,加载当前的此条数据。 Cons:
- 因为有next函数,只能往后,不能往前
- 无法预测迭代器的长度。
二、生成器(Generator)
生成器是一种特殊的迭代器,它是由:
- 含有
yield关键字的函数创建 - 或生成器表达式创建
1. 生成器函数
- 调用时不会立即执行,而是返回一个生成器对象
- 每次调用
next()或send()时,从上次中断处继续执行,直到遇到下一个yield yield会“暂停”函数并返回值return会结束生成器并抛出StopIteration
常用方法:
next(g):执行到下一个yieldsend(value):不仅执行到下一个yield,还把value传给上一个yield表达式close():关闭生成器。生成器被关闭后,再次调用next()函数,不论是否有yield,都会抛出StopIteration异常
def generator():
v1 = yield 1
v2 = yield 2
v3 = yield 3
g = generator()
Explanation:
- 首先需要用 next(g)到达第一个yield。这时候print 1
- print(g.send(100)) 让 v1 = 100, 同时到达第二个yield,print 2
2. 生成器表达式
语法:
(返回值 for 元素 in 可迭代对象 if 条件)
返回一个生成器对象(惰性求值,不立即生成全部数据)。
示例:
numbers = (x for x in range(6))
列表推导式的区别:
-
列表推导式:一次性生成全部数据(占内存多)
-
生成器表达式:按需生成(占内存少)
三、总结对比表
| 对象类型 | 必须方法 | 是否惰性计算 | 是否能重复迭代 | 内存占用 |
|---|---|---|---|---|
| 可迭代对象 | __iter__() | 否 | 可以 | 取决于实现 |
| 迭代器 | __iter__() + __next__() | 是 | 不行(一次性) | 很小 |
| 生成器 | yield 或生成器表达式 | 是 | 不行(一次性) | 很小 |