Python生成器
生成器
通过列表生成式,我们可以直接创建一个列表。但是受内存限制,列表容量肯定是有限的。
而且,创建一个很多很多元素的列表,不仅占用很大的内存空间,如果我们仅仅访问前几
元素,那后面绝大多数的元素占用的空间就白白浪费了。
所以,如果列表元素可以按照某种算法推算出来,那多好!这样就不必浪费空间了,这样
你好我好大家好。
在Python中,这种一边循环一边计算的机制,称为生成器:generator
要创建一个generator,有很多种方法。
1)只要把一个列表生成式的[]改为(),就创建了一个generator。
L = [i * iforiinrange(5)]
print(L)#[0, 1, 4, 9, 16]
g = (i * iforiinrange(5))
print(g)# at 0x111136ca8>
创建L和g的区别仅在于最外层[]和(),L是一个list而g是一个genratot。
我们可以直接打印出list的每一个元素,但我们怎么打印出generator的每一个元素呢?
我们可以通过 next(g)挨个拿其中的元素,最后一个取完之后再进行 next()操作会
报错(StopIteration)。
一个一个取是在太繁琐,我们可以用for循环,因为genertor是一个可迭代对象。
g = (i * iforiinrange(5))
foriing:
print(i) #用for循环来迭代,并且不需要关心StopIteration报错。
2)用函数来实现。
比如,斐波那契数列:1,1,2,3,5,8,13,21,34....
用列表生成式写不出来,但是我们可以用函数把它打印出来:
deffib(number):
n, a, b =0,0,1
whilen < number:
print(b)
a, b = b, a + b
n = n +1
return'OK!'
print(fib(5))
结果:
1
1
2
3
5
OK!
我们可以看出从第一个元素开始,推算出后续任意的元素。很像generator。
要把fib函数变成generator,只需要把print(b)改为yieldb就可以了:
deffib(number):
n, a, b =0,0,1
whilen < number:
yieldb
a, b = b, a + b
n = n +1
return'OK!'
print(fib(5))#
注意:
这里难理解的就是generator和函数的执行流程是不一样的。
函数是顺序执行,遇到return语句或者最后一行函数语句就
返回。
注意:函数创建一次生成一个生成器,所以我们会将创建的生
成器赋值给一个变量。如果直接用函数本身这个生成器,
我们没用一次生成一个新的生成器对象,所以,我们一
般都将创建的生成器赋给一个变量。
generetor的函数,在每次调用 next()的时候执行,遇到
yield语句返回,再次执行时从上次返回的yield语句处继续
执行。
例子:
defodd():
print('step 1')
yield1
print('step 2')
yield(3)
print('step 3')
yield(5)
f = odd()
print(next(f))
print(next(f))
print(next(f))
结果:
step1
1
step2
3
step3
5
执行三次后再执行 next(f) 就会报错了。
可以看到odd不是普通函数,而是generator遇到yield就会中断
下次又继续执行,执行三次后已经没有yield可以执行,所以再执
行 next(f) 就会报错了。最后的yield后面一般不写东西。
把函数改为generator后,我们基本不这么用 next()来获取下一个返回值
而是直接使用for循环来迭代。
比如上面的fib函数。
deffib(number):
n, a, b =0,0,1
whilen < number:
yieldb
a, b = b, a + b
n = n +1
return'OK!'
foriinfib(5):
print(i)
结果:
1
1
2
3
5
注意:用for循环调用generator时,发现拿不到generator的return
语句的返回值。如果想要拿到返回值,必须捕获StopIteration错误
返回值包含在StopIteration的value中。
(关于如何捕获,异常处理见。)
接下来我们来看send⽅方法, send和next()一样都可以让生成器执行到下一个yield.
例子;
defeat():
print("我吃什么啊")
a =yield"馒头"
print("a=",a)
b =yield"⼤大饼"
print("b=",b)
c =yield"韭菜盒⼦子"
print("c=",c)
yield"GAME OVER"
gen = eat()# 获取⽣生成器器
ret1 = next(gen)
print(ret1)
ret2 = gen.send("胡辣汤")
print(ret2)
ret3 = gen.send("狗粮")
print(ret3)
ret4 = gen.send("猫粮")
print(ret4)
结果:
我吃什么啊
馒头
a= 胡辣汤
⼤大饼
b= 狗粮
韭菜盒⼦子
c= 猫粮
GAME OVER
send和next()区别:
1.send()和next()都是让生成器向下走一次
2.send可以给上一个yield的位置传递值,不能给最后一个yield发送值.
在第一次执行生成器代码的时候不能使用send()
三道题:
<1>
defadd(a, b):
returna+b
defTest():
foriinrange(4):
yieldi
g = Test()
fornin[2,10]:
g = (add(n, i)foriing)
print(list(g))
结果:
[20,21,22,23]
<2>
deffunc():
print('1')
yield 'This is one step'
print('2')
yield 'This is two step'
print('3')
yield 'This is theree step'
it = func()#函数返回生成器
print(list(it))
结果:
1
2
3
['This is one step', 'This is two step', 'This is theree step']
<3>
deffunc():
print(111)
yield222
g = func()# 生成器g
g1 = (iforiing)# 生成器g1. 但是g1的数据来源于g
g2 = (iforiing1)# 生成器器g2. 来源g1
print(list(g)) # 获取g中的数据. 这时func()才会被执行. 打印111.获取到222. g完毕.
print(list(g1)) # 获取g1中的数据. g1的数据来源是g. 但是g已经取完了了. g1 也就没有数据了
print(list(g2))# 和g1同理
结果:
111
[222]
[]
[]
Form zero to hero