zoukankan      html  css  js  c++  java
  • Python复习笔记(八)迭代器和生成器和协程

    1. 迭代器

    1.1 可迭代对象

    • 判断xxx_obj是否可以迭代

    • 在第1步成立的前提下,调用 iter 函数得到 xxx_obj 对象的 __iter__ 方法的返回值

    • __iter__ 方法的返回值是一个迭代器

    • 如果想要一个对象称为一个 可以迭代的对象,即可以使用for,必须实现 __iter__方法

    • __iter__ 中必须返回对象的引用【要这个对象有__iter____next__方法, 实际上取的__next__的返回值】

    • 迭代器结束,需要抛出一个 StopIteration 异常。

    from collections import Iterable
    from collections import Iterator
    import time
    
    
    class Classmate(object):
        def __init__(self):
            self.names = list()
    
        def add(self, name):
            self.names.append(name)
    
        def __iter__(self):
            # 如果想要一个对象称为一个 可以迭代的对象,即可以使用for,必须实现 __iter__方法
            return ClassIterator(self)    # 必须返回 
    
    
    class ClassIterator(object):
    
        def __init__(self, obj):
            self.obj = obj
            self.current_num = 0
    
        def __iter__(self):
            pass
    
        def __next__(self):
            if self.current_num < len(self.obj.names):
                ret = self.obj.names[self.current_num]
                self.current_num += 1
                return ret
            else:
                raise StopIteration
    
    classmate = Classmate()
    
    
    classmate.add("王1")
    classmate.add("李2")
    classmate.add("张3")
    
    # print("classmate是否是可以迭代的对象: ", isinstance(classmate, Iterable))
    # classmate_iterator = iter(classmate)
    # print("classmate_iterator是否是迭代器: ", isinstance(classmate_iterator, Iterator))
    
    # iter
    # print(next(classmate_iterator))
    #
    for name in classmate:
        print(name)
        time.sleep(1)

    王1
    李2
    张3

    1.2 调用自己的__next__方法

    from collections import Iterable
    from collections import Iterator
    import time
    
    
    class Classmate(object):
        def __init__(self):
            self.names = list()
            self.current_num = 0
    
        def add(self, name):
            self.names.append(name)
    
        # 判断是否可以迭代
        def __iter__(self):
            # 如果想要一个对象称为一个 可以迭代的对象,即可以使用for,必须实现 __iter__方法
            return self      # 调用返回对象的__next__方法(这里调用自己的__next__方法
    
        def __next__(self):
            if self.current_num < len(self.names):
                ret = self.names[self.current_num]
                self.current_num += 1
                return ret
            else:
                raise StopIteration   # 停止迭代
    
    
    classmate = Classmate()
    
    classmate.add("王1")
    classmate.add("李2")
    classmate.add("张3")
    
    # print("classmate是否是可以迭代的对象: ", isinstance(classmate, Iterable))
    # classmate_iterator = iter(classmate)
    # print("classmate_iterator是否是迭代器: ", isinstance(classmate_iterator, Iterator))
    
    # iter
    # print(next(classmate_iterator))
    #
    for name in classmate:
        print(name)
        time.sleep(1)

    王1
    李2
    张3

    生成斐波那契数列

    class Fibnacci(object):
        def __init__(self, all_num):
            self.all_nums = all_num
            self.current_num = 0
            self.a = 0
            self.b = 1
    
        def __iter__(self):
            return self
    
        def __next__(self):
            if self.current_num < self.all_nums:
                ret = self.a
    
                self.a, self.b = self.b, self.a+self.b
                self.current_num += 1
    
                return ret
            else:
                raise StopIteration
    
    
    fibo = Fibnacci(10)
    
    for num in fibo:
        print(num)

    2. 生成器--一种特殊迭代器

    2.1 创建生成器1:()

    2.2 创建生成器2: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)

    注意:yield的工作流程

    2.3 两个生成器之间没有影响

    2.4 通过异常判断生成器已结束

    2.5 生成器获得return的值

     

    2.6 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)

     注意:send不要放第一次

     

    2.7 yield和return区别

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

    2.8 迭代器和生成器作用

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

    生成器(yield): 实现多任务 !

    3. 多任务-协程(yield执行)

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

    #!/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. 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()

     5. 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)
        ])  

    6. 并发下载器

    #!/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()

    7. 进程/线程/协程对比

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

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

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

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

    • 多进程、多线程根据cpu核数不一样 可能是并行的, 但是协程是在一个线程中, 所以是并发的!

  • 相关阅读:
    为什么要用webUI?
    探索WebKit内核(一)------ 菜鸟起步
    主进程退出的时候,杀死所有子进程
    那两年炼就的Android内功修养
    飞鸽---局域网聊天软件攻防战
    如何利用Fluxion诱惑目标用户获取WPA密码
    性能测试:CPU内存,硬盘IO读写,带宽速度,UnixBench
    解决maven编译错误:程序包com.sun.xml.internal.ws.spi不存在
    Apache-Flink深度解析-DataStream-Connectors之Kafka
    linux下find(文件查找)命令的用法总结
  • 原文地址:https://www.cnblogs.com/douzujun/p/10502984.html
Copyright © 2011-2022 走看看