迭代器
可以把迭代器比喻成一个容器,可以从这个容器中一个接一个的把值取出来,取值的过程就是可迭代的过程
可迭代协议:含有__iter__方法
迭代器协议:含有__iter__方法 且 __next__方法
查看方法:print(l1.__dir__)
可迭代对象: 含有__iter__方法迭代器: 含有__iter__方法和__next__方法 迭代器.__next__ 调用迭代器
(迭代器 是 可迭代对象 的 子集) 第一种判断方法:判断是不是可迭代:print('__iter__' in dir(对象)) 对象可以是字符串、列表、字典、等等
判断是不是迭代器:print('__next__' in dir(对象))
第二种判断方法:
from collections import Iterablefrom collections import Iteratorprint(isinstance(对象,Iterable)) # 判断是不是可迭代的print(isinstance(对象,Iterator)) # 判断是不是迭代器
例如:
li = []a = li.__iter__()from collections import Iteratorfrom collections import Iterableprint(isinstance([1,2],Iterable)) #从功能上判断 而type()是类型 是就是 不是就不是print(isinstance([1,2],Iterator))print(isinstance(a,Iterator))# True# False# True
迭代器的特点:
从前往后依次去取值,不可逆,不可重复 惰性运算 节省内存取值超出范围,会抛出一个异常:
li = [1,2,3,4,5]li_iter = li.__iter__()print(li_iter.__next__())print(li_iter.__next__())print(li_iter.__next__())print(li_iter.__next__())print(li_iter.__next__())print(li_iter.__next__())#Traceback (most recent call last):# File "D:/Demo/test2.py", line 26, in# print(li_iter.__next__())#StopIteration 超出5次 报错 StopIteration
while 模拟 for
li = [1,2,3,4,5]li_iter = li.__iter__()while True: try: print(li_iter.__next__()) except StopIteration: #StopIteration 报出异常 break
python3内置迭代器:
内置迭代器:range() file(文件for循环读) enumerate()
生成器
生成器的本质: 就是 迭代器 内含__next__()方法,
一个函数里面如果有yield,那么这个函数就是生成器函数
def ge_iter(): #ge_iter() 就是生成器函数 生成器函数 里面有 yield关键字 print('aa') yield 11 # 见到yield 取一次值,且记住这个位置,下次直接从这里开始 print('bb') yield 15 yield 1 yield 2g = ge_iter() # ge_iter()是生成器 生成器就是迭代器,能用__next__方法取值print(g.__next__())# aa# 11
while取值:
def ge_iter(): print('aa') yield 11 print('bb') yield 15 yield 1 yield 2g = ge_iter() 调用ge_iter()不会立即执行,而是返回一个生成器while True: try: print(g.__next__()) except StopIteration: break
for循环取值:
def ge_iter(): for i in range(10): yield "我是第{}名".format(i)g = ge_iter()print(g.__next__())print(g.__next__())for i in range(3): # for循环取一段值 print(g.__next__()) # 会接着上次保存的位置,接着取值# 我是第0名# 我是第1名# 我是第2名# 我是第3名# 我是第4名
生成器惰性特点:
def fuc1(n,i): return n + idef duoxing(): for i in range(4): yield ig = duoxing()for n in [1,10]: g = (fuc1(n,i) for i in g) 关键点:因为生成器不取值(不打印),就永远不去运算,不运算,但for不停, 所以最后 g = (fuc1(n,i) for i in (fuc1(n,i) for i in duoxing())) print(list(g)) 直接把 n = 10 带入计算# [20, 21, 22, 23]
生成器取值,取了就没了:
def fuc1(n,i): return n + idef duoxing(): for i in range(4): yield ig = duoxing()for n in [1,10]: g = (fuc1(n,i) for i in g) print(list(g)) 第一次取值了,g生成器就没了 不管循环多少次,只有第一次有值,取空了,后面都是空列表#[1, 2, 3, 4]#[]
==
一个生成器如下:
def cloth(): for i in range(100): yield ‘衣服%’%ig = cloth()
取值:
for i in g: print(i)#等价于 for i in range(100): print(g.__next__()) #循环触发打印100次
for 自动触发可迭代g内部next方法 想当于第二个手动触发
为什么用for 是因为 next() 和send()有可能遇到取完再取的报错
而for循环取完就停止了
示例:文件的监视,监听文件的变化
def tail(): f = open('文件','r',encoding='utf-8') f.seek(0,2) #把光标定位到文件末尾 while True: line = f.readline() #从光标的位置读 if line: #line如果有内容 True yield line import time time.sleep(0.1)g = tail()for i in g: print(i.strip())##注意 文件输入一定要保存 要不然显示不出来
send()方法
__send__() 可以写成send()
__next__() 可以写成next()def f1(): a = 1 b = yield a #执行到yield a 停止 返回a = 1 *** send传值,想当于把yield a 替换 , 变成b = 5 ,继续往下走 yield b #定位到yield b 又停止,返回 b = 5g = f1()print(next(g))print(g.send(5))#1#5
send 想当于next的同时传参( send(None) == next() ) 把生成器里面yield及后面的值整体替换成传入的参数
send不能 用在第一个触发生成器
next + send 数量和 = yield 数量
预激活装饰器
正常情况,需要用next()方法取值一次,才能用send()方法
#每传一个数,求每次传入后的平均值def average(): sum = 0 count = 0 aver_age = None while True: count += 1 a = yield aver_age sum += a aver_age = sum/countg_aver = average()print(g_aver.__next__())print(g_aver.send(10))print(g_aver.send(20))print(g_aver.send(30))#10#15#20
可以利用装饰器,把第一个next()方法放到装饰器里面
def init(func): def inner(*args,**kwargs): g = func(*args,**kwargs) next(g) return g #这个地方一定要返回一个生成器 return inner@initdef average(): sum = 0 count = 0 aver_age = None while True: count += 1 a = yield aver_age #yield后面放什么都行,这个主要是只要一个yield,来返回aver_age 的值 sum += a aver_age = sum/countg_aver = average() #得到生成器,并赋值给g_aver,这一步不能省略,如果写成print(average.send(10)),想当于每次都重新调用average函数# print(g_aver.__next__()) #这一步放到装饰器函数里面,预激活print(g_aver.send(10))print(g_aver.send(20))print(g_aver.send(30))
yield from
代替for 循环
for i in a:
yield i
for i in a: yield i# 等价于yield from a
示例:
a = ‘abcd’ 依次拿到‘a','b','c','d'def func(): a = 'abcd' for i in a: #这 yield i #两句g = func()print(list(g))# ['a', 'b', 'c', 'd']------def func(): a = 'abcd' yield from a #这一句g = func()print(list(g))# ['a', 'b', 'c', 'd']