多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
# 3.6 【基础】生成器 ## 1. 什么是生成器? 生成器(英文名 Generator ),是一个可以像迭代器那样使用for循环来获取元素的函数。 生成器的出现(Python 2.2 +),实现了延时计算,从而缓解了在大量数据下内存消耗过猛的问题。 当你在 Python Shell 中敲入一个生成器对象,会直接输出 `generator object` 提示你这是一个生成器对象 ```python >>> gen = (i for i in range(5)) >>> gen <generator object <genexpr> at 0x10cae50b0> ``` ## 2. 如何创建生成器? ### 使用列表推导式 在上面已经演示过,正常我们使用列表推导式时是下面这样子,使用 `[]` ,此时生成的是列表。 ```python >>> mylist = [i for i in range(5)] >>> mylist [0, 1, 2, 3, 4] ``` 而当你把 `[]` 换成 `()` ,返回的就不是列表了,而是一个生成器 ```python >>> gen = (i for i in range(5)) >>> gen <generator object <genexpr> at 0x10cae50b0> ``` ### 使用 yield `yield` 是什么东西呢? 它相当于我们函数里的 return,但与 return 又有所不同。 - 当一个函数运行到 yield 后,函数的运行会暂停,并且会把 yield 后的值返回出去。 - 若 yield 没有接任何值,则返回 None - yield 虽然返回了,但是函数并没有结束 请看如下代码,我定义了一个 `generator_factory` 函数,当我执行 `gen = generator_factory()` 时,gen 就是一个生成器对象 ```python >>> def generator_factory(top=5): ... index = 0 ... while index < top: ... print("index 值为: " + str(index)) ... index = index + 1 ... yield index ... raise StopIteration ... >>> gen = generator_factory() >>> gen <generator object generator_factory at 0x1018340b0> ``` ## 3. 生成器的使用 从一个生成器对象中取出元素,和我们前面学过的通过切片访问列表中的元素不一样,它没有那么直观。 想要从生成器对象中取出元素,只有两种方法: **第一种方法**:使用 next 方法一个一个地把元素取出来,如果元素全部取完了,生成器会抛出 `StopIteration` 的异常。 ```python >>> gen = (x for x in range(3)) >>> gen <generator object <genexpr> at 0x1072400b0> >>> next(gen) 0 >>> next(gen) 1 >>> next(gen) 2 >>> next(gen) Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration ``` **第二种方法**:使用 for 循环一个一个地迭代出来 ```python >>> gen = (x for x in range(3)) >>> for i in gen: ... print(i) ... 0 1 2 ``` ## 4. 生成器的激活 生成器对象,在创建后,并不会执行任何的代码逻辑。 想要从生成器对象中获取元素,那么第一步要触发其运行,在这里称之为激活。 方法有两种: 1. 使用`next()` :上面已经讲过 2. 使用`generator.send(None)` 还以下面这段代码为例,可以看到 `gen.send(None)` 相当于执行了 `next(gen)` ```python >>> def generator_factory(top=5): ... index = 0 ... while index < top: ... print("index 值为: " + str(index)) ... index = index + 1 ... yield index ... raise StopIteration ... >>> >>> gen = generator_factory() >>> gen.send(None) index 值为: 0 1 >>> gen.send(None) index 值为: 1 2 ``` ## 5. 生成器的状态 生成器在其生命周期中,会有如下四个状态 - `GEN_CREATED` # 生成器已创建,还未被激活 - `GEN_RUNNING` # 解释器正在执行(只有在多线程应用中才能看到这个状态) - `GEN_SUSPENDED` # 在 yield 表达式处暂停 - `GEN_CLOSED` # 生成器执行结束 通过下面的示例可以很轻松地理解这一过程(`GEN_RUNNING` 这个状态只有在多线程中才能观察到,这里就不演示啦) ```python >>> gen = (x for x in range(2)) >>> from inspect import getgeneratorstate >>> gen = (x for x in range(3)) >>> getgeneratorstate(gen) 'GEN_CREATED' >>> >>> next(gen) 0 >>> getgeneratorstate(gen) 'GEN_SUSPENDED' >>> next(gen) 1 >>> next(gen) Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration >>> getgeneratorstate(gen) 'GEN_CLOSED' ``` ## 6. 生成器的异常 在最前面,我有定义了一个生成器函数。 ```python def generator_factory(top=2): index = 0 while index < top: index = index + 1 yield index raise StopIteration ``` 在没有元素可返回时,我最后抛出了 `StopIteration` 异常,这是为了满足生成器的协议。 实际上,如果你不手动抛出 `StopIteration`,在生成器遇到函数 return 时,会我自动抛出 `StopIteration`。 请看下面代码,我将 `raise StopIteration` 去掉后,仍然会抛出异常。 ```python >>> def generator_factory(top=2): ... index = 0 ... while index < top: ... index = index + 1 ... yield index ... >>> gen = generator_factory() >>> next(gen) 1 >>> next(gen) 2 >>> next(gen) Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration ```