多任务-携程与迭代器

编程语言中的——迭代
  • 迭代的概念
    • 是一种数据的访问方式,例如当用for循环访问数据时,我们会从中依次拿到其中的数据
    • 迭代会记住你当前访问的位置
    • 在下一次访问的时候能够基于当前的位置继续进行
    • 在python中迭代只能从前往后
    • 直到访问结束
  • 常见的可迭代类型
    • list、tuple、dict、set、str、等容器类型都是可迭代类型实例
  • 可迭代对象与迭代器
    • 可迭代对象
      • 可迭代对象概念
        • 实现了__iter__方法的对象就是符合了可迭代对象的表象特征,此时可用isinstance判断类型是可迭代类型
        • 可迭代对象实质需要通过调用__iter__方法返回了一个迭代器对象,这才完成了可迭代对象的本质;实际上是通过迭代器对象来完成迭代
      • 综合来说
        • 可迭代对象就是实现了__iter__方法,并且返回了一个迭代器的实例的实例对象
    • 迭代器
      • 迭代器概念
        • 实现了__next__方法,在__next__方法中,提供迭代数据并记录位置信息的对象称为迭代器
        • __next__方法每次返回一个元素,并且会自动指向下一个元素,当元素都已经取完的时候会抛出Stoplteration异常,底层会根据异常来结束for循环的遍历
        • 另外在python中一般要求迭代器也要同时是可迭代对象,所以一个迭代器也会有__iter__方法,并且会返回自己的实例
      • 综合来说
        • 一个迭代器就是实现了:
          • 1.__next__方法用来提供数据和记录数据位置,当数据取完的时候抛出Stoplteration异常
          • 2.迭代器也有__iter__方法,返回时实例是自己本身;  所以迭代器本身即是迭代器也是可迭代对象
      • 迭代器的两种使用场景
        • 迭代访问已经存在的数据<容器>
        • 延迟计算还未存在的数据在迭代访问的时候计算出来     例如:遍历斐波那契数列,提前定义要访问的数据数量
  • 如何判断对象obj是否可迭代
    • 使用方法
      • isinstance(obj, classinfo)  -> bool 判断obj是否是classinfo类型的实例 是则返回真 ; 否则返回假
    • 判断是不是可迭代对象
      • from collections import Iterable
      • is_iterable = isinstance(obj, Iterable) 
        • 判断是不是可迭代对象如过obj实例包含__iter__ 方法,就会认为是iterable创建或者其子类创建出来的实例
  • 判断对象是不是迭代器(后面有介绍迭代器):
    • from collections import Iterator
    • isinstance(iter(obj), Iterator)   obj为可迭代对象,  判断列表的迭代器是不是迭代器类型
  • 理解 for 循环遍历的过程实质
    • 1.for item in Iterable 循环的本质就是先通过iter()函数获取可迭代对象Iterable的迭代器,实际上是对迭代器的操作
    • 2.然后对获取到的迭代器不断调用next()方法来获取下一个值并将其赋值给item,
    • 3.当遇到StopIteration的异常后循环结束。
  • 对list、set、dict、等集合的迭代的理解
  • 对可迭代对象和迭代器的举例
生成器 -- 特殊的迭代器
  • 概念:
    • 生成器generator是一种特殊的优雅的迭代器,使用方式和迭代器一致,实现方式和迭代器不同
    • 一个函数或者子程序都只能 return 一次,但是一个生成器能暂停执行并返回一个中间的结果-这就是 yield 语句的功能 : 返回一个中间值给调用者并暂停执行。使用next()会再次激活生成器执行程序;
  • 第一种生成器
    • 概念:
      • 使用yield关键字实现的迭代器功能   含有yield关键字的函数不再是函数,而是生成器
      • 例:
    • yield 语句的功能 
      • 返回一个中间值给调用者并暂停执行,返回yield后面的表达式,待下次激活重新活重新恢复执行状态。
    •  用生成器遍历斐波那契数列
  • 第二种生成器
    • 格式:
      • lst 也是也个生成器
  • 生成器的方法
    • next(生成器)/ 生成器.next()  “激活生成器”第一次激活,生成器第一次执行代码要用next()激活,或者send(None)
    • send(data);  生成器.send(None)  == next(生成器)  “激活生成器并给生成器发送数据,用yield接收;
      • 注意:第一次激活生成器你能使用send发送内容,因为没有在yield位置,没有接收,会报错”
    • close()  关闭生成器
  • 备注:
    • 生成器对象 一旦调用close() 在下一次恢复调用的时候将raise StopIteration
    • 生成器的任务执行完后,底层会自动raise一个StopIteration
    • return可以终止生成器,Python3中的生成器可以使用return返回最终运行的返回值,返回值是依靠StopInteration异常来传出,可用一次拦截来获取return的内容;   而Python2中的生成器不允许使用return返回一个返回值(即可以使用return从生成器中退出,但return后不能有任何表达式)
  • 案例

