zoukankan      html  css  js  c++  java
  • 生成器详解

    生成器(generator)

    什么是生成器

    生成器是一种特殊的迭代器,生成器实现了迭代器协议__iter__(),__next__()

    生成器解决什么问题

    如果有一亿的数据要我们处理,我们通过列表的方式来访问的话,这一亿的数据是存放在内存的,这样会非常的消耗内存的,但是如果我们使用生成器的话,每当处理一个数据的时候,内存中只是相当于存了一个数据,这样可以节省大量的内存

    简单案例

    当生成器对象__next__()的时候,生成器函数会执行到下一个yield,并会返回一个参数

    例一

    def zx():
        yield 1
        print(1)
    
    if __name__ == '__main__':
        zx=zx()
        data=zx.__next__()
        print(data)
    
    1
    

    例二

    触发StopIteration异常的两种方式

    1.迭代完最后一个元素后,触发StopIteration异常

    def zx():
        yield 1
        print(1)
    
    if __name__ == '__main__':
        zx=zx()
        data=zx.__next__()
        data=zx.__next__()
    
    1
    Traceback (most recent call last):
      File "C:/Users/Administrator/Desktop/01python/研究/生成器/t1.py", line 8, in <module>
        data=zx.__next__()
    StopIteration
    

    2.运行生成器函数的时候遇到return,return的值,会成为异常的说明值,如例子的2

    def zx():
        yield 1
        return 2
        print(1)
        yield 3
    
    if __name__ == '__main__':
        zx=zx()
        data=zx.__next__()
        data=zx.__next__()
    
    Traceback (most recent call last):
      File "C:/Users/Administrator/Desktop/01python/研究/生成器/t1.py", line 10, in <module>
        data=zx.__next__()
    StopIteration: 2
    

    迭代器对象和生成器的产生区别

    迭代器对象是通过可迭代对象的__iter__()生成的

    zx=[1,2,3,4,5,6,7,8,9]
    z1=zx.__iter__()
    

    生成器的创建方式类似生成对象的方式

    def zx():
        for i in range(10):
            yield i
    z1=zx()
    

    send方法

    协程的实现主要就是靠的生成器的send方法

    例子1-错误使用

    def dog():
        print('小乌')
        while True:
            food = yield
            if food == '骨头':
                yield '好吃'
            else:
                yield '旺旺旺'
    xw = dog()    #只是用于返回一个生成器对象,函数并不会执行下去
    print(xw.send('骨头'))
    

    出现错误,不能给刚创建的生成器发送非空的值

    Traceback (most recent call last):
      File "C:/Users/Administrator/Desktop/01python/研究/生成器/t5_send.py", line 10, in <module>
        print(xw.send('骨头'))
    TypeError: can't send non-None value to a just-started generator
    

    报错信息说不能发送非空的值,那我们来试试发送一个None会发生什么

    print(xw.send(None))
    

    执行成功了

    小乌
    None
    

    其实也可以用__next__()也能做到这个打印结果

    print(xw.__next__())
    

    结果一样

    小乌
    None
    

    结论

    1.__next__()的效果其实和send(None)一样

    2.yield默认返回None

    例子2-正确使用

    def dog():
        print('小乌')
        while True:
            food = yield
            if food == '骨头':
                yield '好吃'
            else:
                yield '旺旺旺'
    xw = dog()    #只是用于返回一个生成器对象,函数并不会执行下去
    print(xw.__next__())
    print(xw.send('骨头'))
    

    结果

    小乌
    None
    好吃
    

    结论

    1.当生成器刚创建完成,第一次使用先next或者send(None),不能直接send(非空参数)

    2.send()有给yield的赋值功能

    总结

    1.__next__()的效果其实和send(None)一样

    2.当生成器刚创建完成,第一次使用先next或者send(None),不能直接send(非空参数)

    3.send()方法就相当于__next__()和赋值功能的结合

    生成器实现计数器、协程、斐波那契数列

    计数器

    def jishu():
        i = 0
        while True:
            zx = yield i
            if zx == "按一下":
                i+=1
            elif zx == "重置":
                i=0
    js=jishu()
    print(js.__next__())
    print(js.send("按一下"))
    print(js.send("按一下"))
    print(js.send("按一下"))
    print(js.send("按一下"))
    print(js.send("重置"))
    print(js.send("按一下"))
    print(js.send("按一下"))
    
    0 初始化
    1 按一下
    2
    3
    4
    0 重置
    1
    2
    

    协程

    吃包子

    def consumer(name):
        print(f"{name}老板上包子")
        while True:
            baozi = yield
            print(f"{name}:吃了{baozi}")
    
    def producer():
        for i in range(2):
            print('厨师做了两个包子')
            c1.send(f"肉包{i}")
            c2.send(f"菜包{i}")
    
    c1 = consumer("小黄")
    c2 = consumer("小乌")
    c1.__next__()
    c2.__next__()
    
    producer()
    
    小黄老板上包子
    小乌老板上包子
    厨师做了两个包子
    小黄:吃了肉包0
    小乌:吃了菜包0
    厨师做了两个包子
    小黄:吃了肉包1
    小乌:吃了菜包1
    

    斐波那契数列

    def zx(n):
        z,x=0,1
        while x < n:
            yield x
            z,x = x,z+x
    z=zx(10)
    for i in z:
        print(i)
    
  • 相关阅读:
    如何把python最小化安装在客户机上面
    c++的历史-异常
    插件系统的构建
    ora-01445:无法从不带保留关键字的表的连接视图中选择ROWID或采样
    转--Oracle 审计和测试操作
    foreach属性-动态-mybatis中使用map类型参数,其中key为列名,value为列值
    C语言中 *.c和*.h文件的区别!
    解决方法:loadrunner 场景下执行webservice脚本是---报错10492 Error: Exception was raised when calling per-process-init function in extens
    NoSQL之Redis高级实用命令详解--安全和主从复制
    Expect安装方法
  • 原文地址:https://www.cnblogs.com/zx125/p/11613987.html
Copyright © 2011-2022 走看看