迭代器和生成器
迭代器
能被for循环的数据类型 list # 列表 dict # 字典 str # 字符 set # 集合 tuple # 元组 f = open() # 文件句柄 range() enumerate # 枚举
查看数据类型含有什么方法 使用dir()
print(dir([]))
双下方法:__方法名__()
print(dir([])) 查看方法
print('__add__' in dir([])) #判断双下方法__add__是否在列表方法内
print([1].__add__([2])) # 等同于print([1]+[2])
print([1] + [2]) # 实际运行的方法是print([1].__add__([2]))
可迭代对象:
只要数据类型含有__iter__方法的的数据类型都是可迭代对象
只要都能被for循环的都是可迭代对象
迭代器
当可迭代对象使用了__iter__方法后,它的返回值就是一个迭代器
只要是迭代器 一定可迭代
print(dir([])) print(dir([].__iter__()))
迭代器必须同时包含__iter__和__next__两个方法
__next__()方法可以一个一个的获取值 # for循环其实就是在使用迭代器
print(set(dir([].__iter__())) - set(dir([]))) # 例子 l = [1, 2, 3] print(l.__iter__().__next__()) # 将l 列表通过__iter__()转为迭代器,接着通过__next__可以取一个值 print(l.__iter__().__next__()) # 将l 列表通过__iter__()转为迭代器,接着通过__next__可以取下一个值 print(l.__iter__().__next__()) # 将l 列表通过__iter__()转为迭代器,接着通过__next__可以取下一个值 print(l.__iter__().__next__()) # 因列表已经没有第四个值,直接报错
# 模拟for循环
l = [1,2,3,4,5,6,7,8,9]
a = iter(l) # 将l 转为为迭代器
print('__iter__'and '__next__' in dir(a))
while True:
print(a.__next__())
可迭代协议:只要含有__iter__方法的都是可迭代的
迭代器协议:内部含有__iter__和__next__方法的迭代器
迭代器的好处:
1、从容器类型中一个一个的取值,会把所有的值都取到;
2、节省内存空间 :不会一次性产生所有值,仅通过循环或__next__(),一次取一个
print(range(100000000)) print(list(range(100000000)))
注意:迭代器很好,但是并不能解决所有的,比如我需要生成200W个不同的字符串,于是引出生成器
生成器
生成器的本质就是一个迭代器
生成器函数
1、只要含有yield关键字的函数都是生成器函数,只能用在函数内且不能和return同时存在
2、且执行完yield后这个函数不会结束,可以用__next__接着取值
3、调用函数的时候,函数体不执行,返回一个生成器
4、调用next方法的是会取到一个值,直到完全取完,再执行__next__()就会报错
def get(): for i in range(200): yield i ret = get()
print(ret)
print('__iter__'and '__next__' in dir(ret))
while True:
print(ret.__next__()) #for i in ret: # print(i)
生成器函数的取值方式:
1、__next__()
2、for
3、数据类型的强制转换 list(ret) 占用内存
注意:因为yield关键字,返回值后函数并没有结束,所以可以接着往下用__next__()取值,直到报错
def generator(): print(1) yield 'a' print(2) yield 'b' print(3) yield 'c' g = generator() ret = g.__next__() print(ret) ret = g.__next__() print(ret) ret = g.__next__() print(ret) ret = g.__next__() # 会报错 因为只有3个yield print(ret) #for i in g: # print(i)
注意:解决---我需要生成200W个不同的字符串
def test(): for i in range(200): yield '第%s个 Hello'%i g = test() for i in g: print(i)
问题的进阶:仅需要前50个,接着取51个能否取到?
def test(): for i in range(200): yield '第%s个 Hello'%i g = test() count = 0 for i in g: count += 1 print(i) if count >50: break print(g.__next__())
注意:每对新的变量赋予一次生成器函数调用,不于之前的有任何关联
def test(): for i in range(200): yield '第%s个 Hello'%i g = test() g1 = test() count = 0 for i in g: count += 1 print(i) if count >50: break print(g.__next__()) print(g1.__next__())
例子:监听文件输入
# 注意文件里输入内容后,保存一下,不然不会实时输出 def genternor(filename): with open(filename,encoding='utf-8') as f : while True: line = f.readline().strip() if line: print(line) genternor('count')
问题进阶:如发现输入有Python,实时输出内容+‘******’,或者仅输出带Python的字符串
达到监听过滤的效果
def genternor(filename): with open(filename,encoding='utf-8') as f : while True: line = f.readline().strip() if line: yield line ret = genternor('count') for i in ret: if 'python' in i : print('*****',i) else: print(i) #仅打印带python的字符串 # for i in ret: # if 'python' in i : # print(i)
生成器的进阶----send()方法
def gennerator(): print(123) yield 1 print(456) yield 2 print(789) g = gennerator() ret = g.__next__() print('****',ret) # ret = g.__next__() # print('****',ret) ret = g.send(None) # send的效果和next一样 print('****',ret)
send()方法:格式:send(数据)
send 获取下一个值的效果和__next__()基本一致,只是在获取下一个值的时候,给上一个yield的位置传一个数据
注意:使用send之前,必须先使用__next__()获取第一个yield的值,且注意最后一个yield不能接收外部的值
def gennerator(): print(123) content = yield 1 print('---',content) print(456) yield 2 print(789) g = gennerator() # 调用函数,变量到一个生成器 ret = g.__next__() # 执行__next__()方法,执行到yield 1 就直接返回了,并没有给content进行赋值 print('****',ret) # 所以这里仅打印123 ,1 # ret = g.__next__() # print('****',ret) ret = g.send('数据') # sent 开始从__next__()停留的位置开始接着执行,将括号内的数据传到停留的位置,接着对content赋值content=‘数据’ print('****',ret) # 接着执行到yield 2 后停止
yield from
在一个容器类型里取值,不用一个个返回,用yield from可有一次性返回
def generator(): a = 'abcdef' b = '123456' yield from a yield from b g = generator() for i in g: print(i)
浙公网安备 33010602011771号