多任务——携程
  • 协程概念
    • 协程Coroutine是一种多任务的实现方式.
    • 协程在执行遇阻塞时会根据(底层实现yield或其他)挂起当前函数, 转而执行其他函数代码在合适的时候能够切换到另一个函数中执行
    • 协程的多任务:进程和线程是操作系统提供的多任务方式,协程则是从用户层面解决任务的执行-恢复-执行-完成的过程从而实现多任务。
    • 生成器就是一个简单的协程.
      • 例:
  • 携程相比线程的优点缺点
    • 优点
      • 在实现多任务时, 线程切换从系统层面远不止保存和恢复 CPU上下文这么简单。 操作系统为了程序运行的高效性每个线程都有自己缓存Cache等等数据,操作系统还会帮你做这些数据的恢复操作。 所以线程的切换非常耗性能。但是协程的切换只是单纯的操作CPU的上下文,所以一秒钟切换个上百万次系统都抗的住。
      • 并发性高,占用资源小
    • 缺点
      • 由于协程是基于同一个线程,故不存在并行的情况,不能有效的利用多核
  • python中协程实现-greenlet
    • 实现原理
      • greenlet封装yield关键字的作用
    • 使用方法
      • g = greenlet(func)   创建一个协程 g   ; func为协程程序
      • g.switch( *args, **args)   启动协程g/ 切换执行其他的协程g; args为函数接收的参数
    • 特点:易用性不高,还是太底层,需要手动切换
    • 例子:
  • python中协程实现-gevent
    • 实现原理
      • gevent 底层实现封装了greenlet的代码,不需要人工切换协程;
      • 其原理是当一个greenlet遇到IO(指的是input output 输入输出,比如网络、文件操作等)操作时,比如访问网络,就自动切换到其他的greenlet,等到IO操作完成,再在适当的时候切换回来继续执行。由于IO操作非常耗时,经常使程序处于等待状态,有了gevent为我们自动切换协程,就保证总有greenlet在运行,而不是等待IO
    • 使用方法
      • from gevent import monkey    导入 monkey模块
      • monkey.patch_all(socket=True, dns=True, time=True, select=True, thread=True)    给在程序中导入的各python模块打上补丁(除input所在模块),即替换成gevent可切换的模块,默认都为True既表示可以协程化,可手动修改程False     **注:这句话尽可能放到import其他模块的最前面,放到后面可能出错
      •  g = gevnet.spawn(fuc, *args, **args)     创建协程,func 为函数,  其他为函数需要传入的参数
      • g.join() 和 gevent.joinall([g])      阻塞等待协程结束, 一定要等待协程结束,不然线程会默认结束整个线程
      • **注意:在协程中遇到阻塞等待会切换协程,排除(input()
    • 协程应用场景
      • 应用:爬虫/并发服务器
    • 案例:
如何选择使用什么来实现多任
  • 多进程:当逻辑功能复杂,选择多进程来独立执行
  • 多线程:当有多个重复性的操作,逻辑性不复杂的较轻量级操作,选择用多线程来执行
  • 协程:当涉及到网络等,这种需要高并发和有阻塞等待的场景选择协程类执行;






刘小恺(Kyle) wechat
如有疑问可联系博主