Python进阶-多任务编程-03

Python进阶系列
Python进阶-网络编程-01
Python进阶-网络编程-02
Python进阶-网络编程-03
Python进阶-多任务编程-01
Python进阶-多任务编程-02
Python进阶-多任务编程-03
Python进阶-正则表达式
Python进阶-数据库编程-01
Python进阶-数据库编程-02
Python进阶-数据库编程-03
Python进阶-数据库编程-04
Python进阶-数据拷贝问题
Python进阶-模块导入问题
Python进阶-miniWeb框架

8.1. 自定义迭代对象、迭代器

  • 具体实现步骤

    • 自定义列表类:MyList(object):
      • 初始化方法
      • __iter()__方法,对外提供迭代器
      • addltem()方法,用来添加数据
    • 自定义迭代器类:MyListlterator(object):
      • 初始化方法
      • 迭代器方法__iter__()
      • 获取下一个元素值的方法__next__()
  • 代码示例:

    class MyList(object):
        def __init__(self):
            # 定义实例属性,保存数据
            self.items = []
    
        # 对外提供迭代器
        def __iter__(self):
            mylist_iterator = MyListIterator(self.items)
            return mylist_iterator
    
        def addItem(self,data):
            self.items.append(data)
    
    
    class MyListIterator(object):
        def __init__(self,items):
            # 定义实例属性,保存MyList类传递过来的items
            self.items = items
            # 记录迭代器迭代的位置
            self.current_index = 0
    
        def __iter__(self):
            pass
    
        def __next__(self):
            # 1.判断当前的下标是否越界
            # 1)根据下标获取对应的元素值
            # 2)下标位置+1
            # 3)返回小标对应的数据
            if self.current_index < len(self.items):
                data = self.items[self.current_index]
                self.current_index += 1
                return data
            # 2.如果越界,直接抛出异常
            else:
                # raise 用于主动跑抛出异常
                raise StopIteration
    
    
    if __name__ == '__main__':
        # 创建自定义列表对象
        mylist = MyList()
        mylist.addItem("张飞")
        mylist.addItem("关于")
        mylist.addItem("班长")
    
        # iter(要遍历的对象),mylist是自己定义的列表对象,
        # 类中提供了iter()的可迭代器,表示对象可以迭代
        mylist_iterator = iter(mylist)
    
        value = next(mylist_iterator)
        print(value)
        value = next(mylist_iterator)
        print(value)
        value = next(mylist_iterator)
        print(value)
    

8.2. 迭代器案例:斐波那契数列

  • 自定义一个迭代器 Fibnacci()

    • __iter__()
    • __nxet__()
  • 代码示例:

    class Fibnacci(object):
        def __init__(self,num):
            # 定义实例属性,保存生成的列数
            self.num = num
            # 保存第一列和第二列
            self.a = 1
            self.b = 1
            # 记录位置的下标
            self.current_index = 0
    
        def __iter__(self):
            return self
    
        def __next__(self):
            # 1.判断列数是否超过生成的总列数
            # 定义变量,保存a的值
            # a=b,b=a+b
            # 当前列数+1
            # 返回值
            if self.current_index < self.num:
                data = self.a
                self.a,self.b = self.b,self.a+self.b
                self.current_index += 1
                return data
            else:
                raise StopIteration
            # 如果超出范围,报错
    
    if __name__ == '__main__':
        # 创建迭代器的对象
        fib_iterator = Fibnacci(5)
    
        for value in fib_iterator:
            print(value)
    

8.3. 生成器-基本使用

  • 概念:特殊的迭代器(按照一定的规律生成数列)

  • 生成器创建方式:

    • 列表推导式

      data_list2 = (x*2 for x in range(10))
      print(data_list2)
      
      # 通过next获取下一个值
      value = next(data_list2)
      print("------->",value)
      
    • 函数中使用yield

      # 使用yield 创建了一个生成器
      def test():
          yield 10
      
      n = test()
      print("n =",n)
      
      value = next(n)
      print(value)
      

8.4. 生成器案例:斐波那契数列

  • 思路

    1.创建一个生成器,实现斐波那契数列
    定义变量保存第一列和第二列的值

      定义变量保存当前生成的位置
    
      循环生成数据,条件(当前的列数<总列数)
       保存a的值
       修改a、b的值  (a b,b a+b)
       返回a的值yield
    

    2.定义变量保存生成器
    next(生成器)得到下一个元素值

  • yield的作用

    • 充当return作用
    • 保存程序的运行状态 ,并且暂停程序执行
    • next() 的时候,可以继续唤醒程序从yield位置继续向下执行
  • 程序执行过程

请添加图片描述


8.5. 生成器-使用事项

  • return的作用:可以结束生成器的运行

  • send()的作用生成器.send(值)

    fib.send(1)
    xxx = yield data   # xxx = 1
    

8.6. 协程-yield

  • 协程:在不开辟新的线程的基础上,实现多个任务。协程是一个特殊的生成器

  • 代码示例:

    import time
    
    def work1():
        while True:
            print("正在执行work1…………")
            yield
            time.sleep(0.5)
    
    def work2():
        while True:
            print("正在执行work2…………")
            yield
            time.sleep(0.5)
    
    if __name__ == '__main__':
        # 定义生成器
        w1 = work1()
        w2 = work2()
    
        while True:
            next(w1)
            next(w2)
    

8.7. 协程-greenlet

  • greenlet实现协程:greenlet是一个第三方的模块,自行调度的微线程

  • 具体步骤:

    1. 导入模块

      from greenlet import greenlet
      
    2. 创建任务 work1() work2()

      def work1():
          while True:
              print("正在执行work1…")
              time.sleep(0.5)
              # 切换到第二个任务
              g2.switch()
      
      def work2():
          while True:
              print("正在执行work2…")
              time.sleep(0.5)
              # 切换回第一个任务
              g1.switch()
      
    3. 创建greenlet对象

      g1 = greenlet(work1)
      g2 = greenlet(work2)
      
    4. 手动switch任务

      g1.switch()
      
  • 代码示例:

    import time
    from greenlet import greenlet
    
    def work1():
        while True:
            print("正在执行work1…………")
            time.sleep(0.5)
            # 切换到第二个任务
            g2.switch()
    
    def work2():
        while True:
            print("正在执行work2…………")
            time.sleep(0.5)
            # 切换回第一个任务
            g1.switch()
    
    if __name__ == '__main__':
        # 创建greenlet对象
        g1 = greenlet(work1)
        g2 = greenlet(work2)
    
        g1.switch()
    

8.8. 协程-gevent

  • gevent实现协程:gevent也是第三方库,自动调度协程,自动识别程序中的耗时操作

  • 使用步骤:

    • 导入模块

      import gevent
      
    • 指派任务

      # gevent.spawn(函数名,参数1,参数2…)
      g1 = gevent.spawn(work1)
      g2 = gevent.spawn(work2)
      
    • join()让主线程等待协程执行完毕后再退出

      g1.join()
      g2.join()
      
  • gevent不能识别耗时操作的问题

    1. 替换time.sleep() -> gevent.sleep()
  1. 打补丁:让gevent识别time.sleep()
  • 打猴子补丁过程

    1. 导入模块:

      from gevent import monkey
      
    2. 破解所有:

      monkey.patch_all()
      
  • 猴子补丁的作用

    1. 在运行时替换方法、属性等
    2. 在不修改第三方代码的情况下增加原来不支持的功能
    3. 在运行时为内存中的对象增加patch而不是在磁盘的源代码中增加
  • 代码示例:

    from gevent import monkey
    monkey.patch_all()
    import time
    import gevent
    
    def work1():
        while True:
            print("正在执行work1…………",gevent.getcurrent())
            gevent.sleep(0.5)
    
    def work2():
        while True:
            print("正在执行work2…………",gevent.getcurrent())
            gevent.sleep(0.5)
    
    if __name__ == '__main__':
        # 指派任务 gevent.spawn(函数名,参数1,参数2)
        g1 = gevent.spawn(work1)
        g2 = gevent.spawn(work2)
    
        g1.join()
        g2.join()
    

