生成器

定义生成器
  在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

 

posted @ 2018-11-13 17:31  从python开始  阅读(249)  评论(0编辑  收藏  举报