python迭代器和生成器
迭代器和生成器
迭代器
迭代的概念
迭代就是重复的过程,每重复一次就是一次迭代,并且每次迭代的结果作为下一次迭代的初始值。
#不是迭代,只是重复
while True:
p = input('>>:')
print p
#迭代,每次循环基于上一次的返回值
l = [1,2,3,4]
t = 0
while t < len(l):
print(l[t])
t += 1
可迭代对象
为了提供一种不依赖于索引的迭代方式,python会为一些对象内置
__iter__方法。python中,字符串、列表、元组、字典、集合、文
件都是可迭代对象。
判断是否为可迭代对象可以导入Iterable模块__iter__
from collections import Iterable
f = open('a.txt','w')
f.__iter__()
# 下列数据类型都是可迭代的对象
print(isinstance('abc',Iterable)) # 字符串
print(isinstance([1,2,3],Iterable)) # 列表
print(isinstance({'a':1,},Iterable)) # 字典
print(isinstance({1,2,3},Iterable)) # 集合
print(isinstance((1,2,),Iterable)) # 元组
print(isinstance(f,Iterable)) # 文件
# 输出:
True
True
True
True
True
True
迭代器
判断是否是迭代器导入Iterator模块:
from collections import Iterable,Iterator
f = open('a.txt','w')
# 只有文件是迭代器对象
print(isinstance('abc',Iterator))
print(isinstance([],Iterator))
print(isinstance((),Iterator))
print(isinstance({'a':1},Iterator))
print(isinstance({1,2},Iterator))
print(isinstance(f,Iterator))
# 输出:
False
False
False
False
False
True
从上面可以看出只要文件是迭代器对象。迭代器对象具有__iter__方法和__next__方法。上面讲的可迭代对象obj.__iter__()得到的结果就是迭代器。
迭代器的优点:
-
提供了一种不依赖于索引的取值方式
-
惰性计算。节省内存
迭代器的缺点:
-
取值不如按照索引取值方便
-
一次性的。只能往后走不能往前退
-
无法获取长度
迭代器取值
# for循环取值
l = ['tom',12,'jack',16]
i = l.__iter__() # i此时成为了迭代器对象
for k in i:
print(k)
#执行结果:
tom
12
jack
16
# __next__方法取值
l = ['tom',12,'jack',16]
i = l.__iter__()
print(i.__next__())
print(i.__next__())
print(i.__next__())
print(i.__next__())
#执行结果:
tom
12
jack
16
使用__next_()取值时,迭代到最后会抛出StopIteration的异常,for循环可以捕捉StopIteration异常来终止迭代。如果使用while循环可以使用如try ... except ...
while True:
try:
k = i.__next__() #也可以直接使用k = next(i)
print(k)
except StopIteration:
break
扩展enumrate()
enumerate()方法生成的也是迭代器对象
l=[2,3,4]
i=enumerate(l,1)
print(i)
for k in i:
print(k)
#执行结果
<enumerate object at 0x000001291540B318>
(1, 2)
(2, 3)
(3, 4)
生成器
生成生成器(generator)
>>> g = (x * x for x in range(5))
>>> g
<generator object <genexpr> at 0x000002C1CDF4D728>
上述中g就是一个生成器,生成器是迭代器对象,可以迭代取值:
g = (x * x for x in range(5))
print(g.__next__)
print(g.__iter__)
for i in g:
print(i)
#执行结果:
<method-wrapper '__next__' of generator object at 0x00000202224A1D00>
<method-wrapper '__iter__' of generator object at 0x00000202224A1D00>
0
1
4
9
16
上面讲了通过表达式子生成生成器,下面讲述下生成器函数,生成器函数也是生成器,也是迭代器对象。
def foo():
print('first')
yield 1
print('second')
yield 2
print('third')
yield 3
g = foo()
for i in g:
print(i)
#执行结果
first
1
second
2
third
3
简单分析上面执行,应该是每次迭代生成器函数foo()就执行一次函数,不过是遇到yield关键字就返回yield后面的值,直到没有yield关键字就抛出StopIteration异常。
yield的功能
-
与return类似,都可以返回值,但不一样的地方在于函数遇到return就退出函数不执行以后的内容,而yield返回值后不会退出,如果有下个yield就会执行到下个yield直到没有yiel。
-
为函数封装好了__iter__和__next__方法,把函数的执行结果做成了迭代器
-
遵循迭代器的取值方式obj.__next__(),触发的函数的执行,函数暂停与再继续的状态都是由yield保存的
详细分析生成器函数执行流程:
def foo():
print('in the foo ...')
food = yield '您好'
print('food >>>',food)
print('我在后面')
food1= yield '你坏'
print('food1 >>> ',food1)
g= foo()
res = next(g)
print(res)
res1 = g.send('x')
print(res1)
##res2= g.send('xx')
'''
生成器执行流程:
1.g=foo(),只是将foo()生成器对象赋值给g,然后继续往下执行;
2.遇到next()或g.send('x')就开始执行foo()内部的代码,
执行遇到第一个yield时,就暂停(我也理解为进入休眠状态),
并将yield后面的值返回给next(g),并跳出到foo()外面next(g)所在的那一行,
将yield返回的值赋值给res
3.res接收yield返回给next(g)的值,然后往下执行代码,打印res的值;
4.当再次遇到next()或g.send('x')时,唤醒foo()继续从上次
暂停的位置开始执行, 同时将g.send(‘x’)中的'x'发送
给第一个yield,并赋值给food,然后继续往下执行;
5.当遇到第二个yield时,进入暂停(休眠),
同时将yield后面的值返回给g.send('x'),
跳出到g.send('x')所在的那一行,并将yield返回的值赋值给res1,
然后继续执行至结束。
注意:
print(res1)后面没有代码了,此时foo()中的food1是空,
如果print(res1)后面再出现g.send('xx')代码,
才会将'xx'发送给第二个yield,并赋值给food1;
但是,foo()内部会从第二个yield那一行继续往下执行,
如果后面没有yield关键字了,程序就会抛出一个StopIteration异常。
'''
简单应用
模拟linux的tail -f file|grep 命令:
这个例子在书籍当中比较经典:
import time
file = r'...a.txt'
def tail(file):
with open(file,'r',encoding='utf-8') as f:
f.seek(0,2) #先将光标移动到文件末尾
while True:
line = f.readline()
if line:
yield line
else:
time.sleep(0.5)
def grep(line,pattern): #line传入tail执行后的参数,pattern就是grep的关键词语
for i in line:
if pattern in i:
yield i
def print_content(cmd): #传入生成器参数,迭代打印内容
for k in cmd:
print(k,end='')
print_content(grep(grep(tail('a.txt'),'error'),'404')) #传入参数,类似tail -f a.txt|grep 'error'|grep '404'