8.9. 进程、线程、协程区别

  • 进程、线程、协程的定义

    1. 进程:进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。每个进程都有自己的独立内存空间,不同进程通过进程间通信来通信。由于进程比较重量,占据独立的内存,所以上下文进程间的切换开销(栈、寄存器、虚拟内存、文件句柄等)比较大,但相对比较稳定安全。
    2. 线程:线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。线程间通信主要通过共享内存,上下文切换很快,资源开销较少,但相比进程不够稳定容易丢失数据。
    3. 协程:协程是一种用户态的轻量级线程,协程的调度完全由用户控制。协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候·恢复先前保存的寄存器上下文和栈,直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快。
  • 进程资源分配的基本单位,线程CPU调度的基本单位,协程单线程执行多任务

  • 选择问题

    进程:密集CPU任务,需要充分使用多核CPU资源(服务器,大量的并行计算)的时候,用多进程。
    缺陷:多个进程之间通信成本高,切换开销大。

    线程:密集/O任务(网络1/O,磁盘1/0,数据库I/O)使用多线程合适。
    缺陷:同一个时间切片只能运行一个线程,不能做到高并行,但是可以做到高并发。

    协程:当程序中存在大量不需要CPU的操作时(IO),适用于协程;


8.10. 并行下载器

  • 案例实现思路

    1. 定义要下载的图片路径

    2. 调用文件下载的函数,专门下载文件

    下载函数

    1. 根据url地址请求网络资源

    2. 在本地创建文件,准备保存

    3. 读取网络资源数据

    4. 把读取的网络资源写入到本地文件中

    5. 做异常捕获

  • 打开网址对应的资源

    urllib.request.urlopen(网址)
    
  • joinall() 批量把协程添加join

    joinall( [协程的列表] )
    
    gevent.joinall
    ([
      gevent.spawn(download_img, img_url1, "1.gif"),
      gevent.spawn(download_img, img_url2, "2.gif"),
      gevent.spawn(download_img, img_url3, "3.gif")
    ])
    
  • 代码示例

    from gevent import monkey
    monkey.patch_all()
    import gevent
    import urllib
    
    def main():
        img_url1 = "http.mpitc.cn"
        img_url2 = "http.mpitc.cn"
        img_url3 = "http.mpitc.cn"
        
        gevent.joinall([
            gevent.spawn(download_img, img_url1, "1.gif"),
            gevent.spawn(download_img, img_url2, "2.gif"),
            gevent.spawn(download_img, img_url3, "3.gif")
        ])
    
    def download_img(img_url,file_name):
        try:
            # 1.根据url地址请求网络资源
            resource_data = urllib.request.urlopen(img_url)
            # 2.在本地创建文件,准备保存
            with open(file_name,"wb") as file:
                while True:
                    # 3.读取网络资源数据
                    file_data = resource_data.read(1024)
                    if file_data:
                        # 4.把读取的网络资源写入到本地文件中
                        file.write(file_data)
                    else:
                        break
                # 5.做异常捕获
        except Exception as e:
            print("文件%s下载失败!" % file_name)
        else:
            print("文件%s下载成功!" % file_name)
    
    if __name__ == '__main__':
        main()
    

8.11. 案例:协程版Web服务器

  • 思路:

    1. 导入模块 gevent

    2. 创建协程 gevent.spawn()

      gevent.spawn(self.request_handler,new_client_socket,ip_port)
      
    3. 打补丁

      from gevent import monkey
      monkey.patch_all()
      
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值