#在了解生成器之前,可以先看看一个名为"列表生成式"的东西
list = [ x*2 for x in range(0,9) ] print(list) print(type(list)) ######## [0, 2, 4, 6, 8, 10, 12, 14, 16] <class 'list'>
#由名字可知,列表生成式的作用就是生成一个列表,规则是: [ f(x) for x in 序列 ]
#但是,列表声称式会将生成的列表直接放入内存中,当数据较大时不适用
#所以就有了生成器
#生成器的第一种创建方法:( f(x) for x in 序列 )
s1 = ( x*3 for x in range(0,3) ) print(s1) ######## <generator object <genexpr> at 0x0000000001E8C518>
#发现直接打印s1无法获取其中的信息,输出的是 生成器对象和其地址
#这就是生成器与列表生成式的区别所在,生成器不会直接将所有数据放入内存中,而是作为一个对象返回
#当需要使用时,通过next()方法调用
print(next(s1)) print(next(s1)) print(next(s1)) print(next(s1)) ######## 0 3 6 Traceback (most recent call last): File "E:/9--python/python_project/test.py", line 25, in <module> print(next(s1)) StopIteration
#生成器在创建时就已经确定了其数据量,调用的次数就已经确定,如果超过生成器的数据量的调用就会有 StopIteration 的报错,此报错在for循环中会用到,当然这是后话
#由此可见,生成器占用的内存仅为保存对象地址,在调用时才生成值
#第一种生成器的创建比较粗旷,另外一种生成器的创建则可定义的内容较多
def foo(): print("ok1") yield 2 print("ok2") yield 5 s2=foo() print(s2) print(next(s2)) print(next(s2)) print(next(s2)) ######## <generator object foo at 0x00000000027CC518> ok1 2 ok2 5 Traceback (most recent call last): File "E:/9--python/python_project/test.py", line 37, in <module> print(next(s2)) StopIteration
#生成器也算是函数的另一变种,一个yield就可以迭代一次,yield的返回值同return一样可以用参数接收
#在yield中,自动封装了next()方法,使得我们可以优雅的调用
#next()方法的路径是:
1.第一个next(s1),进入foo()函数体,遇到print("ok1"),输出
2.向下执行代码,遇到yield 2,返回指定值2,退出,状态保存在此,待下一个next()执行来调用
3.第二个next(s2),进入foo()函数体,从yield 2开始,向下执行代码,遇到print("ok2"),输出
4.向下执行代码,遇到yield 5,返回指定值5,退出,状态保存在此,待下一个next()执行来调用
5.第三个next(s3),进入foo()函数体,从yield 5开始,向下执行代码,因为没有yield,报错
#从以上可知,生成器的代码不会一次性执行完,而是遇到yield就退出,并将状态保存,待下次执行时调用,这个是实现协程的基础
#生成器除了next()方法外,还有一个可以传入参数的方法:generator.send()
def bar(): count1 = yield 5 print(count1) count2 = yield 1 print(count2) s3=bar() ret1=s3.send(None) print(ret1) ret2=s3.send(2) print(ret2) ret3=s3.send(6) print(ret3) ######## 5 2 1 6 Traceback (most recent call last): File "E:/9--python/python_project/test.py", line 52, in <module> ret3=s3.send(6) StopIteration
#generator.send()方法可以向生成器传入参数,但第一次因为还没有进入函数体,所以也不知道传递给那个参数,所以第一次使用send()方法时不能传入值,generator.send(None)=next(generator)
#当下一次使用send(var)方法传入值时,会进入上次退出的位置,将值传给此处预先定义的参数,向下执行代码,直到下一个yield,保存状态,退出