迭代器
可迭代对象
遵守可迭代协议的就是可迭代对象,例如:字符串,list dic tuple set都是可迭代对象
或者说,能被for循环的都是可迭代对象
或者说,具有对象.__iter__方法的都是可迭代对象
print(list.__iter__([1,2,3])) print(dict.__iter__({1:2,3:4})) print(tuple.__iter__((1,2,3,4))) print(range.__iter__(range(10))) 运行结果 <list_iterator object at 0x0000023C28DE8710> <dict_keyiterator object at 0x0000023C28DC6868> <tuple_iterator object at 0x0000023C28DE8710> <range_iterator object at 0x00000109ABB3BF30>
迭代器
遵守迭代器的协议(规则)
必须具备 __iter__ 方法和 __next__方法 两者都有,才是迭代器
迭代器的特性
迭代器不能回退
它是由惰性的
是只执行一次
举个例子
lst = [1,2,3,4] l1 = lst.__iter__() #转换成迭代器 print(l1.__next__()) #执行迭代器 print(l1.__next__()) print(l1.__next__()) print(l1.__next__()) 运行结果: 1 2 3 4
for循环就是一个迭代器(for 它的执行机制如下)
lst = [1,2,3,4] count = 0 l = lst.__iter__() while count < len(lst): print(l.__next__()) count += 1 运行结果: #这就是for循环的运行结果 1 2 3 4
大家可能发现一个问题
lst = [1,2,3,4] l1 = lst.__iter__() #转换成迭代器 print(l1.__next__()) #执行迭代器 print(l1.__next__()) print(l1.__next__()) print(l1.__next__()) print(l1.__next__()) print(l1.__next__()) lst共有4个元素,当我在执行的时候多加了几个,它就会报错,那既然for循环也是这样的机制为啥它不报错呢?,这里用到一个知识点,叫捕捉错误 while True: try: lst = [1,2,3,4] l1 = lst.__iter__() #转换成迭代器 print(l1.__next__()) #执行迭代器 print(l1.__next__()) print(l1.__next__()) print(l1.__next__()) print(l1.__next__()) print(l1.__next__()) except StopIteration: #如果知道是什么类型的错误,可以添加 break
如何判断它是不是迭代器或者是不是可迭代对象呢?
from collections import Iterable,Iterator lst = [1,2,3,4] print(isinstance(lst,Iterable)) print(isinstance(lst,Iterator)) print(isinstance(lst.__iter__(),Iterable)) print(isinstance(lst.__iter__(),Iterator)) print(isinstance({1:2,3:4,5:6},Iterable)) print(isinstance({1:2,3:4,5:6},Iterator)) 返回结果: True False True True True False
生成器
生成器表达式:
(结果 for 变量量 in 可迭代对象 if 条件筛选)
生成器表达式可以直接获取到⽣成器对象. ⽣成器对象可 以直接进行for循环. ⽣成器具有惰性机制.
什么是生成器
函数体中存在yield,就是生成器
可以自己定义一些逻辑的
先来举个例子
我们平常上班的时候都比较忙,没时间逛超市,所以一到周末的时候就会去超市把一星期的菜都买好,放冰箱里,这样就不用天天去超市那么麻烦了,但是冰箱里都被放满了,想放点其他的又比较费劲,我们就在想如果能让邻居的大妈每天去买菜的时候给我们带一份是不是很好,那就是如果明天需要做饭就在前一天晚上告诉大妈明天给带一份就可以了,这个就有点类似于生成器的功能,只有在告诉它的时候才会触发.
生成器的执行顺序
def func(): #第1步定义名称 print(1) #第5步 执行生成器 yield 4 #第6步 返回执行结果并打印出来,执行到这里停止 print(2)#第8步 执行生成器 yield 5 #第9步 返回结果并打印出来 g = func() #第2步,执行,当方向代码块中有yield时,识别为生成器, 执行第3步 g = func()赋给变量g print(g.__next__())#第4步第一次调用生成器 print(g.__next__())#第7步第二次调用生成器 运行结果: 1 4 2 5
看到上面这个例子,有的人会说,这样也太麻烦了吧,我执行一次就的在下面写一个print(g.__next__()),并不好用,我们都知道for循环的原理就是生成器,那我们可以把多次执行利用for循环来实现
def gen(): li = [] for i in range(10): yield i g = gen() print(g.__next__())#这样是不是很麻烦 print(g.__next__()) print(g.__next__()) print(g.__next__()) print(g.__next__()) print(g.__next__()) print(g.__next__()) print(g.__next__()) print(g.__next__()) print(g.__next__())
def gen(): li = [] for i in range(10): yield i g = gen() for i in range(10): print(g.__next__())#利用for 循环,很方便的执行
send()
我们在生成器执行时,除了使用next之外,还可以使用send()方法
def func(): print(44) l = yield 5 print(55) print(l) yield 3 g = func() print(g.__next__()) print(g.send('你好啊')) #相对于 __next__ + 赋值(既能执行下一步操作,又能把值带进去 打印结果: 1 4 2 5 44 5 55 你好啊 3 def gen(x): res = 0 for i in range(x): res = yield res ** 2 g = gen(5) g.send(None) print(g.send(1)) print(g.send(2)) print(g.send(3)) print(g.send(4))
生成器总结
1生成器具有延迟的作用
生成器其实就是变异的函数,格式和内容都跟函数类似,我们都知道,当我们执行函数的时候,只要函数名+()就会直接执行,但是生成器却不是这样的,它需要执行__next__的时候才会执行,如果不进行此操作,生成器就不会起作用,跟我在例子说到的一样,用的时候才会调用.
2.生成器可以记录位置和结果
当我们执行调用生成器的时候,遇到yield时,它会记录位置,并且可以返回一个结果,等我们下一次再进行调用时,直接从上一次的位置开始执行下面的.
3.生成器节约内存空间
我们如果想获取1个列表中的上万条数据,那首先得先把列表拿出来,然后再进行循环过滤等等,但我们不会一次性获取所有的,只是一部分,如果这么做就太浪费内存了,如果我们使用生成器,可以要什么调什么,岂不是很好.
4.生成器中next执行和yield要一一对应
推导式
列表推导式
li = [] for i in range(10): li.append(i) print(li) li = [i for i in range(10)] #列表推导式 print(li) 打印结果: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
列表筛选
li = [i for i in range(10)if i > 3] print(li) li = [i for i in range(10)if i % 2 == 0] print(li)
集合推导式
dic = {1:2,3:4} se = {i for i in dic.items()} print(se) 输出结果: {(1, 2), (3, 4)}
推导过程
dic = {1:2,3:4} se = set() for i in dic.items(): se.add(i) print(se) 打印结果: {(1, 2), (3, 4)}
字典推导式
lst = [1,3] lst1 = [2,4] dic = {lst[i]:lst1[i] for i in range(2)} print(dic) 输出结果: {1: 2, 3: 4}
推导过程
lst = [1,3] lst1 = [2,4] dic = {} for i in range(2): dic[lst[i]] = lst1[i] print(dic) 输出结果: {1: 2, 3: 4}
生成器推导式
跟元组很相似,但是元组没有推导式,这个是生成器的推导式
l = (i for i in range(100)) for i in range(10): print(l.__next__())
推导过程
def func(): for i in range(10): yield i l = func() for i in range(10): print(l.__next__())