python迭代器和生成器

这一生的挚爱 提交于 2019-12-24 12:04:55

迭代器和生成器

迭代器

迭代的概念

迭代就是重复的过程,每重复一次就是一次迭代,并且每次迭代的结果作为下一次迭代的初始值。

#不是迭代,只是重复
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'
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!