1 生成器定义

在Python中,一边循环一边计算的机制,称之为生成器(generator)。

生成器是一个迭代器。

含有yield语句的函数是生成器函数,该函数被调用时返回一个生成器对象(yield译为产生或生成)。

生成器拥有next方法并且行为与迭代器完全相同,这意味着生成器也可以用于Python的for循环中。

另外,对于生成器的特殊语法支持使得编写一个生成器比自定义一个常规的迭代器要简单不少,所以生成器也是最常用到的特性之一。

简而言之:

yield用于def函数中,目的是将此函数作为生成器函数使用

yield用来生成数据,供迭代器的next(Iterator) 函数使用

2 生成器分类

生成器有两种,一种生成器函数,一种是生成器表达式(列表推导式

3 使用生成器函数定义生成器

>>> def gen():
...     print("第一次输出")
...     yield 1
...     print("第二次输出")
...     yield 2
...     print("第三次输出")
...     yield 3
...
>>> gen()
<generator object gen at 0x7f23f7f9a780>
 

定义了一个函数类型gen(),但该函数内使用了关键字yield,使得这个函数成为了生成器函数。

1) 调用生成器函数将返回一个生成器

>>> gen1 = gen()
>>> gen1
<generator object gen at 0x7f23f7f9a830>

2) 第一次调用生成器的next()方法时,生成器才开始执行生成器函数(而不是构建生成器时),直到遇到yield时暂停执行(挂起),并且yield的参数将作为此次next方法的返回值;

>>> g1 = gen()
>>> next(gen())
第一次输出
1
>>> next(gen())
第一次输出
1

>>> next(g1)
第一次输出
1
>>> next(g1)
第二次输出
2

>>> g2 = g1
>>> next(g2)
第三次输出
3

注意:

(1)gen()是一个生成器函数,每一次调用,它都会重新执行,同时该函数运行到yeild()时会暂停执行,所以两次执行(函数调用)均输出相同的值。

(2)而g1 、g2是调用函数gen()返回的生成器,为了说明问题,举例如下:

说明g1 和 g2 绑定的一个生成器对象,所以在执行next()时会顺次执行。

3)之后每次调用生成器的next方法,生成器将从上次暂停执行的位置恢复执行生成器对象,直到再次遇到yield时暂停,并且同样的,yield的参数将作为next方法的返回值;

4)如果当调用next方法时生成器函数结束(遇到空的return语句或是到达函数体末尾),则这次next方法的调用将抛出StopIteration异常(即for循环的终止条件);

5)生成器函数在每次暂停执行时,函数体内的所有变量都将被封存(freeze)在生成器中,并将在恢复执行时还原,并且类似于闭包,即使是同一个生成器函数返回的生成器,封存的变量也是互相独立的。

我们的小例子中并没有用到变量,所以这里另外定义一个生成器来展示这个特点:

>>> def fibonacci():
...     a = b = 1
...     yield a
...     yield b
...     while True:
...             a, b = b, a+b
...             yield b
...
>>> for num in fibonacci():
...     if num > 100:
...             break
...     print(num)
...
1
1
2
3
5
8
13
21
34
55
89

或者

>>> def fibonacci():
...     a = b = 1
...     yield a
...     yield b
...     while True:
...             a, b = b, a+b
...             yield b
...
>>> next(fibonacci())
1
>>> next(fibonacci())
1
>>> next(fibonacci())
1
>>> f = fibonacci()
>>> next(f)
1
>>> next(f)
1
>>> next(f)
2
>>> next(f)
3
>>> next(f)
5
>>> next(f)
8
>>> next(f)
13
>>> next(f)
21
>>> next(f)
34
>>> next(f)
55
>>> next(f)
89
>>> next(f)
144
>>> next(f)
233
>>> next(f)
377
>>> next(f)
610
>>> next(f)
987

看到while True可别太吃惊,因为生成器可以挂起,所以是延迟计算的,无限循环并没有关系。这个例子中我们定义了一个生成器用于获取斐波那契数列。

4 一种是生成器表达式

生成器表达式(采用的是小括号“()”形式)类似于列表推导式(采用的是小括号“[ ]”形式),生成器的返回值是按每次next()调用产生一个对象,需要的存储空间较小,而列表推导式则是一次性构建一个列表库,需要的存储空间较大。

