美文网首页Python学习程序员
Python学习笔记十四(多任务、协程、迭代器、生成器)

Python学习笔记十四(多任务、协程、迭代器、生成器)

作者: DragonFangQy | 来源:发表于2018-05-01 09:24 被阅读8次

    迭代[1]

    什么是迭代

    遍历取值的过程叫做迭代。

    可迭代对象[2]

    可以被for循环遍历取值的对象叫做可迭代对象。包括字符串(str)、列表(list)、元组(tuple)、字典(dict)、集合(set)、range等。

    自定义可迭代对象

    需要在类里面提供iter方法,然后此类的实例对象就是可迭代对象

    
    from collections import Iterable
    
    
    class CustomClass(object):
        def __iter__(self):
            pass
    
    
    def main():
        """入口函数"""
        custom = CustomClass()
    
        # 判断CustomClass类的实例对象是不是可迭代对象
        print("CustomClass类的实例对象是不是可迭代对象: ", isinstance(custom, Iterable))
    
    
    if __name__ == '__main__':
        main()
        
    #运行结果:
    #CustomClass类的实例对象是不是可迭代对象:  True
    
    
    

    魔法方法__iter()__ 需要一个返回值,否则对custom 进行遍历 报错

    
    for i in custom: 
            print(i)
    
    # 运行结果:
    # Traceback (most recent call last):
    #   File "/home/python/Desktop/day/mode1.py", line 22, in <module>
    #     main()
    #   File "/home/python/Desktop/day/mode1.py", line 16, in main
    #     for i in custom:
    # TypeError: iter() returned non-iterator of type 'NoneType'
    
    

    需要一个迭代器对象。

    自定义迭代器对象

    迭代器类需要提供__iter()__ 和 __next()__

    
    from collections import Iterable
    
    from collections import Iterator
    
    
    class CustomClass(object):
        def __init__(self):
            super(CustomClass, self).__init__()
            # 提供存储数据的列表
            self.my_list = list()
    
        # 添加信息
        def append_item(self, item):
            self.my_list.append(item)
    
        # 提供可迭代对象方法
        def __iter__(self):
            # 可迭代对象之所以能够把数据迭代出来是通过迭代器完成的
            # 可迭代对象的本质是:通过迭代器把数据依次迭代出来
            custom_iterator = CustomIterator(self.my_list)
            result = isinstance(custom_iterator, Iterator)
            print("custom_iterator是否是迭代器:", result)
            return custom_iterator
    
    
    # 自定义迭代器对象: 在类里面提供__iter__和__next__的方法创建的对象就是迭代器
    # 迭代器的作用:记录数据的位置以便获取下一个位置的值
    class CustomIterator(object):
        def __init__(self, my_list):
            self.my_list = my_list
            # 下标默认是0
            self.current_index = 0
    
        def __iter__(self):
            return self
    
        # 获取迭代器中下一个值
        def __next__(self):
            if self.current_index < len(self.my_list):
                result = self.my_list[self.current_index]
                self.current_index += 1
                return result
            else:
                # 表示下标越界, 抛出停止迭代的异常
                raise StopIteration
    
    
    def main():
        """入口函数"""
        custom = CustomClass()
    
        # 添加数据
        custom.append_item(1)
        custom.append_item(2)
        custom.append_item(3)
    
        # for 循环内部自动捕获了StopIteration 异常
        for i in custom:
            print(i)
    
    
    if __name__ == '__main__':
        main()
    
        # 运行结果:
        # custom_iterator是否是迭代器: True
        # 1
        # 2
        # 3
    
     
    

    小结:

    • 迭代:一般来说就是使用 for循环,遍历取值的过程
    • 可迭代对象:可以被 for循环遍历取值的实例对象,或者类里面提供__iter()__ 方法的类的实例对象
    • 迭代器对象:类里面提供 __iter()__ 方法 和 __next()__ 方法的类的实例对象
    • 可迭代对象的本质:通过迭代器把数据依次迭代出来
    • 迭代器的作用:记录迭代的位置,以便获取下一个位置的数据。
    • __iter()__ 方法:调用可迭代对象的 __iter()__ 方法,获取可迭代对象的迭代器
    • __next()__ 方法:调用迭代器对象的 __next()__ 方法,获取迭代器中的下一个值。
    • for 循环的本质:
    • 遍历可迭代对象,通过可迭代对象的 __iter()__ 方法获取迭代器,然后通过迭代器的 __next()__ 方法,获取迭代器中的下一个值。
    • 遍历迭代器,直接通过迭代器的 __next()__ 方法,获取迭代器中的下一个值。
    • for 循环内部捕获了StopIteration 异常

    生成器

    什么是生成器

    生成器是一类特殊的迭代器,既可以通过 for循环遍历取值,或者通过 next() 取值

    生成器的创建

    推导式

    
    def main():
        """入口函数"""
    
        # 生成器的方式1,把列表推导式的中括号改成小括号创建的对象就是生成器
        generator = (i * 2 for i in range(10))
    
        for value in generator:
            print(value)
    
    
    if __name__ == '__main__':
        main()
    
    
    

    yieid 关键字

    
    # 创建生成器方式2:在def里面看到有yield关键字那么就表示生成器
    
    
    def func(num):
        current_index = 0
        print("--------1")
        # 循环判断条件是否成立
        while current_index < num:
            current_index += 1
            print("--------2")
            # 代码执行到yield会暂停,然后把结果返回出去, 再次启动生成器会在暂停的位置继续往下执行
            yield current_index
            print("--------3")
    
    
    def main():
        """入口函数"""
        tmp = func(5)
        result = next(tmp)
        print(result)
        result = next(tmp)
        print(result)
    
    
    if __name__ == '__main__':
        main()
    
    # 运行结果
    # --------1
    # --------2
    # 1
    # --------3
    # --------2
    # 2
    
    
    

    yieid 与 return

    • yieid 会暂停函数的运行,返回当前数据,执行 next方法,函数会从暂停的位置继续向下执行
    • return 会打断函数的运行,返回结果,再次运行函数,会从头开始重新执行
    • yieid 相同条件多次执行可以返回一组有关联的数据
    • return 相同条件多次执行返回同一结果
    • yieid 和 return 联用只有python3 支持,并且执行到return 会抛出StopIteration 异常
    • 除了next 可以启动生成器,send 也可以,send 启动可以传参,但是send 第一次启动生成器的传参必须为None,否则报错(TypeError: can't send non-None value to a just-started generator)
    
    # 创建生成器方式2:在def里面看到有yield关键字那么就表示生成器
    
    
    def func(num):
        current_index = 0
        print("--------1")
        # 循环判断条件是否成立
        while current_index < num:
            current_index += 1
            print("--------2")
            # 代码执行到yield会暂停,然后把结果返回出去, 再次启动生成器会在暂停的位置继续往下执行
            sned_value = yield current_index
            print("--------3")
    
            if sned_value > 3:
                return
    
            print("sned_value ", sned_value)
    
    
    def main():
        """入口函数"""
        tmp = func(5)
    
        result = tmp.send(None)
    
        print(result)
    
        result = tmp.send(2)
        print(result)
    
    
    if __name__ == '__main__':
        main()
    
        # 运行结果 
        # --------1
        # --------2
        # 1
        # --------3
        # sned_value 2
        # --------2
        # 2
    
    
    

    协程

    什么是协程

    协程又称微线程,用户级线程,是Python 中实现多任务的方式之一。特点:在不开辟线程的基础上完成多任务

    简单的协程

    在 def 中有一个yieid 关键字,就是协程

    
    import time
    
    
    # 协程1
    def func1():
        for i in range(5):
            print("func1...")
            time.sleep(0.2)
            yield
    
    
    # 协程2
    def func2():
        for i in range(5):
            print("func2...")
            time.sleep(0.2)
            yield
    
    
    def main():
        """入口函数"""
        f1 = func1()
        f2 = func2()
    
        for i in range(5):
            next(f1)
            next(f2)
    
    
    if __name__ == '__main__':
        main()
    
        # 运行结果
        # func1...
        # func2...
        # func1...
        # func2...
        # func1...
        # func2...
        # func1...
        # func2...
        # func1...
        # func2...
    
     
    

    协程:在单线程的基础上可以完成多任务,多个任务按照一定顺序交替执行

    greenlet 和 gevent

    greenlet

    
    # greenlet: greenlet框架封装的是yield,greenlet能够让程序员很直观的查看协程的切换
    import time
    import greenlet
    
    
    # 任务1
    def work1():
        for i in range(5):
            print("work1...")
            time.sleep(0.2)
            # 切换到协程2里面执行对应的任务
            g2.switch()
    
    
    # 任务2
    def work2():
        for i in range(5):
            print("work2...")
            time.sleep(0.2)
            # 切换到第一个协程执行对应的任务
            g1.switch()
    
    
    if __name__ == '__main__':
        # 创建协程指定对应的任务
        g1 = greenlet.greenlet(work1)
        g2 = greenlet.greenlet(work2)
    
        # 切换到第一个协程执行对应的任务
        g1.switch()
    
    
    

    gevent

    
    import gevent
    import time
    from gevent import monkey
    
    # 总结: gevent封装是greenlet,可以根据耗时操作自动完成协程之间的切换执行
    
    # 打补丁,让gevent框架识别耗时操作,比如:time.sleep,网络请求延时
    monkey.patch_all()
    
    
    # 任务1
    def work1():
        for i in range(10):
            print("work1....")
            time.sleep(0.2)
            # gevent.sleep(0.2)
    
    # 任务1
    def work2():
        for i in range(10):
            print("work2....")
            time.sleep(0.2)
            # gevent.sleep(0.2)
     
    
    if __name__ == '__main__':
        # 创建协程指定对应的任务
        g1 = gevent.spawn(work1)
        g2 = gevent.spawn(work2)
    
        # 主线程等待协程执行完成以后程序再退出
        g1.join()
        g2.join()
    
    
    

    进程、线程、协程

    • 进程是系统资源分配的基本单位,线程是CPU调度的基本单位,协程是在不开辟线程的基础上完成多任务
    • 进程资源开销大,稳定性强,效率很低;线程资源开销较小,稳定性较差,效率一般;协程资源开销小,稳定性依赖于线程,效率较高
    • 多进程,多线程可能是并行,但协程在一个线程中,所以是并发
    • 协程依赖于线程,线程依赖于进程。

    进程就像是一个团队,团队有一些资源,线程是团队的成员,主线程就是team leader,协程就是团队中全栈工程师,哪里需要,去哪里。


    到此结   DragonFangQy   2018.5.1


    1. 迭代

    2. Python 迭代对象、迭代器、生成器

    相关文章

      网友评论

        本文标题:Python学习笔记十四(多任务、协程、迭代器、生成器)

        本文链接:https://www.haomeiwen.com/subject/gmlxrftx.html