大家好,欢迎来到IT知识分享网。
什么是生成器?
一、生成式 —— 一种调函数的方式
如果忽略开水、价钱,买方便面和泡方便面有何差别?买方便面可能一次买一箱,不是吃多少卖多少,但泡方便面则不会一次泡一箱,时,肯定是吃多少泡多少。
生成式给了我们另一种选择,即两个函数的交替执行。它像是泡方便面,调一次函数拿到一项数据,不用想办法存储所有的数据,体现了延迟演算的思想,而代码却不用有多少变化。
生成式有三种形式:元组式、yield 语句、yield表达式。
二、元组推导式 —— 延迟演算
列表式很常见,它根据一组数据计算出一组新的数据,是对赋值语句的一种改进。下列代码将得到列表 [1,3,2,5] 中各元素的平方构成的新列表。
a = [k2 for k in [1,3,2,5]] print(a)
运行结果
[1, 9, 4, 25]
又一个例子,它能显示计算过程,注意这里没有执行 print,但依旧有结果被打印出来,说明系统立即计算了新列表的所有元素。
a = [(i,print(i))[0] for i in [1,3,2,5]]
运行结果
1 3 2 5
元组也能根据一组数据计算出一组新的数据,但有着不同的中间过程。代码做了修改,将方括号改为圆括号,结果有所不同。
a = ((i,print(i))[0] for i in [1,3,2,5])
运行结果是空的,咦,…
这表明,元组式并没有生成它自己的元素,一个都没有,这就是延迟演算。
a = ((i,print(i))[0] for i in [1,3,2,5]) i = 0 for _ in a: if i > 1: break i += 1
运行结果如下。上面的代码有一个循环,for 执行了 3 遍后结束循环,相应地从 a 中取了 3 个元素,导致 print 被调用 3 次,所以将打印三项数据。
1 3 2
这就是生成式的第一种形式,上面的例子体现了列表式的立即演算和生成式的延迟演算特性。
三、yield 语句 —— 交替执行
def g(): for i in range(3): print(f'\tin g({
i})') def f(): g() for i in range(3): print(f'in f({
i})') f()
运行结果如下。当 g 在工作时,f 在等待;当 f 继续工作时,g 已经结束了,呈现出顺序结构的工作模式。
in g(0) in g(1) in g(2) in f(0) in f(1) in f(2)
g 在函数中执行 yield,可做到暂时离开执行中的 g 回到 f,待 f 执行 next 时又回到 g 中,实现 f 与 g交替执行。
def g(): for i in range(3): print(f'\t\t\tg(),i={
i}') yield i def f(): a = g() print(f'f()') for j in range(3): b = next(a) print(f'f(),j={
j},b={
b}') f()
运行结果如下。代码中,f 首先执行 a = g() 创建一个生成器,执行到 next(a) 语句时暂停,g 开始执行直到 yield 语句,并将 yield 后的表达式的值传递给 b = next(a) 中等号左边的 b,g 暂停,f 继续执行直到下一个 next(a) 或者结束。
f() g(),i=0 f(),j=0,b=0 g(),i=1 f(),j=1,b=1 g(),i=2 f(),j=2,b=2
四、yield 表达式 —— 双向传递数据
表达式可以出现在等号右边。使用 yield 表达式可实现 f 与 g 双向数据传送。f 执行 b = a.send©,g 执行 y = yield x。f 执行 b = a.send© 将 c 传给 y = yield x 中的 y 后等待 b,并将控制传递给 g,g 执行 y = yield x 将 x 传递给 f 的 b 后等待 y,并将控制交还给 f。
def g(x): for i in range(10): y = yield x print('\t', y) x = y def f(): a = g(11) b = a.send(None) print (b) b = a.send(b+1) print (b) b = a.send(b+1) print (b) f()
运行结果
11 12 12 13 13
五、计算斐波那契数列 —— 生成无限的数据
生成器有什么用呢?看看下面的例子。
def fib(): a = 0 b = 1 yield a while True: yield b a, b = b, a+b a = fib() for i in range(10): print(next(a)) for i in range(200): print(next(a))
运行结果如下。与标准函数相比,这时 fib 才真正有了有数列的感觉。你一直调用它,它会源源不断地返回更多的后继,像是射击,开一枪,响一声,再开一枪,再响一声。
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765 10946 17711 28657 46368 75025
请设想,用类能够实现类似的功能吗?生成器是不是最简的选择?
【下一个坑:str 与 repr 有何差别?】
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/140453.html