zoukankan      html  css  js  c++  java
  • python (生成器,生成推导式)

    一. 生成器

      生成器的实质就是迭代器, 在python中有三种方式获得生成器

        1. 生成器函数

        2. 各种推导式实现生成器

        3. 数据转换也可以获得生成器

    def func():
        print("111")
        return 222
    ret = func()
    print(ret)
    
    #结果:
    #    111
    #    222

      将函数中的return 换成 yield就是生成器

    def func():
        print("111")
        yield 222
    ret = func()
    print(ret)
    
    #结果:
    #<generator object func at 0x0000025256283A40>

      以上两段代码执行的结果不一样. 因为下面的代码有yield,所以这个函数就是一个生成器函数.这个时候再执行这个函数,就不在是函数的执行了.而是获取这个生成器.

      所以要想运行这个函数,就要执行__next__()函数来执行函数

    def func():
        print("111")
        yield 222
    gener = func()  # 这个时候函数不会执行. 而是获取到⽣生成器
    ret = gener.__next__()  # 这个时候函数才会执行. yield的作⽤和return⼀样. 也是返回数据
    print(ret)
    
    #结果:
    #     111
    #     222

      可以看到:return和yield执行的效果是一样. 但是二者是有区别的,yield是分段执行一个函数,return是直接停止执行函数

    def func():
        print("111")
        yield 222
        print("333")
        yield 444
    gener = func()
    ret = gener.__next__()
    print(ret)
    ret2 = gener.__next__()
    print(ret2)
    ret3 = gener.__next__()  # 最后⼀一个yield执行完毕. 再次__next__()程序报错, 也就是说和return⽆无关了了.
    print(ret3)
    
    #结果:
    #111
    #222
    #333
    #444
    #Traceback (most recent call last):
    # File "D:/pycharm项目/week3/day3/函数生成器.py", line 103, in #<module>
    #    ret3 = gener.__next__()  # 最后⼀一个yield执⾏行行完毕. 再次#__next__()程序报错, 也就是 说. 和return⽆无关了了.
    #StopIteration

      当程序运行完后⼀个yield. 那么后面继续进行__next__()程序会报错. 

      生成器作用:

    def cloth():
        lst = []
        for i in range(0, 100):
            lst.append("衣服"+str(i))
        return lst
    cl = cloth()
    print(cl)
    
    #结果:
    #['衣服0', '衣服1', '衣服2', '衣服3', '衣服4', '衣服5', '衣服6', '衣服7', '衣服8', '衣服9', '衣服10', '衣服11', '衣服12', '衣服13', '衣服14', '衣服15', '衣服16', '衣服17', '衣服18', '衣服19', '衣服20', '衣服21', '衣服22', '衣服23', '衣服24', '衣服25', '衣服26', '衣服27', '衣服28', '衣服29', '衣服30', '衣服31', '衣服32', '衣服33', '衣服34', '衣服35', '衣服36', '衣服37', '衣服38', '衣服39', '衣服40', '衣服41', '衣服42', '衣服43', '衣服44', '衣服45', '衣服46', '衣服47', '衣服48', '衣服49', '衣服50', '衣服51', '衣服52', '衣服53', '衣服54', '衣服55', '衣服56', '衣服57', '衣服58', '衣服59', '衣服60', '衣服61', '衣服62', '衣服63', '衣服64', '衣服65', '衣服66', '衣服67', '衣服68', '衣服69', '衣服70', '衣服71', '衣服72', '衣服73', '衣服74', '衣服75', '衣服76', '衣服77', '衣服78', '衣服79', '衣服80', '衣服81', '衣服82', '衣服83', '衣服84', '衣服85', '衣服86', '衣服87', '衣服88', '衣服89', '衣服90', '衣服91', '衣服92', '衣服93', '衣服94', '衣服95', '衣服96', '衣服97', '衣服98', '衣服99']
    
    
    
    def cloth():
        for i in range(0, 10000):
            yield "衣服"+str(i)
    cl = cloth()
    print(cl.__next__())
    print(cl.__next__())
    print(cl.__next__())
    print(cl.__next__())
    
    #结果:
    #    衣服0
    #    衣服1
    #    衣服2
    #    衣服3

      区别: 第一种是将循环的内容直接全部拿出来,很占用内存. 第二种使用生成器,一个__next__函数只拿出一个,之后循环停止在当前位置,下一个再取值再从停止位置继续向前取值.

    def eat():
        print("我吃什么啊")
        a =  yield  "馒头"
        print("a=",a)
        b =  yield  "大饼"
        print("b=",b)
        c =  yield  "韭菜盒子"
        print("c=",c)
        yield  "GAME OVER"
    
    gen = eat()      # 获取⽣成器
    
    ret1 = gen. __next__ ()
    print(ret1)
    ret2 = gen.send("胡辣汤")
    print(ret2)
    ret3 = gen.send("狗粮")
    print(ret3)
    ret4 = gen.send("猫粮")
    print(ret4)
    
    #结果:
    #    我吃什么啊
    #    馒头
    #    a= 胡辣汤
    #    大饼
    #    b= 狗粮
    #    韭菜盒子
    #    c= 猫粮
    #    GAME OVER

      send和__next__()区别: 1. send和next()都是让生成器向下走⼀次 2. send可以给上⼀个yield的位置传递值, 不能给后⼀个yield发送值. 在第⼀次执行生成器代码的时候不能使用send()

      生成器可以使用for循环来循环获取内部的元素:

    def func():
        print(111)
        yield 222
        print(333)
        yield 444
        print(555)
        yield 666
    gen = func()
    for i in gen:
        print(i)
    
    #结果:
    #    111
    #    222
    #    333
    #    444
    #    555
    #    666

    二. 列表推导式

      列表推导式:最终给的是列表

      语法:  [最终结果(变量) for 变量 in 可迭代对象]

    lst = []
    for i in range(1,15):
        lst.append("python%s" % i)
    print(lst)
    #结果:
    #['python1', 'python2', 'python3', 'python4', 'python5', 'python6', 'python7', 'python8', 'python9', 'python10', 'python11', 'python12', 'python13', 'python14']
    
    
    lst = ["python%s" % i for i in range(1,15)]  
    print(lst)              
    #结果:
    #['python1', 'python2', 'python3', 'python4', 'python5', 'python6', 'python7', 'python8', 'python9', 'python10', 'python11', 'python12', 'python13', 'python14']

       列表推导式是通过⼀行来构建你要的列表, 列表推导式看起来代码简单. 但是出现错误之后很难排查. 

      ⽣生成器表达式和列表推导式的语法基本上是⼀样的. 只是把[]替换成() 

    g = (i for i in range(10))
    print(g)    #打印结果是一个生成器
    #结果:
    #<generator object <genexpr> at 0x0000029147073A40>
    
    
    gen = ("麻xx我第%s次爱你" % i for i in  range(10))
    for i in  gen:    #遍历生成器内容
        print(i)
    #结果:
    #麻xx我第0次爱你
    #麻xx我第1次爱你
    #麻xx我第2次爱你
    #麻xx我第3次爱你
    #麻xx我第4次爱你
    #麻xx我第5次爱你
    #麻xx我第6次爱你
    #麻xx我第7次爱你
    #麻xx我第8次爱你
    #麻xx我第9次爱你      

      生成器表达式可以进行筛选:

    lst = [i for i in range(1,101) if i % 3 == 0]
    print(lst)
    #结果:
    #[3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48, 51, 54, 57, 60, 63, 66, 69, 72, 75, 78, 81, 84, 87, 90, 93, 96, 99]
    
    lst = [i*i for i in range(1,100) if i % 3 ==0]
    print(lst)
    #结果:
    #[9, 36, 81, 144, 225, 324, 441, 576, 729, 900, 1089, 1296, 1521, 1764, 2025, 2304, 2601, 2916, 3249, 3600, 3969, 4356, 4761, 5184, 5625, 6084, 6561, 7056, 7569, 8100, 8649, 9216, 9801]
    
    找出下列名字中带有两个'e'的.
    names = [['Tom', 'Billy', 'Jefferson' , 'Andrew' , 'Wesley' , 'Steven' ,
    'Joe'],['Alice', 'Jill' , 'Ana', 'Wendy', 'Jennifer', 'Sherry' , 'Eva']]
    lst = [ii for i in names for ii in i if ii.count('e') ==2]
    print(lst)
    #结果:
    #['Jefferson', 'Wesley', 'Steven', 'Jennifer']

      生成器表达式和列表推导式的区别:

      1. 列表推导式比较耗内存. ⼀次性加载. 生成器表达式几乎不占用内存. 使用的时候才分配和使用内存

      2. 得到的值不⼀样. 列表推导式得到的是⼀个列表. 生成器表达式获取的是⼀个生成器. 

      生成器的惰性机制:生成器只有在访问的时候才取值. 说白了就是找他要他才给值,不要的话,他是不会执行的

      总结:

        推导式有列表推导式,字典推导式,集合推导式,没有元组推导式

        生成器表达式:(结果 for 变量 in 可迭代对象 if 条件筛选)

        生成器表达式可以直接获取到生成器对象,生成器可以直接进行for循环. 生成器具有惰性机制

  • 相关阅读:
    BZOJ 2002 [Hnoi2010]Bounce 弹飞绵羊(分块)
    BZOJ 4241 历史研究(分块)
    BZOJ 3110 [Zjoi2013]K大数查询(整体二分)
    hdu 5412 CRB and Queries(整体二分)
    POJ2104 K-th Number(整体二分)
    luogu P3157 [CQOI2011]动态逆序对(CDQ分治)
    陌上开花(CDQ分治)
    BZOJ 1176[Balkan2007]Mokia(CDQ分治)
    BZOJ 3626 LCA(离线+树链剖分+差分)
    bzoj1592 Making the Grade
  • 原文地址:https://www.cnblogs.com/dong-/p/9330727.html
Copyright © 2011-2022 走看看