#列表表达式,生成一个元素库
>>> a = [x for x in range(1,10)]
>>> a
[1, 2, 3, 4, 5, 6, 7, 8, 9]

#生成器
>>> b = (x for x in range(1,10))
>>> b
<generator object <genexpr> at 0x7fb2bf6f24c0>

生成器表达式与列表推导式区别

>>> sum([i for i in range(100000000)])
>>>4999999950000000

>>> sum((i for i in range(100000000)))
>>>4999999950000000

虽然结果都能算出来两者之间,但是列表推导式耗时长、易卡顿,而生成器表达式计算速度快、不卡顿。

详细可参考 如何更好地理解Python迭代器和生成器?知乎

5 生成器函数的FAQ

1) 生成器函数可以带参数(带形参)吗?

生成器函数也是函数的一种,所以可以带参数

>>> def counter(start=0):
...   while True:
...     yield start
...     start += 1
...

这是一个从指定数开始的计数器。

2)生成器函数中可以有return 语句吗?

不可以,因为生成器函数已有默认了返回值——生成器,所以不能重新用return返回值(即使return None也不行),如果使用return返回值则会抛出语法错误异常。

>>> def gen():
...     print("输出1")
...     yield 1
...     print("输出2")
...     return "return语句"
...     yield 2
...     print("输出2")
...
>>> g = gen()
>>> next(g)
输出1
1
>>> next(g)
输出2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration: return语句
>>> next(g)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>> next(g)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

参考

Python函数式编程指南(四):生成器

深入理解Python中的生成器

补充

1 沿着某条路线依次访问,且只访问一次,该种现象成为遍历

2 迭代器的访问是一次性的,指针向下移动时,不会后退;也即一次性调用。

3 生成器也是迭代器

def gen_ite():
    yield 1

it = gen_ite()
a = iter(it)

print(id(it))
print(id(a))

4 列表与生成器之间的关系

#生成一个列表库
>>> c = list(range(10))
>>> c
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

#将一个新变量进行绑定一个新生成的序列
>>> c1 = [x for x in c]
>>> c1
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

#将一个新变量绑定生成器
>>> c2 = (x for x in c)
>>> c2
<generator object <genexpr> at 0x7fb2bf6f2518>
#生成器进行序列化
>>> list(c2)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

#改变原变量c中键值为1的值
>>> c[1] = 100
#c1为新序列绑定的变量,改变原变量值对其没有影响
>>> c1
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
#c2其实是生成器中的第二种(列表推导式方式)产生生成器的方式(第一种是yield语句)。
#同时生成器返回按需产生结果的一个对象,而不是一次构建一个结果列表

#这里c2为空列表的主要原因是指针后移造成的
>>> c2
<generator object <genexpr> at 0x7fb2bf6f2518>
>>> list(c2)
[]
 

下段代码阐述生成器函数是按需产生结果

>>> c = list(range(10))
>>> c2 = (x for x in c)

>>> c
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

>>> c[1] = 100
>>> c
[0, 100, 2, 3, 4, 5, 6, 7, 8, 9]

>>> next(c2)
0

>>> c
[0, 100, 2, 100, 4, 5, 6, 7, 8, 9]

#生成器随着原数据库变化而变化
>>> next(c2)
100
 
 
 
 

