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'
  • 相关阅读:
    Verilog非阻塞赋值的仿真/综合问题 (Nonblocking Assignments in Verilog Synthesis)上
    异步FIFO结构及FPGA设计 跨时钟域设计
    FPGA管脚分配需要考虑的因素
    An Introduction to Delta Sigma Converters (DeltaSigma转换器 上篇)
    An Introduction to Delta Sigma Converters (DeltaSigma转换器 下篇)
    中国通信简史 (下)
    谈谈德国大学的电子专业
    中国通信简史 (上)
    Verilog学习笔记
    Verilog非阻塞赋值的仿真/综合问题(Nonblocking Assignments in Verilog Synthesis) 下
  • 原文地址:https://www.cnblogs.com/liao-lin/p/7029605.html
Copyright © 2011-2022 走看看