• 迭代器--》生成器--》协程的关系与区别


    1、迭代器(iterator)

            是一个实现了迭代器协议的对象,python的一些内置数据类型(列表,数组,字符串,字典等)都可以通过for语句进行迭代,我们也可以自己创建一个容器,实现了迭代器协议,可以通过for,next方法进行迭代,在迭代的末尾,会引发stopIteration异常。

    判断xxx_obj是否可以迭代
    
    在第1步成立的前提下,调用 iter 函数得到 xxx_obj 对象的 __iter__ 方法的返回值
    
    __iter__ 方法的返回值是一个迭代器
    
    如果想要一个对象称为一个 可以迭代的对象,即可以使用for,必须实现 __iter__方法
    
    __iter__ 中必须返回对象的引用【要这个对象有__iter__和__next__方法, 实际上取的__next__的返回值】
    
    迭代器结束,需要抛出一个 StopIteration 异常。

    2、生成器(generator)【一种特殊迭代器

            1)是通过yield语句快速生成迭代器,可以不用iter和next方法 ;

            2)yield可以使一个普通函数变成一个生成器,并且相应的next()方法返回是yield后的值。一种更直观的解释是:程序执行到yield时会返回结果并暂停,再次调用next时会从上次暂停的地方继续开始执行

            3)生成器自身有构成一个迭代器,每次迭代时使用一个yield返回 的值,一个生成器中可以有多个yield的值

            4)创建生成器的方法:

                  a、()

        

                   b、yield    

    def create_num(all_num):
        # a = 1
        # b = 1
        a, b = 0, 1
        current_num = 0
        while current_num < all_num:
            yield a              # 如果一个函数中有yield语句,那么这个就不在是函数,而是一个生成器的模板
            a, b = b, a + b
            current_num += 1
     
     
    if __name__ == '__main__':
        # 如果在调用create_num的时候,发现这个函数有yield,此时不是调用函数,而是创建一个生成器对象
        obj = create_num(10)
        for num in obj:
            print(num)

            5、send使用--启动生成器

    def create_num(all_num):
        a, b = 0, 1
        current_num = 0
        while current_num < all_num:
            res = yield a
            print(">>>>ret>>>>", res)
            a, b = b, a + b
            current_num += 1
     
     
    if __name__ == '__main__':
        obj = create_num(4)
        # obj.send(None) # send一般不会放到第一次启动生成器,如果非要如此,传递None
        ret = next(obj)
        print(ret)
        ret = obj.send("hhhhh")
        print(ret)
        # send里面的数据,会传递给第5行,当作yield a的结果,然后res保存这个结果..
        # send的结果是下一次调用yield时,yield后面的值
        ret = obj.send(None)
        print(ret)
        ret = obj.send(None)
        print(ret)

           6、yieldreturn区别

                 yield可以暂停函数执行,且下一次执行时候恢复

    3、迭代器和生成器作用

    • 迭代器: 减少内存空间, 能实现循环
    • 生成器: 能让一个函数看上去能暂停执行
    • 都是保证生成数据代码, 不是保存结果

    4、多任务-协程(yield执行)  

      a、生成器函数其实就是实现了协程,即使用yield, send(), next()实现协程

      b、进程占资源最多, 其次线程, 协程占资源最少!

      

    #!/bin/python3
    # -*- coding=utf-8 -*-
     
    import time
    def task_1():
        while True:
            print("------1-------")
            time.sleep(0.1)
            yield 
     
     
    def task_2():
        while True:
            print("------2------")
            time.sleep(0.2)
            yield 
     
     
    def main():
       t1 = task_1()
       t2 = task_2()
       while True:
           next(t1)
           next(t2)
    if __name__ == "__main__":
        main()

      并行: 有两个任务, 但是有四个CPU的核, 一个任务占一个核, 每个都在做

      并发: 有很多任务, 但是只有两个核, 所以 交替执行

      4.1、greenlet实现多任务(核心还是yield)

               

    #!/bin/python3
    # -*- encoding=utf-8 -*-
     
    from greenlet import greenlet
    import time
    def test1():
        while True:
            print("----A----")
            gr2.switch()
            time.sleep(0.5)
    def test2():
        while True:
            print("----B----")
            gr1.switch()
            time.sleep(0.5)
    gr1 = greenlet(test1)
    gr2 = greenlet(test2)
    # 切换到gr1中执行
    gr1.switch()

      4.2、gevent实现协程(更强大,常用)

        

    #!/bin/python3
    # -*-encoding=utf-8-*-
     
    import gevent
    import time 
    def f1(n):
        for i in range(n):
            print(gevent.getcurrent(), i)
            gevent.sleep(0.5)
    def f2(n):
        for i in range(n):
            print(gevent.getcurrent(), i)
            gevent.sleep(0.5)
    def f3(n):
        for i in range(n):
            print(gevent.getcurrent(), i)
            gevent.sleep(0.5)
    print("----1-----")
    g1 = gevent.spawn(f1, 5)
    print("----2-----")
    g2 = gevent.spawn(f2, 5)
    print("----3-----")
    g3 = gevent.spawn(f3, 5)
    print("----4-----")
    g1.join()
    g2.join()
    g3.join()

    gevent遇到延时操作就切换, 利用了等待耗时的操作, 去做其他的事情

    如下: 加入monkey.patch_all()则无须将 time.sleep()改成 gevent.sleep()

    #!/bin/python3
    # -*-encoding=utf-8-*-
     
    import gevent
    from gevent import monkey 
    import time 
    monkey.patch_all()
    def f1(n):
        for i in range(n):
            print(gevent.getcurrent(), i)
            time.sleep(0.5)
    #        gevent.sleep(0.5)
     
    def f2(n):
        for i in range(n):
            print(gevent.getcurrent(), i)
            time.sleep(0.5)
     #       gevent.sleep(0.5)
     
    def f3(n):
        for i in range(n):
            print(gevent.getcurrent(), i)
            time.sleep(0.5)
    #        gevent.sleep(0.5)
     
    print("----1-----")
    g1 = gevent.spawn(f1, 5)
    print("----2-----")
    g2 = gevent.spawn(f2, 5)
    print("----3-----")
    g3 = gevent.spawn(f3, 5)
    print("----4-----")
    g1.join()
    g2.join()
    g3.join()

    如下: 将需要join的代码, 写成列表, 简洁

    #!/bin/python3
    # -*-encoding=utf-8-*-
     
    import gevent
    from gevent import monkey 
    import time 
    monkey.patch_all()
    def f1(n):
        for i in range(n):
            print(gevent.getcurrent(), i)
            time.sleep(0.5)
    #        gevent.sleep(0.5)
     
    def f2(n):
        for i in range(n):
            print(gevent.getcurrent(), i)
            time.sleep(0.5)
     #       gevent.sleep(0.5)
     
    def f3(n):
        for i in range(n):
            print(gevent.getcurrent(), i)
            time.sleep(0.5)
    #        gevent.sleep(0.5)
     
    gevent.joinall([
        gevent.spawn(f1, 5), 
        gevent.spawn(f2, 5), 
        gevent.spawn(f3, 5)
        ])  

      4.3并发下载器

    #!/bin/python3
    #-*- encoding=utf-8 -*-
     
    import gevent 
    import urllib.request 
    from gevent import monkey
    monkey.patch_all()
    def downloader(img_name, img_url):
        req = urllib.request.urlopen(img_url)
        img_content = req.read()
          
        with open("./img/"+ img_name, "wb") as f:
            f.write(img_content)
    def main():
        gevent.joinall([
                gevent.spawn(downloader, "1.jpg", 'https://rpic.douyucdn.cn/asrpic/190417/5440020_3968619_65b10_2_2142.jpg'),
                gevent.spawn(downloader, '2.png', "https://rpic.douyucdn.cn/asrpic/190417/594613_2143.png")
            ])  
    if __name__=="__main__":
        main()

    5、进程/线程/协程对比

    • 进程: 耗费资源最多, 进程里一定有一个线程, 默认线程称为主线程。进程是资源分配的单位。(最稳定, 耗费资源最多)

    • 线程线程是操作系统调度的单位. 线程切换需要的资源一般, 效率一般 (不考虑GIL情况) 

    • 协程: 协程切换任务资源很小, 效率高;

      • 特点: 在等待某个资源到来 期间, 去执行其他代码....多线程里有很多网络堵塞, 推荐先用协程 !

    • 多进程、多线程根据cpu核数不一样 可能是并行的, 但是协程是在一个线程中, 所以是并发的!
  • 相关阅读:
    面试题
    网络编程
    python_控制台输出带颜色的文字方法
    httpie 101
    JSON Web Signature 规范解析
    Kong 系列 -- Kong 101
    关于过渡机制的一点理解
    XAML概览 1(译自JeremyBytes.com)
    awk与sed简明教程
    Connection failed: NT_STATUS_ACCOUNT_RESTRICTION
  • 原文地址:https://www.cnblogs.com/liugp/p/11074917.html
Copyright © 2020-2023  润新知