Python学习笔记014——生成器Generator的更多相关文章

  1. Python学习笔记之生成器、迭代器和装饰器

    这篇文章主要介绍 Python 中几个常用的高级特性,用好这几个特性可以让自己的代码更加 Pythonnic 哦 1.生成器 什么是生成器呢?简单来说,在 Python 中一边循环一边计算的机制称为 ...

  2. Python学习笔记014——迭代工具函数 内置函数enumerate()

    1 描述 enumerate() 函数用于将一个可遍历的数据对象(如列表.元组或字符串)组合为一个索引序列,同时列出数据和数据下标,一般用在 for 循环当中. 2 语法 enumerate(sequ ...

  3. python学习笔记014——错误和异常

    Python有两种错误很容易辨认:语法错误和异常. 1 什么是语法错误 Python 的语法错误或者称之为解析错,是初学者经常碰到的,如下实例 if i>4 print("if语句输出 ...

  4. python学习笔记(四):生成器、内置函数、json

    一.生成器 生成器是什么?其实和list差不多,只不过list生成的时候数据已经在内存里面了,而生成器中生成的数据是当被调用时才生成呢,这样就节省了内存空间. 1. 列表生成式,在第二篇博客里面我写了 ...

  5. Python学习笔记014——迭代器 Iterator

    1 迭代器的定义 凡是能被next()函数调用并不断返回一个值的对象均称之为迭代器(Iterator) 2 迭代器的说明 Python中的Iterator对象表示的是一个数据流,被函数next()函数 ...

  6. Python学习笔记014——迭代工具函数 内置函数zip()

    1 描述 zip() 函数用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的列表. 如果各个迭代器的元素个数不一致,则返回列表长度与最短的对象相同,利用 * 号操 ...

  7. Python学习笔记(十一)

    Python学习笔记(十一): 生成器,迭代器回顾 模块 作业-计算器 1. 生成器,迭代器回顾 1. 列表生成式:[x for x in range(10)] 2. 生成器 (generator o ...

  8. Deep learning with Python 学习笔记(10)

    生成式深度学习 机器学习模型能够对图像.音乐和故事的统计潜在空间(latent space)进行学习,然后从这个空间中采样(sample),创造出与模型在训练数据中所见到的艺术作品具有相似特征的新作品 ...

  9. Deep learning with Python 学习笔记(6)

    本节介绍循环神经网络及其优化 循环神经网络(RNN,recurrent neural network)处理序列的方式是,遍历所有序列元素,并保存一个状态(state),其中包含与已查看内容相关的信息. ...

随机推荐

  1. jQuery多文件下载

    文件下载是一个Web中非常常用的功能,不过你是做内部管理系统还是做面向公众的互联网公司都会遇到这个问题,对于下载一般有点实际开发经验的都会自己解决,上周弄了一下多文件下载,业务场景就是一条数据详细信息 ...

  2. Bootstrap学习js插件篇之滚动监听

    1.滚动监听 案例 滚动监听插件可以根据滚动条的位置自动更新所对应的导航标记.Bootstrap中文网左侧就是一个滚动监听的例子. 代码段: <nav id="navbar-examp ...

  3. Android实现图片轮显效果——自定义ViewPager控件

    一.问题概述 使用ViewPager控件实现可横向翻页.水平切换图片等效果,但ViewPager需要手动滑动才能切换页面,图片轮显效果的效果本质上就是在ViewPager控件的基础上让它能自动的进行切 ...

  4. Godaddy ssl续费更新问题总结

    之前客户在Godaddy 上购买的ssl证书过期了,但客户续费后打开https时却提示证书过期了 进行Godaddy 后台看到证书确实是过期的 但在账户里也确实看到ssl续费成功了 猜想可能是ssl续 ...

  5. 老三星手机i9001刷机记录

    家里的老的三星i9001,准备给我妈用,打算刷机,但又实在头疼那些复杂的刷机技术,昨天研究了一下,用比较简单的方法完成刷机,记录如下: 用卡刷比较简单,线刷不考虑 进入恢复模式的方法:1.电源+音量加 ...

  6. (LeetCode 203)Remove Linked List Elements

    Remove all elements from a linked list of integers that have value val. ExampleGiven: 1 --> 2 --& ...

  7. 在自己的服务器上部署 GitLab 社区版

    GitLab 简介 因为我的个人网站 restran.net 已经启用,博客园的内容已经不再更新.这篇文章是在 Gitlab 7.4 的环境下配置的,相关内容可能已经过时. 后续做了一次迁移,将 Gi ...

  8. php之快速入门学习-15(php函数)

    PHP 函数 PHP 的真正威力源自于它的函数. 在 PHP 中,提供了超过 1000 个内建的函数. PHP 内建函数 如需查看所有数组函数的完整参考手册和实例,请访问我们的 PHP 参考手册. P ...

  9. mkdir命令(转)

    原文:http://www.cnblogs.com/peida/archive/2012/10/25/2738271.html linux mkdir 命令用来创建指定的名称的目录,要求创建目录的用户 ...

  10. MySQL 导出函数与存储过程

    C:\Users\yan>mysqldump -u用户 -p -n -t -d -R 数据库 > .sql Enter password: ******** C:\Users\yan> ...