• Python生成器以及yield语句


    生成器是一种暂缓求值的技术,它可以用来生成一系列的值,但不会一次性生成所有的值,而只在需要的时候才计算和生成一个值。

    通过yield语句构建生成器

    要得到一个生成器,我们需要定义一个函数,这个函数返回一个生成器。这个函数与普通函数不同的地方在于,它使用 yield 来返回值。

    下面这个函数返回一个生成器,该生成器用来产生斐波拉契数:

    # fib函数返回一个生成器
    def fib(max):
        a, b = 0, 1
        while a < max:
            yield a
            a, b = b, a + b
    
    g = fib(10) # 此时斐波拉契数列还没有生成
    # 每次调用生成器的next()方法,就计算下一个要返回的斐波拉契数
    print g.next()
    print g.next()
    print g.next()
    print g.next()
    
    # 可以在循环中使用生成器
    for f in fib(10):
        print f
    

    yield语句用来返回当前的值,然后退出函数。生成器会记住当前的状态,当下次调用next()方法的时候,会从上次的状态开始,直到yield返回一个新的值。

    一些内置函数以及Python的某些语句能够接受一个生成器,在需要的时候调用它的next()方法。

    例如 for...in 语句:

    for f in fib(100):
        print f
    

    又比如内置的list()函数,可以接受一个生成器来得到一个新列表:

    L = list(fib(100))
    

    你当然也可以定义自己的函数,接受一个生成器,在函数内部调用这个生成器的next()方法获取它的值。

    生成器表达式

    如果觉得每次都要定义函数来返回生成器有点麻烦,那么 生成器表达式 能够让你仅用一个表达式就能得到一个生成器。

    >>> g = (i for i in range(10)) # 生成器表达式
    >>> g
    <generator object <genexpr> at 0x103814410>
    

    上面在两个括号中使用了类似循环的表达式,我们把这种括号语法叫生成器字面量,而括号里的表达式也就是生成器表达式。后面有更多各种字面量的介绍。

    生成器表达式适合用在比较简单的情形下,就跟我们需要lambda表达式来快捷得到一个匿名函数一样。

    生成器表达式是如此方便,它的适用范围绝不只是在字面量中,它可以直接作为函数的参数,这点在下面会讲到。

    为什么使用生成器

    生成器只有在每次调用它的next()方法的时候,才计算以及返回下一个值,这有利于节省内存空间。

    例如要处理一个从0到9999的数据集合,我们可以先用range函数得到一个0到9999的列表,然后遍历这个列表逐个处理每个数字,但这样的话,直到我们处理完,这个长度为10000的列表会一直驻留在我们的内存中;但如果使用生成器,只有在每次处理的时候才会让生成器产生一个值,这显然有利于内存利用。

    # 使用列表
    L = [i for i in range(10000)]
    for i in L:
        print i
    
    # 使用生成器
    g = (i for i in range(10000))
    for f in g:
        print f
    

    生成器表达式和列表综合

    1. 生成器表达式使用括号,而列表综合使用中括号。

      >>> g = (i for i in range(10)) # 生成器表达式
      >>> g
      <generator object <genexpr> at 0x103814410>
      >>> L = [i for i in range(10)] # 列表综合表达式
      >>> L
      [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
      >>s1 = {i for i in range(10)} # 集合综合
      >>s1
      >>set([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
      
    2. 列表综合只用在字面量表达式里,生成器表达式却可以作为函数参数。

          >>s2 = set(i for i in range(10))
          >>s2
          >>set([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
      

    set()是一个内置函数,i for i in range(10)是生成器表达式,它返回一个生成器来作为 set 函数的实参。为了证明是返回了一个生成器,我们定义一个函数 get_set(),

    >>> def get_set(g):
            print repr(g)
            print set(g)
    >>get_set(i for i in range(10))
    <generator object <genexpr> at 0x103914460>
    set([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
    

    我们在get_set函数中打印参数g,我们可以看到g确实是一个生成器。

    更多字面量

    其他语言(Java,C#)中都有字面量这个术语,其实Python中也可以使用这个术语,例如下面这些都可以叫做字面量:

    • 字符串字面量,用引号表示,我们经常用这种方法来创建一个字符串。

      s = '用字面量语法表示一个字符串'

    • 列表字面量,用中括号表示。

      L1 = [0,1,2,3,4]
      L2 = [i for i in range(5)] # 使用了列表综合语法

    • 集合字面量,用花括号表示。

      set1 = {0,1,2,3,4}
      set2 = {i for i in range(5)} # 集合综合语法

    • 字典字面量,也用花括号表示,但语法与集合不同。

      d = {1: 'a', 2: 'b', 3: 'c'}

    • 生成器字面量,用括号表示,需要借助生成器表达式。

      g = (i for i in range(10))

  • 相关阅读:
    javascript删除JSON元素
    State ,Getter , Mutation , Action , Module
    mutation和action区别
    图片懒加载
    git命令合并分支代码
    vue 项目生产环境下去重console
    rem px pt em (区别)
    npm 安装依赖 以及dependencies 和 devDependencies的区别
    仿百度搜索
    h5页面在移动端需要注意的一些事情
  • 原文地址:https://www.cnblogs.com/skeeter/p/3453037.html
Copyright © 2020-2023  润新知