迭代器
容器类型:list / tuple / dict /set
容器类型取值方法
例:列表
#列表 #第一种方法:利用索引 l = [1,2,3,4] print(l[0]) # 利用索引 print(l[:2])# 所有切片都相当于把一段内容取出来放在一个新列表中 print(l[:]) # 生成了一个新的列表,默认浅拷贝 # 所有的切片,都拷贝第一层, 对于没有嵌套的列表来说,切片就相当于深拷贝 #第二种:for循环 l = [1,2,3,4] for i in l: print(i) 当需要对某一个列表中的每一个元素都需要的时候才用for循环
例:字典
dic = {'name':'wanglan','age':18} #第一种方法:key print(dic['name']) print(dic['age']) # #第二种方法:for循环 for k,v in dic.items(): print(k,v) for k in dic: if k == "name": print(dic[k])
整型
for i in 10: print(i) 结果: for i in 10: TypeError: 'int' object is not iterable
报错:int类型不是一个iterable ,iterable表示可迭代的,说明整型不能迭代,可用使用dir()来查看数据类型是否可以迭代
print(dir([])) #判断列表 print(dir(())) #判断元组 print(dir(123)) #判断整型 :整型的内部不含有__iter__ 如果输出内容函数__iter__方法,说明该数据类型是可迭代的
可迭代协议 : 只要是含有'__iter__' 方法的数据类型都是可迭代的
检查某个变量或值是不是可迭代的
#第一种检测方法 print('__iter__' in dir([])) print('__iter__' in dir(123)) #结果: True False #第二种检测方法 from collections import Iterable print(isinstance([],Iterable)) # 内置函数,判断一个具体的值是不是某个数据类型的 print(isinstance(123,Iterable)) #结果: True False
可以迭代的都是可以使用for循环的
l = [1,2,3,4] res = l.__iter__() print(res) 结果: <list_iterator object at 0x00000000025D80F0> # iterator : 迭代器
迭代器和可迭代的区别
l = [1,2,3,4] res = l.__iter__() print(dir(res)) print(dir(l)) print(set(dir(res)) - set(dir(l))) #在 res 中,但是不在 l 中的所以方法 结果: ['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__length_hint__', '__lt__', '__ne__', '__new__', '__next__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__'] ['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort'] {'__length_hint__', '__setstate__', '__next__'} #区别在此
__length_hint__ :查看迭代器中有多少个元素()
l = [1,2,3,4] res = l.__iter__() print(res.__length_hint__()) 结果: 4
__setstate__ :控制迭代器从哪儿开始迭代
l = [1,2,3,4] res = l.__iter__() res.__setstate__(2) for i in res: print(i) 结果: 3 4
__next__:从迭代器中取下一个值
l = [1,2,3,4] res = l.__iter__() print(res.__next__()) print(res.__next__()) print(res.__next__()) print(res.__next__()) 结果: 1 2 3 4
用迭代器的next方法来写一个不依赖for的遍历
l = [1,2,3,4] l_iter = l.__iter__() item = l_iter.__next__() print(item) item = l_iter.__next__() print(item) item = l_iter.__next__() print(item) item = l_iter.__next__() print(item) item = l_iter.__next__() print(item) 结果: item = l_iter.__next__() StopIteration 1 2 3 4
这段代码会报错,因为一直取next取到迭代器没有元素了,就会抛出StopIteration
使用异常处理机制来处理异常
l = [1,2,3,4] l_iter = l.__iter__() while True: try: item = l_iter.__next__() print(item) except StopIteration: break
迭代器协议 : 含有__next__和__iter__方法的变量/值都是迭代器
迭代器的特点:
具有next和iter方法
通过一个next多次执行就可以获得所有这个容器中的值
迭代器中的值只能取一次
不取的时候值不出现
for 循环取值:
for循环内部的机制就是迭代器取值的机制
在for循环执行的过程中 : 先把可迭代的变成一个迭代器,然后再从中一个一个的取值
本质上for循环替我们做的操作:
1.生成迭代器
2.循环每一次对这个迭代器执行next
3.并且到迭代器的最后就停止
迭代器的作用就是节省内存,for循环就是利用了迭代器节省内存的特点来对python当中的变量来进行操作的
l = [1,2,3,4] res1 = l.__iter__() res2 = l.__iter__() # res1和res2都是迭代器 # 是两个完全不同的迭代器
l = [1,2,3,4] res1 = l.__iter__() res2 = l.__iter__() for i in res1: print(i) print('**') for i in res1: print(i) print('--') 结果: 1 2 3 4 ** --
第二个for循环并没有打印,因为for循环内部的机制就是迭代器取值的机制,每个迭代器都只能取值一次
创建一个迭代器,循环这个迭代器,每次执行iter方法就创建一个迭代器,
l = [1,2,3,4] for i in l.__iter__(): print(i) print('^^') for i in l.__iter__(): print(i) 结果: 1 2 3 4 ^^ 1 2 3 4
用while循环实现一个for循环的功能(循环list/dict)
l = [1,2,3,5,6,7,12,23,412] l_iter = l.__iter__() count = 0 while count < len(l): try: # 要保护的代码 print(l_iter.__next__()) except StopIteration: # 要保护的代码中一旦出现了StopIteration错误,程序不报错,直接执行break break count +=1
生成器
迭代器有两种:
一种是调用方法直接返回的,一种是可迭代对象通过执行iter方法得到的,迭代器有的好处是可以节省内存。
如果在某些情况下,我们也需要节省内存,就只能自己写。我们自己写的这个能实现迭代器功能的东西就叫生成器
生成器函数:
常规函数定义,但是,使用yield语句而不是return语句返回结果。yield语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次重它离开的地方继续执行
本质:迭代器(所以自带了__iter__方法和__next__方法,不需要我们去实现)
特点:惰性运算,开发者自定义
定义:
一个包含yield关键字的函数就是一个生成器函数。yield可以为我们从函数中返回值,但是yield又不同于return,return的执行意味着程序的结束,调用生成器函数不会得到返回的具体的值,而是得到一个可迭代的对象。每一次获取这个可迭代对象的值,就能推动函数的执行,获取新的返回值。直到函数执行结束。
定义一个生成器
def func(): print('hello') yield 1 a = func() #调用生成器函数,调用不会执行这个函数,而是返回一个生成器,a是一个生成器 g = a.__next__() #执行一次 print(g)
def func(): print('hello') yield 1 print('world') yield 2 g = func() a = g.__next__() print(a) b = g.__next__() print(b)
yield关键字的特点: 可以记录当前函数中执行的位置,下一次继续执行,需要next来触发这个函数继续向下执行
next和yield是一对搭档 : next开始函数的执行 yield停止函数的执行
生成器函数和其他函数[在本质上]我们得到的结果是相同的,只不过中间的过程不同
普通函数:必须得到所有结果之后才返回
生成器函数:可以一边计算结果一边返回
迭代器:
可迭代协议 : 某个变量中含有__iter__方法,这个变量可迭代
迭代器协议 : 某个变量中含有__iter__和__next__方法,这个变量是迭代器
迭代器和可迭代的关系:
迭代器一定可迭代反之不成立
迭代器的特点 :
节省内存
惰性运算
只能取一次
只能按照顺序取
for循环和迭代器之间的关系
如果是非迭代器,for循环会帮助我们把这个飞迭代器编程迭代器(通过iter方法)
帮助我们执行next方法从迭代器中取值
帮助我们处理异常停止迭代
生成器
生成器就是迭代器 我们怎么获得生成器?? 生成器函数 生成器表达式 什么是生成器函数? 含有yield方法 生成器函数有什么特点? 调用的时候不执行 返回一个生成器/迭代器 从生成器中取值的方式和迭代器一样
例:
def produce(): """生产衣服""" for i in range(2000000): yield "生产了第%s件衣服"%i product_g = produce() print(product_g.__next__()) #要一件衣服 print(product_g.__next__()) #再要一件衣服 print(product_g.__next__()) #再要一件衣服 num = 0 for i in product_g: #要一批衣服,比如5件 print(i) num +=1 if num == 5: break 结果: 生产了第0件衣服 生产了第1件衣服 生产了第2件衣服 生产了第3件衣服 生产了第4件衣服 生产了第5件衣服 生产了第6件衣服 生产了第7件衣服
监听文件的输入,对于文件中随时输入的内容进行自动化分析/自动化展示
def get_line(): f = open('file',encoding='utf-8') while True: line = f.readline().strip() if line: yield line.strip() line_g = get_line() for line in line_g: print(line.split(','))
send
def func(): print('草莓') yield 1 print('香蕉味') yield 2 print('榴莲') yield 3 g = func() for i in g: print(i) # 拆开for循环 # n = g.__next__() # print(n) # n = g.__next__() # print(n) # n = g.__next__() # print(n) # send def func(): print('草莓') wl = yield 1 print(wl) yield 2 print('榴莲') yield 3 g = func() print(g.__next__()) #开头必须是__next__ print(g.send('香蕉')) #相当于g.__next__,只不过传入一个值进去
计算移动平均值
def fun(): sum_n = 0 count = 0 while True: if count: num = yield sum_n/count #9 返回 4.0 else: # 2 num = yield 0 #3 返回 0 # 6 num = 4 sum_n +=num #7 sum_n = 4 count +=1 #8 count = 1 g = fun() avg = g.__next__() # 1 print(avg) #4 0 avg = g.send(4) # 5 传入4 #10 返回4 print(avg) ##11 4.0 avg = g.send(5) print(avg) avg = g.send(6) print(avg)
yield form
def func(): l1 = [1,2,3] s2 = 'abc' yield from l1 #yield form 后面直接跟可迭代变量 yield from s2 g = func() for i in g: print(i)
其他
def func(): lst = [1,2,3,4,5] return lst #[1,2,3,4,5] #lst = func() for i in func(): print(i)
def func(): lst = [1,2,3,4,5] for num in lst: yield num #相当于 # num = 1 # yield num # num = 2 # yield num # num = 3 # yield num # num = 4 # yield num # num = 5 # yield num #g = func() #生成器函数的返回值是一个生成器,生成器的本质是迭代器,g是一个生成器/迭代器 for i in func(): # g 和 func 是相同的 print(i) #for 循环是通过迭代器实现的 #相当于 # print(g.__next__()) # 1 从生成器中,通过next取到的值才是yield的返回值 # print(g.__next__()) # 2 # print(g.__next__()) # 3 # print(g.__next__()) # 4 # print(g.__next__()) # 5
在python2.x中: range() 列表 xrange():生成器
在python3.x中:range() 可迭代的,既不是生成器也不是迭代器