生成器这个章节尤其的重要,我们以后的协程的部分要用到这个知识点.
什么是生成器函数呢?
定义为:
只要方法里有 yield 这个关键字 代表不是普通的函数,就可以认为是生成器函数 .
怎么就不普通了,咱们和普通的比较下 ,代码如下:
def gen_func():
yield 1
def func():
return 1
print(gen_func(),func())
#打印结果
# 1
我们可以发现 gen_func() 返回的是一个对象,并不是1.
func()返回的是1 .
有的小伙伴该说了,那我们怎么去访问生成器里面的1 呢?
这个是个好问题,我们访问对象里的值一定要用到前面的知识,迭代器和迭代对象.
PS:看来知识都是关联的,要想遇到什么就学什么,绝对没有思路.
我也不做多解释,用代码解释来的直接 :
from collections import Iterable, Iterator
def gen_func():
yield 1
def func():
return 1
print(gen_func(),func())
print(isinstance(gen_func(),Iterable)) #True
print(isinstance(gen_func(),Iterator)) #True
print(next(gen_func())) # 1
借用小岳岳的一句话“这么神奇呀,生成器也是迭代器”
有了这个我们就可以放心用for 循环进行输出. 这太方便了吧.
那有较真的小伙伴该说了,你这不是瞎胡搞吗,本来直接return 个返回值
直接输出就行了,你非得再绕一大圈 然后再输入,你感觉有意思吗?
好像他说的也没有错呀,but 有个场景,我需要return 两次甚至更多,如何做呢
按照你的方法,要更多return 呗, 好我就让你心服口服,看看代码吧
def func():`在这里插入代码片`
return 1
return 2
print(func())
#打印结果: 1
return 2 并没有返回,为什么呢, 因为在return 1 的时候已经断掉了,后边的代码不会执行.
小伙伴们该说了,这样是行不通,那有什么办法吗 ? 当然离不开这节的主题,答案是生成器
请看下边的代码:
def gen_func():
yield 1
yield 2
yield "Andy"
print(gen_func())
for i in gen_func():
print(i)
#打印结果
#
#1
#2
#Andy
都打印出来了, 功能强爆了.
稍微解释下:
当我们用for 循环的时候,会调用 第一个 next() 打印出1,然后在next()打印出2
一直循环到报 StopIteration 异常结束.
由于我们调用next()会返回一个值,不调用不会往下返回值,这个特性很重要,
官方的叫法是惰性求值 .
为了明白惰性求值的好处, 我来举个例子 ,
经典斐波拉契
斐波那契数列指的是这样一个数列 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233,377
这个数列从第3项开始,每一项都等于前两项之和。
用普通的方法,来 求10位之和
def func(index):
if index<=2:
return 1
else:
return func(index-1)+func(index-2)
print(func(10)) # 55
用普通的方法,也可用实现,但是我们有个问题就是 想打印它计算过程,你怎么搞?
用普通的方法我们可以这么写:
def func2(index):
a=0
b=1
n=0
list_num=[]
while n
上边的 a,b=b,a+b 就相当于 a=b , b=a+b 这也是循环的递增方式.
有个问题,我们知道用list 有个缺点就是 性能低,如果index 很大的时候,不要用print,
这是血的教训 ,我刚刚就重启了. 还好博客的内容还在. 当index 是上千万的时候,也不要调用方法
,非常占内存,你的机器同样也会死机。
我们开始用 生成器的方式进行访问如下边:
def gen_func(index):
a=0
b=1
n=0
while n
我们已经实现了打印过程,为什么它可以使用index值为成千上万的呢,这还是离不开生成器的惰性机制,
当调用 yield 的时候,会把数值存在生成器里面,直到循环结束.
next()的时候,调用一下,不调用就不输出.
更多的功能我会在一节介绍,请关注