zoukankan      html  css  js  c++  java
  • 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'
  • 相关阅读:
    JVM
    事务
    Spring中AutowireMode(自动装配模型)
    ImportAware应用
    spring中几个比较重要的扩展点
    动态代理在Spring中的应用
    基于ImportSelector模拟简单的Aop
    正则表达式分组(Grouping)
    正则表达式断言(Assertions)
    一个JSON解析器
  • 原文地址:https://www.cnblogs.com/liao-lin/p/7029605.html
Copyright © 2011-2022 走看看