02 Python语法 (2) 迭代器(Iterator)与生成器(Generator)笔记

一、迭代器(Iterator)与可迭代对象(Iterable)

1. 迭代器的定义

一个对象如果同时实现了:

  1. __iter__() 方法,并返回自身 (return self)
  2. __next__() 方法,用来返回下一个值
  3. 当没有数据时,__next__() 抛出 StopIteration 异常

就叫 迭代器对象

特点

  • 只能往后取值,不能回退
  • 每次取值都会释放之前的值,节省内存
  • 无法直接得知长度(除非额外计算)

2. 可迭代对象的定义

如果一个对象实现了:

  • __iter__() 方法,并且返回一个 迭代器对象

它就是一个可迭代对象

range() is a iterable but NOT Iterator. However, it can produce iterator.

Python 内部会做:

  1. 调用 iter(range(3)) → 得到一个 range_iterator 对象

  2. 这个 range_iterator__next__() 方法,for 会不停调用它,直到遇到 StopIteration 停止循环

列表也是可迭代对象。

所有的可迭代对象,都能通过for循环遍历。


3. 两者的关系

  • 迭代器一定是可迭代对象(因为它有 __iter__ 方法)
  • 可迭代对象不一定是迭代器(可能只有 __iter__ 而没有 __next__

4. for 循环的本质

for x in obj 其实做了三步:

  1. 调用 iter(obj) → 得到迭代器
  2. 不断调用 next(迭代器)
  3. 捕获 StopIteration 异常 → 停止循环

5. 迭代器Pros&Cons

Pros:

  1. 提供了一种通用不依赖index的迭代取值方式
  2. 节省内存,迭代器在内存中只占用一个数据的空间。因为每次取值都上一条数据会在内存释放,加载当前的此条数据。 Cons:
  3. 因为有next函数,只能往后,不能往前
  4. 无法预测迭代器的长度。

二、生成器(Generator)

生成器是一种特殊的迭代器,它是由:

  • 含有 yield 关键字的函数创建
  • 生成器表达式创建

1. 生成器函数

  • 调用时不会立即执行,而是返回一个生成器对象
  • 每次调用 next()send() 时,从上次中断处继续执行,直到遇到下一个 yield
  • yield 会“暂停”函数并返回值
  • return 会结束生成器并抛出 StopIteration

常用方法

  • next(g):执行到下一个 yield
  • send(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 或生成器表达式不行(一次性)很小