zoukankan      html  css  js  c++  java
  • 16 python 初学(生成器)

     列表生成器(列表生成式):

    使用此种方式生成的列表会放在内存中占用内存

    a = [x*2 for x in range(1, 11)]
    print(a)
    
    # >>> [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
    # 完成3步:1. 从列表中取出每一个元素 
    #               2. 对每一个元素操作 
    #               3. 放回列表中


    # 这里对函数的操作,即 x*2 也可以用函数来代替
    # def f(n):
    # return n**2
    #
    # a = [f(x) for x in range(1, 11)]
    # print(a)
     

    生成器:值不在生成器中。(比喻:生成器是厨师,不调用的话就不给你做饭,即生成数据。)

    1. 优点是不占用内存,因此不能像列表一样直接根据索引取元素,只能按顺序一个一个取
    2. 生成器是可迭代对象 iterable。(元组,字典,列表,集合,生成器都是可迭代对象) 
    3. 什么是可迭代对象:从现象上看能进行 for 循环的都是可迭代对象,但是本质上有_iter_()方法的才是可迭代对象。
    4. 生成器最难理解的是:generator 和函数的执行流程不一样。函数是顺序执行,遇到 return 语句或者最后一行函数语句就返回。而变成 generator 的函数,在每次调用next()的时候执行,遇到 yield 语句返回,再次执行时从上次返回的 yield 语句处继续执行。
    5. 生成器在创建的时候就已经决定了能计算出值的个数,调用 next 的次数超过这个值就会报StopIteration

    生成器有两种实现方式:

     

    # 第一种,使用小括号方式生成
    s = (x*x for x in range(1, 11))  # 创建一个生成器
    print(s)   # <generator object <genexpr> at 0x000000FD2251A318>
    
    
    # 第二种,使用 yield
    # foo() 是一个生成器对象。只要有 yield,foo()这种写法就不会再被当作函数执行
    def foo():
        print('ok1')
        yield 1
        print('ok2')
        yield 2
    #     没有return,默认 return None

     

    既然说了生成器里面是没有值的,那么我们想要取得值的时候怎么办呢?用什么方法呢?(我感觉生成器就像是造出来的一个有生成值的作用的机器,你不去启动它,他就不会生成值。我们让生成器计算出值所调用的方法,就是一个启动机器生成值的办法。)

    有以下方法:

    1. 调用内部函数__next__()方法,这个时候生成器会帮我们计算出第一个值。接着调用,接着生成第 2 个值.....

        但是不建议调用此方法,因为这是内部特殊的方法

    s = (x*x for x in range(1, 11))  # 创建一个生成器
    
    print(s.__next__())
    print(s.__next__())
    
    # >>> 1
    # >>> 4

    2. 使用 next()函数

    #python2 中写法有区别:s.next()
    print(next(s))  # 等价于 s.__next__()  >>> 1
    print(next(s))
    #>>>1
    #>>>4

    记录一个自己遇到的问题:

     1 # foo() 是一个生成器对象。
     2 def foo():
     3     print('ok1')
     4     yield 1
     5     print('ok2')
     6     yield 2
     7 
     8  # next(foo()) 是等于 1 ,yield 相当于一个 return, 把 1 返回给了next(foo())。但是此处我们并没有将  next(foo()) 进行打印,因此不会输出 1
     9 
    10 next(foo()) 
    11 next(foo())
    12 
    13 #疑问抛出:观察下面输出,为什么总是输出 ok1 呢,难道不应该接着向下执行吗??
    14 #>>>ok1
    15 #>>>ok1
    16 
    17 
    18 # 再看另一种方式,这种方式便可以输出 ok1,ok2。
    19 # 上面是 next(foo()),这样每次执行这个语句的时候,foo()会被重新初始化,因此每次都从第一步开始执行
    20 g = foo()
    21 next(g)
    22 next(g)
    23 #>>>ok1
    24 #>>>ok2
    View Code

    学习完了 next()函数,接下来学习 send()函数。

    在使用send()函数前,必须要进入这个生成器内。

    send函数其实和next()一样,都是进入生成器内,并且也是遇到 yield 返回,返回值也是 yield 的值。但是send多了一个功能,send('hello')函数是传一个值进去,但是这个值要给谁呢?

    答案:传给 yield 前边的变量。 如 count = yield 1。 如果 yield 前边没有变量来接收这个值,也不会报错。

    
    
    def foo():
    print('ok1')
    count = yield 1
    print(count)
    print('ok2')
    yield 2

    g = foo()

    s = g.send(None) # 相当于next(b) 第一次 send 前如果没有 next,但是又需要进入这个生成器,只能传一个 send(None)。
    print(s)
    s = g.send('eeee')
    print(s)

    output:

    ok1
    1
    eeee
    ok2
    2

    
    

     生成器实现斐波那契数列:

    def fib(max):
        n, before, after = 0, 0, 1
        while n < max:
            # print(before)
            yield before
            before, after = after, before + after
            n += 1
    
    g = fib(8)
    
    for i in g:
        print(i)
    View Code

    yield 实现伪并发(包子代码)

    import time
    def consumer(name):
        print("%s 准备吃包子啦!" %name)
        while True:
           baozi = yield
    
           print("包子[%s]来了,被[%s]吃了!" %(baozi,name))
    
    
    def producer(name):
        c = consumer('A')
        c2 = consumer('B')
        c.__next__()
        c2.__next__()
        print("老子开始准备做包子啦!")
        for i in range(10):
            time.sleep(1)
            print("做了2个包子!")
            c.send(i)
            c2.send(i)
    
    producer("alex")
    View Code

     

  • 相关阅读:
    js参数自定义
    分页插件--记录
    .net mvc接收参数为null的解决方案
    c#枚举转字典或表格
    openlayers添加弹出框
    openlayers按坐标点播放
    openlayers轨迹匀速播放
    MyEclipse配置进行Hibernate逆映射
    BIO,NIO,AIO
    Git遇到的一点错误
  • 原文地址:https://www.cnblogs.com/mlllily/p/10261742.html
Copyright © 2011-2022 走看看