• python之迭代器与生成器


    python之迭代器与生成器

    可迭代

    假如现在有一个列表,有一个int类型的12345。我们循环输出。

    list=[1,2,3,4,5]
    for i in list:
        print(i)
    for i in 12345:
        print(i)
    

    结果:

    Traceback (most recent call last):
      File "C:/Pycham/生成器与迭代器/test1.py", line 6, in <module>
        for i in 12345:
    TypeError: 'int' object is not iterable
    1
    2
    3
    4
    5
    
    报错显示:1234不是可以被迭代的。
    
    那python中哪些是可迭代的:字符串、列表、元组、字典、集合。
    
    for循环工作机制:for循环在循环一个对象的时候,会调用这个对象的iter方法,得到迭代器,然后在调用这个迭代器的next方法,去获得这个迭代器中包涵的每个值。
    现在可能我们不太明白,什么是iter方法,什么是迭代器,什么是next方法。

      

    迭代器

    但是如果只是将数据集内的数据“一个挨着一个的取出来,for循环就可以做到,为什么要使用迭代器呢?迭代器是什么?
    迭代器能迭代的一定是可以迭代的数据类型。
    如果我们要使用迭代器,一定要将可以被迭代的数据集转为迭代器,使用__iter__()

      

    #列表生成式
    list=[1,2,3,4,5]
    #生成器
    gen=list.__iter__()
    print(list)
    print(gen)
    
    结果:
    [1, 2, 3, 4, 5]
    <list_iterator object at 0x0000000002627278>
    

     

    迭代器的三个方法

    iter_l = [1,2,3,4,5,6].__iter__()
    #获取迭代器中元素的长度
    print(iter_l.__length_hint__())
    #根据索引值指定从哪里开始迭代
    print(iter_l.__setstate__(2))
    #一个一个的取值
    print(iter_l.__next__())
    

      

    结果:
    6 None 3

     

    循环输出迭代器的内容

    注意:

    很重要的特性,就是不可逆,只能前进,不能后退。

    如果迭代的次数超过里面的数据,就会报错。

    l = [1,2,3,4]
    l_iter = l.__iter__()
    while True:
        try:
            item = l_iter.__next__()
            print(item)
        except StopIteration:
            break
    

      

    总结:一个对象是否可迭代,全都取决于这个对象是否有iter方法,调用对象的iter方法,就回返回一个迭代器,这个迭代器一定具有next方法,在调用这个迭代器的next方法时,迭代器就回返回它的下一个值,当迭代器中没有
    值可以返回了,就回抛出一个名为StopIteration的异常,停止迭代。
    for循环的工作机制,可以让我们遍历任何一个可迭代的数据集。
    虽然序列类型字符串,列表,元组都有下标,你用下标的方式访问。
    但是非序列类型像字典,集合,文件对象这样的数据类型也是可迭代的。
    

      

    生成器

    通过列表生成式,我们可以直接创建一个列表,但是,受到内存限制,列表容量肯定是有限的,而且创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。
    
    所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间,在Python中,这种一边循环一边计算的机制,称为生成器:generator
    
    生成器是一个特殊的程序,可以被用作控制循环的迭代行为,python中生成器是迭代器的一种,使用yield返回值函数,每次调用yield会暂停,而可以使用next()函数和send()函数恢复生成器。
    
    生成器类似于返回值为数组的一个函数,这个函数可以接受参数,可以被调用,但是,不同于一般的函数会一次性返回包括了所有数值的数组,生成器一次只能产生一个值,这样消耗的内存数量将大大减小,而且允许调用函数可以很快的处理前几个返回值,因此生成器看起来像是一个函数,但是表现得却像
    是迭代器。 总结:生成器是个比较特殊的可迭代对象,它与其他的可迭代对象不太一样的地方,其他的可迭代对象需要调用iter方法,返回个迭代器对象,然后通过迭代器对象去执行next方法,获取迭代器中的值,但是生成器直接可以被迭代,无需执行iter方法。

      

      
    生成器Generator:
    
      本质:迭代器(所以自带了__iter__方法和__next__方法,不需要我们去实现)
    
      特点:惰性运算,开发者自定义
    

      

    初始生成器

    import time
    def genrator_fun1():
        a = 1
        print('现在定义了a变量')
        yield a
        b = 2
        print('现在又定义了b变量')
        yield b
    
    g1 = genrator_fun1()
    print('g1 : ',g1)       #打印g1可以发现g1就是一个生成器
    print('-'*20)   #我是华丽的分割线
    print(next(g1))
    time.sleep(1)   #sleep一秒看清执行过程
    print(next(g1))

    python中生成器有两种表达形式

    函数式生成器:在常规的函数中定义的生成器,语句的返回值不再使用return去返回,而是使用yield关键字每次返回一个结果,一个函数中不可以有多个return,但是可以有多个yield,函数中的每一个yield都会返回一个结果,每执行一个yield,函数的执行状态都会被‘挂起’可以理解
    为暂停,下次继续调用这个函数的时候,会从上次挂起的位置继续向下执行。
    def func1():
    
        yield 1
    
        print("第一个yield执行完成~")
    
        yield 2
    
        print("第二个yield执行完成~")
    
        yield 3
    
        print("第三个yield执行完成~")
    
    
    for i in func1():
        print(i)
    

      

    结果:
    1 第一个yield执行完成~ 2 第二个yield执行完成~ 3 第三个yield执行完成~

      

    生成器表达式:使用类似于列表推导式的方法,但是返回的对象不再是一个列表,而是一个可以按需生成结果的一个对象(生成器)。
    只要把一个列表生成式的[]中括号改为()小括号,就创建一个generator
    #列表生成式
    lis = [x*x for x in range(10)]
    print(lis)
    #生成器
    generator_ex = (x*x for x in range(10))
    print(generator_ex)
     
    结果:
    [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
    <generator object <genexpr> at 0x000002A4CBF9EBA0>
    

      

    for循环输出生成器的值

    #生成器
    generator_ex = (x*x for x in range(10))
    for i in generator_ex:
        print(i)
         
    结果:
    0
    1
    4
    9
    16
    25
    36
    49
    64
    81
    
    所以我们创建一个generator后,基本上永远不会调用next(),而是通过for循环来迭代,并且不需要关心StopIteration的错误(迭代超过长度)。
    使用生成器的优点:
    
    延迟计算,一次返回一个结果。也就是说,它不会一次生成所有的结果,这对于大数据量处理,将会非常有用。
    

      

    最后示例:

    import time
    
    def cumtom(name):
        print('%s准备吃包子' %name)
        time.sleep(1)
        while 1:
            count=yield
            print('%s吃到第%d个包子' %(name,count))
    
    def producter():
        con1.__next__()
        con2.__next__()
        n=1
        while 1:
            time.sleep(1)
            print('已经生产出来%d、%d个包子' %(n,n+1))
            #通过send方法通知
            con1.send(n)
            con2.send(n+1)
            n+=2
    
    
    con1=cumtom('cumtom1')
    con2=cumtom('cumtom2')
    producter()
    

      

    cumtom1准备吃包子
    cumtom2准备吃包子
    已经生产出来1、2个包子
    cumtom1吃到第1个包子
    cumtom2吃到第2个包子
    已经生产出来3、4个包子
    cumtom1吃到第3个包子
    cumtom2吃到第4个包子
    已经生产出来5、6个包子
    cumtom1吃到第5个包子
    cumtom2吃到第6个包子
    已经生产出来7、8个包子
    cumtom1吃到第7个包子
    cumtom2吃到第8个包子
    已经生产出来9、10个包子
    cumtom1吃到第9个包子
    cumtom2吃到第10个包子
    已经生产出来11、12个包子
    cumtom1吃到第11个包子
    cumtom2吃到第12个包子
    已经生产出来13、14个包子
    cumtom1吃到第13个包子
    cumtom2吃到第14个包子
    已经生产出来15、16个包子
    cumtom1吃到第15个包子
    cumtom2吃到第16个包子
    

      

  • 相关阅读:
    text-overflow white-space word-break word-wrap word-spacing line-clamp 傻傻分不清楚0.0=>文本超出显示省略号/数字英文字母折行有关css 属性/显示两行,第二行省略号显示css方法
    jq 操作表单中 checkbox 全选 单选
    用 pdf.js兼容部分安卓显示PDF在线预览 时,a标签直接链接参数文件不能含中文的解决办法
    通过form实现enter事件
    小白随笔之数组的方法
    引用类型之Array
    Reset
    js常用事件
    让女朋友能懂的网络技术篇之动态代理
    图论之Dijkstra算法
  • 原文地址:https://www.cnblogs.com/-wenli/p/10487019.html
Copyright © 2020-2023  润新知