迭代器和生成器
迭代器
collections 模块
isinstance(object,classinfo) 内置函数,判断一个对象的变量类型
dir() 当不传参数时,返回当前作用域内的变量、方法和定义的类型列表
当参数对象是模块时,返回模块的属性、方法列表
当参数对象是类时,返回类及其子类的属性、方法列表
当对象定义了__dir__方法,则返回__dir__方法的结果
try....except 为了处理异常,我们使用try...except
try:
pass
except : 我们把可能发生错误的语句放在try模块里,用except来处理异常。except可以处理一个专门的异常,
也可以处理一组圆括号中的异常, 如果except后没有指定异常,则默认处理所有的异常。每一个try,
都必须至少有一个except
http://www.cnblogs.com/Lival/p/6203111.html
==================================
可以把迭代器比喻成一个容器,可以从这个容器中一个接一个的把值取出来,取值的过程就是可迭代的过程
可迭代协议:含有__iter__方法
迭代器协议:含有__iter__方法 且 __next__方法
可迭代对象: 含有__iter__方法
迭代器: 含有__iter__方法和__next__方法 迭代器.__next__ 调用迭代器
(迭代器 是 可迭代对象 的 子集)
第一种判断方法:
判断是不是可迭代:print('__iter__' in dir(对象)) 对象可以是字符串、列表、字典、等等
判断是不是迭代器:print('__next__' in dir(对象))
第二种判断方法:
from collections import Iterable from collections import Iterator print(isinstance(对象,Iterable)) # 判断是不是可迭代的 print(isinstance(对象,Iterator)) # 判断是不是迭代器
例如:
li = [] a = li.__iter__() from collections import Iterator from collections import Iterable print(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 <module> # 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 2 g = 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 2 g = 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 + i def duoxing(): for i in range(4): yield i g = 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 + i def duoxing(): for i in range(4): yield i g = 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 ‘衣服%’%i g = 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 = 5 g = 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/count g_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 @init def 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/count g_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']