3

生成器简介

python中,含有yield关键字的对象就是一个生成器,每次调用next方法时会执行到yield后面的语句,然后返回yield后面代码块的执行结果。其实也可以调用send方法
下面给个例子方便理解。

next方法

def foo():
    bar_a = yield 1         # bar_a是语句块(yield 1)的返回值,默认为None
    bar_b = yield bar_a
    yield "最后一个值,再迭代就要报StopIteration了"

f = foo()                   # 创建生成器,此时没有执行foo()里的任何语句
print(next(f))              # 从foo()里进入,一直执行到(yield 1)处,此时变量bar_a还没有创建
print(next(f))              # 先将语句块(yield 1)的返回值赋值个bar_a,此时bar_a的值是None。
                            # 然后执行到语句块(yield bar_a),bar_b也还没有被创建
print(next(f)

输出:

>>>1
>>>None
>>>最后一个值,再迭代就要报StopIteration了

可以看出,f = foo()创建生成器时,每次执行到yield时,会跳出去并将yield关键字后面的内容返回给调用者。下一次有别的调用者再次调用生成器时,会先恢复生成器上次的机器状态,再接着执行指导遇到yield或者元素迭代完毕。
而且我们可以看到bar_abar_b是语句yield 1yield bar_a的返回值,注意:不是生成器的返回值
这里有个比较绕的地方,我们用bar_a = yield 1做分析:

  • 1是生成器的返回值。因为生成器返回yield后面的代码块
  • bar_a是语句yield 1的返回值,这就好比我们写

    a = print('my lover')
    print('a的值是:', a)

    会输出:

    >>>my lover
    >>>a的值是: None
    

send方法

def foo():
    bar_a = yield 1
    bar_b = yield bar_a
    yield "最后一个值,再迭代就要报StopIteration了"

f = foo()
print(f.send(None))
print(f.send("my lover"))
print(next(f))

输出:

>>>1
>>>my lover
>>>最后一个值,再迭代就要报StopIteration了

这里f.send(None)是初始化生成器,和next(f)的效果一模一样。但是不推荐这么写,因为不规范。
注意输出的第二行是字符串my lover,而不是None。这是因为send函数带有一个参数,这个参数会覆盖yield 1语句的返回值,也就是bar_a的值现在不是None了。

FAQ

官网提到,当我们创建一个生成器时,第一次调用只能用next()或者send(None)。因为此时send传入其他参数也没有yield语句去接收。
这句话我看不懂,说的好像传入None就有yield来接收似的。各位如果明白的欢迎指点。
原文和传送门如下:

官网传送门

Resumes the execution and “sends” a value into the generator function. The value argument becomes the result of the current yield expression. The send() method returns the next value yielded by the generator, or raises StopIteration if the generator exits without yielding another value. When send() is called to start the generator, it must be called with None as the argument, because there is no yield expression that could receive the value.
Google翻译版本:
恢复执行并将值“发送”到生成器函数中。所述 值参数成为当前产量表达的结果。该 send()方法返回由生成器产生的下一个值,或者StopIteration如果生成器退出而不产生另一个值则引发。当send()调用启动生成器时,必须将其None作为参数调用,因为没有可以接收该值的yield表达式。

更新:
2018-11-24
对生成器使用send(None)方法,解释器在底层会调用__next__方法,也就是next()方法


bingo
16 声望5 粉丝