4 迭代器和生成器
一 迭代器
1.1 概念
# 凡是可以使用for循环取值的都是可迭代的
# 可迭代协议 :内部含有__iter__方法的都是可迭代的,如list,dic,tuple,str,集合,range,文件,等都是可迭代的。
# 迭代器协议 :内部含有__iter__方法和__next__方法的都是迭代器
1.2 优势
可迭代 最大的优势 节省内存
迭代器优势:
1节省内存
2取一个值就能进行接下来的计算 ,而不需要等到所有的值都计算出来才开始接下来的运算
1.3 例子
1 这两个都是可迭代的,都会取到1,2,3 2 3 lst_iter = [1,2,3].__iter__() 4 print(lst_iter.__next__()) 5 print(lst_iter.__next__()) 6 print(lst_iter.__next__()) 7 8 9 for i in [1,2,3]: 10 print(i)
二 生成器
2.1 概念
生成器 Generator
# 自己写的迭代器 就是一个生成器
# 两种自己写生成器(迭代器)的机制:生成器函数 生成器表达式
凡是带有yield的函数就是一个生成器函数
2.2优势
同样是节省内存
2.3 例子(生成器函数)
1 # 生成器函数的调用不会触发代码的执行,而是会返回一个生成器(迭代器) 2 # 想要生成器函数执行,需要用next 3 4 def cloth_g(num): 5 for i in range(num): 6 yield 'cloth%s'%i 7 8 9 g = cloth_g(1000) 10 print(next(g)) 11 print(next(g)) 12 print(next(g))
2.4 send关键字
1 # send关键字 2 def func(): 3 print(11111) 4 ret1 = yield 1 5 print(22222,'ret1 :',ret1) 6 ret2 = yield 2 7 print(33333,'ret2 :',ret2) 8 yield 3 9 10 11 g = func() 12 ret = next(g) 13 print(ret) 14 print(g.send('alex')) # 在执行next的过程中 传递一个参数 给生成器函数的内部 15 print(g.send('金老板')) 16 17 # 想生成器中传递值 有一个激活的过程 第一次必须要用next触发这个生成器
2.5 预激活生成器 这样在下面函数执行的时候不用再next了,或者多个函数需要激活的时候,就适合方便用这种方法
1 # 预激生成器 2 # def init(func): 3 # def inner(*args,**kwargs): 4 # ret = func(*args,**kwargs) 5 # next(ret) # 预激活 6 # return ret 7 # return inner 8 # 9 # @init 10 # def average(): 11 # sum_money = 0 12 # day = 0 13 # avg = 0 14 # while True: 15 # money = yield avg 16 # sum_money += money 17 # day += 1 18 # avg = sum_money/day 19 # 20 # g = average() 21 # print(g.send(200)) 22 # print(g.send(300)) 23 # print(g.send(600))
2.6 yield from
1 第一种写法: 2 def genrator(): 3 for i in range(5): 4 yield i 5 for j in ('hello'): 6 yield j 7 g = genrator() 8 9 第二种写法: 10 11 def generator_func(): 12 yield from range(5) 13 yield from 'hello' 14 g = genrator()
2.7 如何从生产器中取值,三种
# 第一种 :next 随时都可以停止 最后一次会报错
# print(next(g))
# print(next(g))
# 第二种 :for循环 从头到尾遍历一次 不遇到break、return不会停止
# for i in g:
# print(i)
# 第三种 :list tuple 数据类型的强转 会把所有的数据都加载到内存里 非常的浪费内存
# print(g)
# print(list(g))
2.8生成器函数总结
# 一个生成器 只能取一次
# 生成器在不找它要值的时候始终不执行
# 当他执行的时候,要以执行时候的所有变量值为准
# 主要特征是 在函数中 含有yield
# 调用一个生成器函数 不会执行这个函数中的带码 只是会获得一个生成器(迭代器)
# 只有从生成器中取值的时候,才会执行函数内部的带码,且每获取一个数据才执行得到这个数据的带码
# 获取数据的方式包括 next send 循环 数据类型的强制转化
# yield返回值的简便方法,如果本身就是循环一个可迭代的,且要把可迭代数据中的每一个元素都返回 可以用yield from
# 使用send的时候,在生成器创造出来之后需要进行预激,这一步可以使用装饰器完成
# 生成器的特点 : 节省内存 惰性运算
# 生成器用来解决 内存问题 和程序功能之间的解耦
三列表推导式和生成器表达式
区别就是中括号和小括号的区别
1 ##列表推导式 2 3 # print([i**2 for i in range(5)]) 4 # 5 # l = [1,2,3,-5,6,20,-7] 6 # print([abs(i) for i in l ]) 7 # print([i for i in l if i%2==1]) 8 ##被30整除数的平方 9 # print([i**2 for i in range(30) if i%3 ==0]) 10 列表推导式 11 [i**2 for i in range(30) if i%3 ==0] 12 13 生成器表达式 14 g = (i**2 for i in range(30) if i%3 ==0)