迭代器和生成器函数
迭代器顾名思义那就是可迭代的 才能做迭代器。(个人理解 )
字符串、列表、元组、字典、集合都可以被for循环,说明他们都是可迭代的。
迭代器它和装饰器一样只是一个器物 需要我们去转换使用 那么首先就是先把这个迭代的物品转化为迭代器 才能使用
那就是首先这个被转化的必须是可迭代的 才可以(字符串,列表 字典 元组 ...)这些可迭代的都可以转化为迭代器
比如你要查看你所不确定的类型是不是可做迭代器那么你就可以先测试下
s = 'nihaoalaoxiang' print(dir(s)) #查看当前字符串 列表、字典、变量所使用的方法 print(dir(5)) s = 'laowangzaigebi' print('__iter__' in dir(s)) #判断迭代器所含有的方法在不在这个字符串内 如果在里面返回的是True这个就是迭代器
你可以首先用dir来查看你所不确定的类型是不是可迭代的 当它包含的方法有__iter__这个方法的时候那就是可以做迭代器的
也直接判断这个__iter__方法在不在它里面 然后在里面就是返回True不在里面就是False
什么是可迭代对象:内部含有__iter__方法的对象就叫做可迭代对象
#我们也可以导入一个模块来判断 from collections import Iterable l = [1, 2, 3, 4] print(isinstance(l, Iterable)) #True print(type(l)) #list print(isinstance(l,list))
我们现在是从结果分析原因,能被for循环的就是“可迭代的”,但是如果正着想,for怎么知道谁是可迭代的呢? (因为for就是一一给循环的 那么就是可以一一迭代的)
假如我们自己写了一个数据类型,希望这个数据类型里的东西也可以使用for被一个一个的取出来,那我们就必须满足for的要求。这个要求就叫做“协议”。
可以被迭代要满足的要求就叫做可迭代协议。可迭代协议的定义非常简单,就是内部实现了__iter__方法。
迭代器遵循迭代器协议:必须拥有__iter__方法和__next__方法。
for循环,能遍历一个可迭代对象,他的内部到底进行了什么?
- 将可迭代对象转化成迭代器。(可迭代对象.__iter__())
- 内部使用__next__方法,一个一个取值。
- 加了异常处理功能,取值到底后自动停止。
可迭代对象转化成迭代器:可迭代对象.__iter__() --->迭代器
迭代器不仅含有__iter__,还含有__next__。遵循迭代器协议。
如果迭代器想要输出结果那么就要__next__方法来实现
ll = [1, 2, 3] ll_obj = ll.__iter__() #把可迭代类型转化为迭代器 print(ll_obj.__next__())#1 打印第一个迭代器第一个元素 print(ll_obj.__next__())#2 打印迭代器第二个元素 print(ll_obj.__next__())#3 打印迭代器第三个元素
但是你也只能用__next__()方法来实现迭代器的元素的打印功能 每一个迭代器调用这个方法只输出一个元素 如果你输入的__next__()方法比迭代器内的元素多的话那么就会报错
迭代器的好处:
1、节省内存空间
2、满足惰性机制 #就是你每输出一个next才能输出一个数字
3、不能反复取值,不可逆
迭代器的优点:如果用了迭代器,节约内存,方便操作
可迭代和迭代器的不同点:就是迭代器内部多实现了一个__next__方法
第一种:判断内部是不是实现了__next__方法
1 '__iter__' in dir(str)#如果__iter__在这个方法里面,就是可迭代的。
第二种:
Iterable 判断是不是可迭代对象
Iterator 判断是不是迭代器
用法:
from collections import Iterable 2 from collections import Iterator 3 4 #比如给一个字符串 5 s='abc' 6 print(isinstance(s,Iterable))#isinstance判断类型的 7 print(isinstance(s,Iterator))
面试题:
怎么样运用while循环把for使用迭代器的元素给输出:
l2 = [1, 2, 3, 4, 5, 6] l2_obj =l2.__iter__() #把l2这个列表转化为迭代器 while True: try : i = l2_obj.__next__() print(i) except Exception: break
生成器函数:常规定义函数,但是,使用yield语句而不是return语句返回结果。yield语句一次返回一个结果。生成器的好处,就是一下子不会在内存中生成太多的数据
python中提供的生成器:1.生成器函数 2.生成器表达式
第一:函数中只要有yield 那他就不是一个函数,而是一个生成器
也就是生成器其实就是函数只不过他是带有yield的函数 只要一个函数带有yield那么它就是一个生成器
生成器的本质:就是一个迭代器
生成器的产生方式:
1,生成器函数构造。
2,生成器推导式构造。
3,数据类型的转化。
第一:函数中只要有yield 那他就不是一个函数,而是一个生成器
def func1(): print(111) print(222) print(333) yield 666 yield 555 yield 777 g = func1() #:g称作生成器对象。
也就是生成器中必须要有yield
def denerator(): print(123) print(456) yield g = denerator() g.__next__()
yield也可以写在数值前面 然后用send来改变这个值
生成器中的__next__方法使用一次就可以把生成器中的第一个yield上面的内容全部打印出来,第二个__next__()是把第一个和第二个yield中间的值全部打印出来 而yield 修饰的值是不会打印出来 --next--方法相当与把yield修饰的值给收起来了你想打印yield修饰的值 需要print'生成器调用的--next--()方法
在生成器中next也可以使用进行一一输出 send也可以但是send只能对yiled的修改值进行输出 就是yield进行修饰的值 这个被yield修改的值也必须进行赋值然后才能send进行重新赋值,
send只能对上一个yield的修饰值进行重新赋值 不能对最后一个进行赋值 所以用send的话 那么你的生成器中必须有两个yield并且这个被send重新定义的yiled修饰的数值必须赋值给一个变量然后打印这个变才能看出send重新定义的值
def denerator(): print(123) m = yield 4 print(m) print(456) yield 3 g = denerator() g.__next__() # g.send('234') g.send(345)
send和next一样取值的如果你首先用next取了一个值之后 你的send就会取下一个值的并且把你的上一个yield的值进行重新赋值定义,send也就是对你的相应的yield的值进行重新赋值然后读取下一个的值,
send就是对上一个yield进行重新赋值并且读取下一个yield的值
所以最后一个yield的值只能被send读取不能被修改
def func(): m = yield 444 print(m) yield 55 yield 666 g = func() print(g.__next__()) print(g.send(1)) # 结果: 444 # 1 # 55 #