生成器概念:在实现一个迭代器时,关于当前迭代到的状态需要我们自己记录,进而才能根据当前状态生成下一个数据。为了达到记录当前状态,并配合next()函数进行迭代使用,我们可以采用最简便的语法,即生成器。生成器是一类特殊的迭代器。
创建生成器的方法:
1.使用元组推导式
1 import random 2 3 numbertuple = (random.randint(10, 100) for i in range(10)) 4 print(numbertuple) # 生成的numbertuple是一个生成器对象 5 6 # 可以使用for循环提取生成器对象中的元素的值 7 for num in numbertuple: 8 print(num)
运行结果:
<generator object <genexpr> at 0x00000000025FE740> 21 57 31 31 97 58 52 93 73 11
2.自己定义生成器(函数)——函数中含有yield,创建出的变量即是生成器
1 def create_num(all_num): 2 a, b = 0, 1 3 current_num = 0 4 while current_num < all_num: 5 yield a # 如果一个函数中有yield语句,那个这个就不再是函数,而是生成器的模板 6 a, b = b, a+b 7 current_num += 1 8 return "OK" 9 10 11 # 如果在调用creat_num函数的时候,发现这个函数中有yield 12 # 那么此时,不是调用函数,而是创建一个生成器对象,此时的fibo就是一个生成器对象 13 fibo = create_num(10)
可以使用for循环提取生成器中的元素的值:
1 for num in fibo: 2 print(num) 3 4 5 # 运行结果: 6 0 7 1 8 1 9 2 10 3 11 5 12 8 13 13 14 21 15 34
利用next函数提取生成器中元素的值:
1 # next函数执行步骤:第一次执行会从含yield的函数的开头执行,当执行到yield时函数执行暂停 2 # 将yield后面的值返回,返回到调用next函数代码位置 3 obj = next(fibo) 4 print(obj) 5 6 # 第二次执行会从函数yield暂停位置继续向下执行,当再次执行到yield时函数再次暂停 7 # 将yield后面的值返回,返回到调用next函数代码位置 8 obj = next(fibo) 9 print(obj) 10 11 # 运行结果: 12 0 13 1
利用next函数循环提取生成器中元素的值,提取完退出程序:
1 while True: 2 try: 3 ret = next(fibo) 4 print(ret) 5 except StopIteration as ret: 6 break 7 8 # 运行结果: 9 10 0 11 1 12 1 13 2 14 3 15 5 16 8 17 13 18 21 19 34 20 OK
如果生成器的模板(函数)中含有return的返回值,可以在异常中通过异常.value获取这个返回值:
1 def create_num(all_num): 2 a, b = 0, 1 3 current_num = 0 4 while current_num < all_num: 5 yield a 6 a, b = b, a+b 7 current_num += 1 8 return "OK" 9 10 fibo = create_num(10) 11 12 while True: 13 try: 14 ret = next(fibo) 15 # print(ret) 16 except StopIteration as ret: 17 # test = ret.value 18 # print(test) 19 print(ret.value) 20 break 21 22 # 运行结果: 23 OK
除了通过next函数提取生成器中的元素的值外,还可以通过send函数提取,send函数可以传一个参数到生成器的模板(函数)内:
1 def create_num(all_num): 2 a, b = 0, 1 3 current_num = 0 4 while current_num < all_num: 5 yield a 6 a, b = b, a+b 7 current_num += 1 8 9 fibo = create_num(10) 10 11 obj = next(fibo) 12 print(obj) 13 14 ret = fibo.send("hello python") 15 print(ret) 16 17 # 运行结果: 18 0 19 hello python 20 1
send函数使用场景:当取生成器元素的值到一定程度时,不想取下一个值而想从某次往后重新取值,可以使用send函数传一个值到生成器内部,yield在前面用一个变量=yield来接收,传入的值传给这个变量。例如:
1 def create_num(all_num): 2 current_num = 1 3 while current_num < all_num: 4 a = 2**current_num 5 ret = yield a 6 if ret != None: 7 current_num = ret-1 8 current_num += 1 9 10 11 obj = create_num(10) 12 13 ret = next(obj) 14 print(ret) 15 16 ret = next(obj) 17 print(ret) 18 19 ret = next(obj) 20 print(ret) 21 22 ret = next(obj) 23 print(ret) 24 25 ret = next(obj) 26 print(ret) 27 28 ret = next(obj) 29 print(ret) 30 31 ret = obj.send(3) # 从第3次往后重新取值 32 print(ret) 33 34 ret = next(obj) 35 print(ret)
运行结果:
2 4 8 16 32 8 16