zoukankan      html  css  js  c++  java
  • Python3 协程

    首发地址

    迭代是访问集合元素的一种方式。迭代器是一个可以记住遍历的位置的对象。迭代器对象从集合的第一个元素开始访问,直到所有元素被访问完结束。迭代器只能往前不会后退。

    判断是否可迭代

    In [1]: from collections.abc import Iterable
    
    In [2]: isinstance("abc",Iterable)
    Out[2]: True
    
    In [3]: isinstance([1,2,3],Iterable)
    Out[3]: True
    
    In [4]: isinstance(123,Iterable)
    Out[4]: False
    

    返回为True即为可迭代

    迭代器

    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
        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")
    for i in classmate:
        print(i)
    

    迭代器的应用(斐波那契数列)

    class Fibonacci(object):
        def __init__(self,max_num):
            self.max_num = max_num
            self.current_num = 0
            self.front = 0
            self.after = 1
        def __iter__(self):
            return self
        def __next__(self):
            if self.current_num < self.max_num:
                ret = self.front
                self.front, self.after = self.after, (self.front + self.after)
                self.current_num += 1
                return ret
            else:
                raise StopIteration
    
    fibo = Fibonacci(20)
    for num in fibo:
        print(num)
    

    迭代器的其他使用方法

    并不是只有for循环能够接受可迭代对象,除for循环外,list、tuple等也能接受。

    a = (11,22,33)
    list(a)
    #此处list(a)不是单纯的类型转换,而是首先创建一个空列表,然后调用next方法,一个一个的往列表中添加。
    

    生成器

    生成器是一种特殊的迭代器

    创建生成器

    方法1

    In [1]: nums = [x*2 for x in range(5)]
    
    In [2]: nums
    Out[2]: [0, 2, 4, 6, 8]
    
    In [3]: nums = (x*2 for x in range(5))
    
    In [4]: nums
    Out[4]: <generator object <genexpr> at 0x000001DC00D7AF20>
    

    方法2

    如果一个函数中,存在yield语句,那么它就不是一个函数而是一个生成器

    def create_num(all_num):
        a, b, current_num = 0, 1, 0
        while current_num < all_num:
            yield a
            a, b = b, a + b
            current_num += 1
    
    generator = create_num(100)
    #generator就是一个生成器对象
    for num in generator:
        print(num)
    
    生成器也是一个特殊的迭代器(使用next函数访问)
    def create_num(all_num):
        a, b, current_num = 0, 1, 0
        while current_num < all_num:
            yield a
            a, b = b, a + b
            current_num += 1
        return "ok ..."
    
    generator = create_num(100)
    for i in range(102):
        try:
            print(next(generator))
        except StopIteration as e:
            print(e.value)
            break
    
    send唤醒

    send与next的区别,send可以向生成器中传参

    def create_num(all_num):
        a, b, current_num = 0, 1, 0
        while current_num < all_num:
            ret = yield a
            print(ret)
            a, b = b, a + b
            current_num += 1
        return "ok ..."
    
    generator = create_num(100)
    print(next(generator))
    print(generator.send("传参"))
    

    第一次启动生成器,如果使用send,不能传值。第一次建议使用next,非要使用send,可以传入None这个空值。

    使用yield完成多任务

    import time
    
    def task_1():
        while True:
            print("---1---")
            time.sleep(0.1)
            yield
    
    def task_2():
        while True:
            print("---2---")
            time.sleep(0.1)
            yield
    
    def main():
        t1 = task_1()
        t2 = task_2()
        while True:
            next(t1)
            next(t2)
    
    if __name__ == "__main__":
        main()
    

    使用greenlet完成多任务

    安装greenlet

    pip install greenlet
    

    greenlet

    import time
    from greenlet import greenlet
    
    
    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.switch()
    

    greenlet已经实现了协程,但是还需要人工切换。gevent可以自动切换

    使用gevent完成多任务

    安装gevent

    pip install gevent
    

    简单实现

    import gevent
    
    def f(n):
        for i in range(n):
            print(gevent.getcurrent(),i)
            #只有使用gevent.sleep才会切换
            gevent.sleep(1)
    
    g1 = gevent.spawn(f,5)
    g2 = gevent.spawn(f,5)
    g3 = gevent.spawn(f,5)
    
    g1.join()
    g2.join()
    g3.join()
    

    给程序打补丁

    monkey.patch_all()

    import gevent
    from gevent import monkey
    import time
    
    monkey.patch_all()
    #monkey.patch_all()给程序打补丁,程序当遇到耗时的代码,会换为gevent中自己实现的模块
    
    def f(n):
        for i in range(n):
            print(gevent.getcurrent(),i)
            #只有使用gevent.sleep才会切换
            time.sleep(1)
    
    g1 = gevent.spawn(f,5)
    g2 = gevent.spawn(f,5)
    g3 = gevent.spawn(f,5)
    
    g1.join()
    g2.join()
    g3.join()
    

    简洁写法

    import gevent
    from gevent import monkey
    import time
    
    monkey.patch_all()
    #monkey.patch_all()给程序打补丁,程序当遇到耗时的代码,会换为gevent中自己实现的模块
    
    def f(n):
        for i in range(n):
            print(gevent.getcurrent(),i)
            #只有使用gevent.sleep才会切换
            time.sleep(1)
    
    gevent.joinall([gevent.spawn(f,5),gevent.spawn(f,5),gevent.spawn(f,5)])
    

    案例-图片下载器(利用协程提高速度)

    import gevent
    from gevent import monkey
    import urllib.request
    
    monkey.patch_all()
    #monkey.patch_all()给程序打补丁,程序当遇到耗时的代码,会换为gevent中自己实现的模块
    
    def download_img(url):
        req = urllib.request.urlopen(url)
        with open(url[-8:],'wb') as file:
            file.write(req.read())
    
    def main():
        gevent.joinall([
            gevent.spawn(download_img,"https://assets.ubuntu.com/v1/3887354e-CVE-Priority-icon-High.svg"),
            gevent.spawn(download_img,"https://www.venustech.com.cn/u/cms/www/202106/11174004guxu.png"),
            gevent.spawn(download_img,"https://www.baidu.com/img/flexible/logo/pc/result.png")])
    
    if __name__ == "__main__":
        main()
    

    进程、线程、协程对比

    1. 进程是资源分配的单位
    2. 线程是操作系统调度的单位
    3. 进程切换需要的资源很大,效率很低
    4. 线程切换需要的资源一般,效率一般(不考虑GIL情况下)
    5. 协程切换任务资源很小,效率高
    6. 多进程、多线程根据CPU核数不一样可能是并行,但是协程是在一个线程中 所以是并发
  • 相关阅读:
    Android摄像头採集的视频数据流怎样通过Socket实时发送到目标服务端
    linux c语言 select函数使用方法
    【CODEFORCES】 B. Dreamoon and Sets
    Cocos2d-x 3.0final 终结者系列教程15-win7+vs2012+adt+ndk环境搭建(无Cygwin)
    error MSB8011: 未能注冊输出。请尝试启用“逐用户重定向”
    Web服务(Web Service)相关概念
    HDU 5186 zhx&#39;s submissions (进制转换)
    GPS项目小结
    VS2008 格式化时候乱码 或者 为全为0
    JAX-WS HandlerChain使用具体解释
  • 原文地址:https://www.cnblogs.com/xlcm/p/14963833.html
Copyright © 2011-2022 走看看