生成器
生成器
生成器仅仅保存了一套生成数值或者对象的算法,并且没有让这个算法现在就开始执行,而是我什么时候调它,它什么时候开始计算一个新的值,并给你返回。
为何要用生成器
存储海量的数据会占用内存资源,如果我们可以根据算法推算后面的数据,什么时候需要的时候就去生成,这样将极大地减少内存占用。
创建生成器
1. 列表生成式的[]改成()
L = [x * x for x in range(10)]
print(L)
G = (x * x for x in range(10))
print(G)
print(next(G)
执行结果
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
<generator object <genexpr> at 0x02739DB0>
0
next是生成器能迭代的关键,next(G)等价于G.__next__()
,调用他就生成一个新的对象并返回。
2. yield
在函数里面有yield关键字,函数就变成了一个生成器。当调用next()函数时,就相当于生成下一个对象(统称),这一次next开始的地方是接着上一次next停止的地方执行的,即yield之后继续执行,到yield处返回生成的对象。
例子
def fun():
print('run fun')
while True:
rtn = yield 6
print('rtn:',rtn)
g = fun()
print(g)
print(next(g))
print('-------')
print(next(g))
print('------------')
print(next(g))
执行结果
<generator object fun at 0x0278CDB0>
run fun
6
-------
rtn: None
6
------------
rtn: None
6
分析
-
程序开始执行以后,因为fun函数中有yield关键字,所以先得到一个生成器对象g,由print(g)可以看出他是一个生成器对象
-
直到调用next方法,fun函数正式开始执行,先执行fun函数中的print方法,然后进入while循环(++生成器都有一个while True循环以此不断生成对象并返回++)
-
程序遇到yield关键字,把yield想象成return,return了一个6之后,程序停止,并没有执行赋值操作,此时next(g)语句执行完成,返回6,是执行print(next(g))的结果,
-
程序执行print,,输出---
-
又开始执行下面的print(next(g)),这个时候是从刚才那个next程序停止的地方开始执行,也就是要执行rtn的赋值操作,这时候要注意,这个时候赋值操作的右边是没有值的(因为刚才那个是return出去了,并没有给赋值操作的左边传参数),所以这个时候rtn赋值是None,所以接着下面的输出就是rtn:None,继续走while循环
-
又一次碰到yield,这个时候同样return 出6,然后程序停止,print函数输出的6就是这次return的返回值.
-
继续5-6
再看
def fun():
print('run fun')
while True:
rtn = yield 6
print('rtn:',rtn)
g = fun()
print(g)
print(next(g))
print('-------')
print(g.send(True))
print('------------')
print(g.send('赋值操作'))
执行结果
<generator object fun at 0x029AEAB0>
run fun
6
-------
rtn: True
6
------------
rtn: 赋值操作
6
这次的rtn为什么有值了呢?
send和next区别
1. send和next()都是让生成器向下走一次
2. send可以给上一个yield的位置传递值,不能给最后一个yield发送值,在第一次执行生成器代码的时候不能使用send()
send()干了两件事情:
- 发送一个参数做赋值操作
- 函数内部包含执行next(),yield 6 返回