zoukankan      html  css  js  c++  java
  • 生成器

    定义生成器
      在python中,生成器就是一种数据类型,它自动实现了迭代器协议,生成器有两种表现形式:生成器表达式和生成器函数。
      生成器表达式:元组解析式

    >>> tu = (x**2 for x in range(5))    # 元组解析式定义的生成器
    >>> tu    生成器对象定义时,并没有内容,只有在开始执行后才会通过执行__next__取值
    <generator object <genexpr> at 0x10bc1da98>
    >>> list(tu)
    [0, 1, 4, 9, 16]
    >>> tu2 = (x*2 for x in range(3))    # 生成器对象就是迭代器
    >>> tu2.__next__()    # 生成器对象有__next__属性,就是迭代器
    0
    >>> tu2.__next__()
    2
    >>> tu2.__next__()
    4
    >>> tu2.__next__()
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    StopIteration

      生成器函数:定义生成器函数必须使用yield关键字。在python中,yield关键字是生成器的标志。

    >>> def g():    # 使用关键字yield定义生成器
    ...     yield 1
    ...     yield 2
    ...     yield 3
    ... 
    >>> ge = g()    # 执行函数,产生生成器对象
    >>> ge
    <generator object g at 0x10bc1d9a8>    # 生成器对象
    >>> ge.__next__()    # 生成器对象也有__next__属性,生成器也是迭代器
    1
    >>> ge.__next__()
    2
    >>> ge.__next__()
    3
    >>> ge.__next__()    # 抛出__next__的异常
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    StopIteration

      含有yield关键字的函数是一个生成器对象,这个生成器对象也是迭代器。生成器是一种用普通函数语法定义的迭代器。
      yield语句的作用就是在调用的时候返回相应的值。详解上面的运行过程:
        1.ge = g():ge引用生成器对象;
        2.ge.__next__():由__next__方法触发生成器的执行,遇到了第一个yield语句,将值返回,并暂停执行(挂起);
        3.ge.__next__():由__next__方法触发生成器的执行,从上次暂停的位置开始,继续向下执行,遇到yield语句,将值返回,又暂停,而不是从头开始执行;
        4.ge.__next__():重复上面的操作;
        ge.__next__():从上面的暂停位置开始,继续向下执行,但后面没有可执行的对象了,于是__next__()发出StopIteration异常。
      yield除了作为生成器函数的标志之外,还有一个功能:返回值。
      生成器对象定义时,并没有内容,只有在开始执行后才会通过执行__next__()方法在生成器中取值,并且生成器是一次性的,只能被取值一次。

    >>> t = [1, 2, 3, 4, 5]
    >>> t1 = (i for i in t)    # 通过t产生生成器t1,此时t1只是声明的生成器对象,没有执行其内容
    >>> t2 = (j for j in t1)    # 通过t1产生生成器t2,此时t2只是声明的生成器对象,产生它的t1并未被执行
    >>> list(t1)    # 将t1转换为列表,此时t1第一次执行,且只能取值一次
    [1, 2, 3, 4, 5]
    >>> list(t2)    # t1已被取值,执行t2时再从t1中取不到值了
    []

    yield
      yield在函数中具有返回值的功能,它和return的区别是,return将值返回后,函数结束,若return语句后面还有语句,则return语句后面的内容不再执行。而yield将值返回后,函数暂停执行,并记住当前的位置,等待下一次的继续执行。总的来说,普通函数止于return,生成器函数,执行时遇到yield则挂起。

    >>> def r_yield(n):    # 定义生成器函数
    ...     print('start...')
    ...     while n > 0:
    ...         print('before yield')
    ...         yield n    # yield生成器标志并返回值
    ...         n -= 1
    ...         print('after yield')
    ... 
    >>> r = r_yield(3)    # 引用生成器对象,此时没有执行函数体内语句
    >>> for i in r:    # for循环,调用__next__依次取值
    ...     print(i)
    ... 
    start...
    before yield
    3    # 遇到yield,返回值并暂停
    after yield    # 从上次暂停的位置开始继续执行
    before yield
    2    # 再次遇到yield,返回值并暂停
    after yield    # 重复上面的过程
    before yield
    1
    after yield

    send
      在生成器中,触发生成器执行的除了有生成器中魔术方法的__next__方法和内建函数next()之外,还有一个生成器的自带普通方法:send()方法。
      send方法的作用有两个:
        1.触发生成器,使生成器从当前位置继续向下执行,等同于__next__方法;
        2.赋值的作用,给当前挂起的位置的yield赋值,yield的作用除了返回值之外(相当于return),还可以赋值给变量,而send可以将它的参数传给当前挂起位置的yield,再由yield赋值给函数内的局部变量。

    >>> def test():
    ...      print('开始执行啦')
    ...      print('执行第一次')
    ...      yield 1
    ...      print('执行第二次')
    ...      second = yield 2    # 此处yield既返回值,也可给变量second赋值
    ...      print('send传过来的值:', second)
    ...      print('执行第三次')
    ...      yield 3
    ...      print('执行结束啦')
    ... 
    >>> t = test()
    >>> t.__next__()    # __next__方法触发生成器
    开始执行啦
    执行第一次
    1
    >>> next(t)    # next()函数触发生成器
    执行第二次
    2
    >>> t.send('send传值')   # send方法触发生成器,并将参数传给当前挂起位置的yield,由yield赋变量second
    send传过来的值: send传值    # send既触发了下一次的执行,又承接了上一次的内容即传值
    执行第三次
    3
  • 相关阅读:
    函数的对象
    函数的调用
    函数的参数
    函数的返回值
    定义函数的三种方式
    网络的瓶颈效应
    编程语言分类
    计算机操作系统
    【建议收藏】2020最全阿里,腾讯,美团面试题总结(附答案整理)
    建议收藏!2020阿里面试题(JVM+Spring Cloud+微服务)上
  • 原文地址:https://www.cnblogs.com/wgbo/p/9953403.html
Copyright © 2011-2022 走看看