一. 迭代器
1.可迭代协议&迭代器协议(Iterable/Iterator)
- 只要是能被for循环的数据类型就一定拥有iter方法
- 【==可迭代协议(Iterable)==】只要含有iter方法的都是可迭代的(就可以被for循环)
- 判断是否含有iter方法(是否可以被for循环)
print('__iter__' in dir(dict)) >>> True
- 判断是否含有iter方法(是否可以被for循环)
- for 循环先去找iter方法,找到后开始遍历,如果没找到就报错
- for循环实际是在使用迭代器
- 可以被for循环的东西
- list
- dict
- str
- set
- tuple
- f = open() #文件
- range()
- enumerate
- (可迭代的东西).__iter__() >>> 迭代器
- 一个列表执行了iter()之后的返回值就是一个迭代器
[].__iter__() #迭代器
- 一个列表执行了iter()之后的返回值就是一个迭代器
- 迭代器包含·的方法有:
#【1】__setstate__ #【2】__next__ #通过next可以从迭代器中一个一个的取值 #【3】__length_hint__ [0,1].__iter__().__length_hint__() #元素个数 >>> 2
- 【==迭代器协议(Iterator)==】内部同时含有next和iter方法的就是迭代器
from collections import Iterator class A: def __iter__(self): pass def __next__(self): pass a = A() print(isinstance(a, Iterator)) >>> True
- 模拟for循环
list1 = [1,2,3,4] iterator = list1.__iter__() while True: print(iterator.__next__())
2.迭代器的好处
- 从容器类型中一个一个的取值,会把所有值都取到
- 节省内存空间
- 迭代器并不会在内存中再占用一大块内存
- 而是随着循环 每次生成一个
- 每次next每次给我一个
range(10000000000) #类似定义,但还未读取占空间 print(list(range(10000000000)))
- 迭代器并不会在内存中再占用一大块内存
二. 生成器(自己写的迭代器)
1.生成器函数(自己写的函数)
- 含有yield关键字的函数,是生成器函数
- 特点:
- 调用函数之后,函数不执行,返回一个生成器
- 每次调用next方法的时候会取到一个值
- 直到取完最后一个,再执行next会报错
- yield不能与return共用,且需要写在函数内部
- return会会结束
- yelid后不会结束
def generator(): #1.定义生成器函数
print(1)
yield 'a'
ret = generator() #2.生成器函数调用generator(),并得到返回值(返回值ret是一个生成器) #3.返回的生成器 >>> ret
print(ret) #>>> <generator object generator at 0x000001BC7CC2A5C8> 说明内部代码未被执行,ret是生成器
print(ret.__next__()) #ret是生成器(迭代器)含有next方法,可调用__next__()
>>>
1
a
[图片上传失败...(image-eb4702-1587608216353)]
- 生成器可阶段性取值(每做一部分就可以取值)
def func():
for i in range(2000000):
yield '取值%s'%i
g = func() #生成器1
g1 = func() #生成器2(新生成器,与生成器1无关)
#从生成器1取50个
count = 0
for i in g:
count+=1
print(i)
if count >50:
break
#从生成器2继续取50个(共取100个)
for i in g:
count+=1
print(i)
if count >100:
break
监听文件输入的例子
def tail(filename):
f = open(filename, encoding='utf-8')
while True:
line = f.readline()
if line.strip(): #如果line.strip()[去掉回车]不为空
yield line.strip() #不能用return,用return只能取到第一行
g = tail('file')
for i in g:
if 'python' in i: #监听过滤/有‘python’就打印,没有就不打印
print(i)
send()的用法
- send() 获取下一个值的效果和next基本一致
- 只是再获取下一个值的时候,给上一个yield的位置传一个数据
- 使用send的注意事项
- 第一次不能用send
- 第一次使用生成器的时候,是用next获取下一个
- 函数中最后一个yield不能接收新的值
def generator():
print(123)
content = yield 1
print('=====',content)
print(456)
yield 2
g = generator()
ret = g.__next__()
print('***',ret)
ret = g.send('hello') #send的效果和next一样
print('***',ret)
>>>
123
*** 1
===== hello
456
*** 2
[图片上传失败...(image-cb1cf0-1587608216353)]
- 函数执行:先执行等号右边,再执行左边
- 执行第一个next的时候,先执行到等号右边的yield,此时还没有赋值给content;下一步send的值发送给yield并赋值给等号左边的content
如果yield后还有要执行的代码
def generator():
print(123)
content = yield 1
print('=====',content)
print(456)
arg = yield 2
''' 代码语句'''
yield #这里yield空
g = generator()
ret = g.__next__()
print('***',ret)
ret = g.send('hello')
print('***',ret)
获取移动平均值
def average():
sum = 0
count = 0
avg = 0
while True:
num = yield avg
sum += num
count += 1
avg = sum/count
avg_g = average()
avg_g.__next__()
avg1 = avg_g.send(10)
avg1 = avg_g.send(20)
'''......'''
print(avg1)
预激生成器的装饰器
def init(func):
def inner(*args,**kwargs):
g = func(*args,**kwargs)
g.__next__()
return g
return inner
@init
def average():
sum = 0
count = 0
avg = 0
while True:
num = yield avg
sum += num
count += 1
avg = sum/count
avg_g = average()
avg1 = avg_g.send(10)
avg1 = avg_g.send(20)
print(avg1)
[图片上传失败...(image-7007f9-1587608216353)]
yield from
- 原代码
def generator():
a = 'abchdhb'
b = '123555'
for i in a:
yield i
for i in b:
yield i
g = generator()
for i in g:
print(i)
- 修改后
def generator():
a = 'abchdhb'
b = '123555'
yield from a
yield from b
g = generator()
for i in g:
print(i)
2.生成器表达式
#【生成器表达式】
g = (i for i in range(10)) #g是生成器
for i in g:
print(i)
#【列表表达式】
list = [i for i in range(10)]
for i in list:
print(i)
- 区别:
- 括号不一样
- 返回的值不一样(生成器表达式几乎不占用内存)
面试题
- 1.生成器中的数据只能取一次,取完就没有了
- 2.惰性运算:不找它取值,它就不工作
面试题1
def demo():
for i in range(4):
yield i
g = demo()
g1 = (i for i in g)
g2 = (i for i in g1)
print(list(g1))
print(list(g2)) #g1的值已经被取完了
>>>
[0, 1, 2, 3]
[]
面试题2: for循环+生成器
def add(n,i):
return n+i
def test():
for i in range(4):
yield i
g = test()
for n in [1,10]:
g = (add(n,i) for i in g)
'''
n = 1
g = (add(n,i) for i in g) #1
n = 10
g = (add(n,i) for i in g) #都未执行,直到print语句才执行,这个时候再找g
>>>
n = 1
g = (add(n,i) for i in g)
n = 10
g = (add(n,i) for i in (add(n,i) for i in g)) #这步的g是1.的g
>>>
n = 1
g = (add(n,i) for i in g)
n = 10
g = (add(n,i) for i in (add(n,i) for i in test()))
>>>
n = 1
g = (add(n,i) for i in g)
n = 10
g = (add(n,i) for i in (add(10,i) for i in test())) #i=0,1,2,3
#g = (add(n,i) for i in (10,11,12,13))
#g = (add(10,i) for i in (10,11,12,13))
'''
print(list(g))
>>>
[20, 21, 22, 23]
def add(n,i):
return n+i
def test():
for i in range(4):
yield i
g = test()
for n in [1,10,5]:
g = (add(n,i) for i in g)
'''
n = 1
g = (add(n,i) for i in test())
n = 10
g = (add(n,i) for i in add(n,i) for i in test())
n = 5
g = (add(n,i) for i in add(n,i) for i in add(n,i) for i in test())
#g = (add(5,i) for i in add(5,i) for i in add(5,i) for i in test())
'''
print(list(g))
识别多个文件中的特殊字段,并返回文件名
- 一个文件夹下,有多个文件,找寻含有'python'字段的文件,并返回文件名
import os
def init(func):
def wrapper(*args,**kwargs):
g=func(*args,**kwargs)
next(g)
return g
return wrapper
@init
def list_files(target):
while 1:
dir_to_search=yield
for top_dir,dir,files in os.walk(dir_to_search):
for file in files:
target.send(os.path.join(top_dir,file))
@init
def opener(target):
while 1:
file=yield
fn=open(file)
target.send((file,fn))
@init
def cat(target):
while 1:
file,fn=yield
for line in fn:
target.send((file,line))
@init
def grep(pattern,target):
while 1:
file,line=yield
if pattern in line:
target.send(file)
@init
def printer():
while 1:
file=yield
if file:
print(file)
g=list_files(opener(cat(grep('python',printer()))))
g.send('/test1')
协程应用:grep -rl /dir
网友评论