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'
原文地址:https://www.cnblogs.com/liao-lin/p/7029605.html