python生成器讲解1什么是生成器将列表生成式的[]改成()用 yield 创建生成器yield的执行流程

什么是生成器

我们可以使用列表生成式很方便地创建一个列表,如以下代码:

In [1]: l = [ x*2 for x in range(5) ]

In [2]: l
Out[2]: [0, 2, 4, 6, 8]

如果要创建的是一个1000万个元素的列表呢?不可能使用以上的方式,即使你的电脑性能强劲,内存足够用,也不是这么用来浪费的。好比,你不可能用一个大桶来装一茶杯的水。

怎样才能满足既能实现我们的需求,又不占用大量的内存?如果储存的只是生成列表的算法,而不是具体的值,就可以实现了。

这种存储算法的数据结构就称为生成器。

创建生成器有以下几种方法

将列表生成式的[]改成()

In [3]: l = ( x*2 for x in range(5) )

In [4]: l
Out[4]: <generator object <genexpr> at 0x103ca38b8> # 生成器

打印生成器看到的只是数据类型,而不是具体的值。需要使用next()函数获得生成器的下一个返回值

In [5]: next(l)
Out[5]: 0

In [6]: next(l)
Out[6]: 2

In [7]: l.__next__()
Out[7]: 4

next() 与 生成器的 next() 方法作用相同

如果 next() 超出了生成器的数据范畴,会怎样?

In [9]: next(l)
Out[9]: 6

In [10]: next(l)
Out[10]: 8

In [11]: next(l)
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-11-cdc8a39da60d> in <module>()
----> 1 next(l)

StopIteration:

当超出生成器的计算范畴,会抛出 StopIteration 异常

如果生成器的数据是用 next() 一个个调用,那会让人无比烦躁,而且还得谨防 StopIteration 异常。实际开发中,我们是用 for 去循环调用生成器

In [15]: l = ( x*2 for x in range(5) )

In [16]: for x in l:
    ...:     print(x)
    ...:
0
2
4
6
8

用 yield 创建生成器

生成斐波拉契函数

In [18]: def fib(times):
    ...:     n = 0
    ...:     a, b = 0, 1
    ...:     while n < times:
    ...:         print(b)
    ...:         a, b = b, a+b
    ...:         n += 1
    ...:     return 'done'
    ...:
    ...:

In [19]: fib(5)
1
1
2
3
5
Out[19]: 'done'

In [20]: def fib(times):
    ...:     n = 0
    ...:     a, b = 0, 1
    ...:     while n < times:
    ...:         yield(b) # 将print改为yield, 函数就成为了生成器
    ...:         a, b = b, a+b
    ...:         n += 1
    ...:     return 'done'
    ...:
    ...:

In [21]: F = fib(5)

In [22]: next(F)
Out[22]: 1

In [23]: next(F)
Out[23]: 1

In [24]: next(F)
Out[24]: 2

In [25]: next(F)
Out[25]: 3

In [26]: next(F)
Out[26]: 5

In [27]: next(F)
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-27-372178f5f53b> in <module>()
----> 1 next(F)

StopIteration: done

In [28]: F
Out[28]: <generator object fib at 0x103ca3570> # 生成器

使用for循环迭代生成器

In [7]: for x in fib(5):
   ...:     print(x)
   ...:
1
1
2
3
5

for循环调用能将结果输出,可是并没有捕获到fib()的return值。返回值其实是包含在StopIteration的value中,因此必须捕获StopIteration异常

In [8]: F = fib(5)

In [9]: while True:
   ...:     try:
   ...:         x = next(F)
   ...:         print(x)
   ...:     except StopIteration as e:
   ...:         print('返回值是:%s' %e.value)
   ...:         break
   ...:
1
1
2
3
5
返回值是:done

yield的执行流程

In [20]: def fib(times):
    ...:     n = 0
    ...:     a, b = 0, 1
    ...:     while n < times:
    ...:         print('%s_before_yield' %n)
    ...:         yield(b) # 将print改为yield, 函数就成为了生成器
    ...:         print('%s_after_yield' %n)
    ...:         a, b = b, a+b
    ...:         n += 1
    ...:     return 'done'
    ...:
    ...:

In [21]: F = fib(5)

In [22]: next(F)
0_before_yield 
Out[22]: 1 # 执行到yield, 程序停住,不再往下执行,并记住当前的执行位置

In [23]: next(F)
0_after_yield
1_before_yield
Out[23]: 1 # 从上一处的停顿往下继续执行,同样到yield处继续停住

In [24]: next(F)
1_after_yield
2_before_yield
Out[24]: 2

In [25]: next(F)
2_after_yield
3_before_yield
Out[25]: 3

In [26]: next(F)
3_after_yield
4_before_yield
Out[26]: 5

In [27]: next(F) # 超出生成器的范围报错
4_after_yield
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-27-372178f5f53b> in <module>()
----> 1 next(F)

StopIteration: done

python

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏前端小栈

正则详解

转自: JS正则表达式一条龙讲解,从原理和语法到JS正则、ES6正则扩展,最后再到正则实践思路

1363
来自专栏nummy

ECMAScript5 Object的新属性方法

虽然说现在并不是所有的浏览器都已经支持ECMAScript5的新特性,但相比于ECMAScript4而言ECMAScript5被广大浏览器厂商广泛接受,目前主流...

1174
来自专栏chenjx85的技术专栏

leetcode-179-Largest Number(理解规则,自定义cmp函数进行排序)

1、这道题给定一个vector,里面存放着int类型的非负整数,要求把这些非负整数拼起来,尽可能拼成一个最大的整数。

1913
来自专栏老付的网络博客

Java中lambda表达式详解

上面的代码中,e是一个lambda的对象,根据java的继承的特性,我们可以说e对象的类型是继承自eat接口。而e1是一个正常的匿名类的对象.

5774
来自专栏androidBlog

Java 反射机制详解

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/gdutxiaoxu/article/details/...

901
来自专栏趣谈编程

Unicode与UTF-8的区别

要弄清Unicode与UTF-8的关系,我们还得从他们的来源说起,下来我们从刚开始的编码说起,直到Unicode的出现,我们就会感觉到他们之间的关系

1322
来自专栏coding for love

JS入门难点解析10-创建对象

(注1:如果有问题欢迎留言探讨,一起学习!转载请注明出处,喜欢可以点个赞哦!) (注2:更多内容请查看我的目录。)

1243
来自专栏HansBug's Lab

1798: [Ahoi2009]Seq 维护序列seq

1798: [Ahoi2009]Seq 维护序列seq Time Limit: 30 Sec  Memory Limit: 64 MB Submit: 2930...

3065
来自专栏ImportSource

如何更好的定义枚举

枚举虽然是一个比较基础的东西。但如果你能在具体的开发中,更优雅的定义枚举的话会让你的代码看起来清新脱俗,本文将介绍枚举的各种用法,特别后面的通过构造函数传参以后...

3419
来自专栏偏前端工程师的驿站

JS魔法堂:再识Number type

Brief                                   本来只打算理解JS中0.1 + 0.2 == 0.300000000000000...

2295

扫码关注云+社区

领取腾讯云代金券