Python 生成器相关知识

Python 生成器相关知识

  1. 生成器

    • 生成器概念:

      在Python社区,大部分人把生成器与迭代器看成是一种。生成器的本质就是迭代器。

      唯一区别:生成器是我们自己用Python代码构建的数据结构。迭代器都是系统提供的,或者转化得来的。

    • 获取生成器的三种方式:

      • 生成器函数:

        yield:只要函数中有yield,那么它就是生成器函数而不是一般的函数了。生成器函数中可以存在多个yield,一个yield对应一个next。yield不会结束函数

        return:函数中只存在一个return,遇到next即结束函数,并为执行者提供一个返回值。

        def func():
            a=22
            yield a
            b=12
            yield b
            c=a+b
            yield c
        ret=func()
        print(next(ret))	# yield返回值可以使用next方法逐个提取
        print(next(ret))	# 每次运行只运行到当前的yield即停止
        print(next(ret))
        22
        12
        34
        
        • 生成器函数的应用:

          其实是对数据的一种构造方法
          1. 吃包子例子:预订5000个包子,一次生全部生产出来占地方(内存),还有吃不完的浪费;如果使用生成器,那么就相当于生产一个吃一个,不会产生浪费不占地方,节省资源。
          2. 鸡蛋例子:家里需要5000个鸡蛋,有两种方法可以获取,一种是去菜场或超市直接购买5000个回来,用篮子装着放家里,占地方还怕吃不完坏掉了;第二种方法是去买个生蛋的母鸡,只要它活着就能一直生蛋,要几个就让它生几个。
        • 程序举例:

          def func():  # 这个函数就好比直接一次性生产5000个包子,生产出来的包子都在列表中,
              list1 = []  # 占用大量内存,如果数据量过大,则导致运行速度下降甚至撑爆内存。
              for i in range(1, 5001):
                  list1.append(f'第{i}个包子')
              return list1
          print(func())
          
          def gen_func():  # 使用生成器函数生产包子
              for i in range(1, 5001):  # 也是可以生产5000个,但并不是一次性返回
                  yield f'第{i}个包子'  # yield配合for循环也是可以使用,next一次提取一个数据
          baozi = gen_func()  # 调用函数,将返回值放到baozi变量中
          for i in range(1000):  # 可以根据需要的数量产生,这次需要1000个包子
              print(next(baozi))  # next1000次就可以获得
          
        • yield form

          前面用到的yield都是在函数内返回一个不可变数值,但如果想next一次就返回可迭代对象中的一个元素,就需要用到yield form。举例如下:

          def func():
              list1 = [1, 2, 3, 4, 5, 6]  # 返回的对象是个列表
              yield from list1  # 如果不用yield form将返回整个列表
          ret = func()
          for i in range(3):    # 循环3次,返回列表中的前3个数据
              print(next(ret))
          
      • 列表推导式和生成器表达式:

        • 列表推导式:用一行代码构建一个比较复杂、有规律的列表。

          列表推导式分成两种模式:

          1. 循环模式:[变量(加工后的变量) for 变量 in iterable]

            例一:一行代码实现for循环产生有规律的列表

            list1=[i for i in range(1,11)]
            print(list1)
            >>>[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
            

            例二:将10以内所有整数的平方放入列表

            pflist=[i**2 for i in range(1,11)]
            print(pflist)
            >>>[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
            

            例三:将50以内所有偶数放入列表

            list1=[i for i in range(2,51,2)]
            print(list1)
            

            例四:从Python1期到Python100期写入列表。

            list2=[f'Python{i}期' for i in range(1,101)]
            print(list2)
            
          2. 筛选模式:[变量(加工后的变量) for 变量 in iterable if 条件]

            在循环模式下,满足条件的放入列表

            例一:30以内能被3整除的数放入列表

            list1 = [i for i in range(1, 31) if i % 3 == 0]
            print(list1)
            >>>[3, 6, 9, 12, 15, 18, 21, 24, 27, 30]
            

            例二:过滤掉长度小于3的字符串列表,并将剩下的转换成大写

            l1 = ['abc', 'a', 'h', 'inter', 'amwk', 'ce', 'sk', 'at', 'apple', 'c', 'reset']
            print([i.upper() for i in l1 if len(i) >= 3])
            >>>['ABC', 'INTER', 'AMWK', 'APPLE', 'RESET']
            

            例三:含有两个'e'的所有的人名全部大写留下来

            names = [['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven', 'Joe'],
                     ['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']]
            print([name.upper() for i in names for name in i if name.count('e')==2])
            >>>['JEFFERSON', 'WESLEY', 'STEVEN', 'JENNIFER']
            
        • 生成器表达式:
          生成器表达式与列表推导式的写法几乎一模一样:只是将列表推导式中的方括号变成了圆括号,生成器的最大优点是节省资源。

          生成器表达式也分为循环模式和筛选模式,支持多层循环构建,写法上只有一个不同:[]换成()。虽然只是写法不同,但是本质有很大区别,列表推导式非常占用内存,生成器表达式非常节省内存。

          obj= (i for i in range(0,11, 2))
          print(next(obj))
          print(next(obj))
          print(next(obj))
          for i in obj:
              print(i,end='  ')
          >>>0
          2
          4
          6  8  10  
          
        • 两种表达式的总结:

          1. 列表推导式:

            缺点:

            • 有毒,列表推导式只能构建比较复杂并且有规律的列表,不要太着迷。
            • 超过三层循环才能构建成功的列表,就不建议用列表推导式。
            • 排错不行,查找错误(debug)下不方便,不直观。

            优点:

            • 一行构建,简单
            • 装逼?(太白这样觉得)
          2. 生成器表达式:
posted @ 2020-12-03 10:57  amwkvi  阅读(63)  评论(0编辑  收藏  举报