【问题标题】:python3 send() function in generators生成器中的 python3 send() 函数
【发布时间】:2018-01-19 12:55:55
【问题描述】:

根据docs,send()函数:

"恢复执行并将一个值“发送”到生成器函数中。value 参数成为当前 yield 表达式的结果。send() 方法返回生成器生成的下一个值,如果生成器生成,则引发 StopIteration退出而不产生另一个值。当调用 send() 来启动生成器时,必须以 None 作为参数调用它,因为没有 yield 表达式可以接收该值。"

但我不明白,为什么“值参数成为当前yield表达式的结果”在下面的例子中没有发生:

def gen():
    yield 1
    x = (yield 42)
    print(x)
    yield 2

>>>c=gen() #create generator
>>>next(c) #prints '1' and stop execution, which is caused by yield 1
>>>c.send(100) #prints '42', because 'The send() method returns the next value yielded by the generator'
>>>next(c) #prints 'None' and '2'

尽管我通过 c.send(100) 向它发送了 100,但为什么 x 变量保持“无”? 看起来,右侧的 yield 表达式分两步工作:首先它返回生成器调用者的值,第二个它返回生成器内部发送函数的参数。如果在发送(42)之前添加额外的下一个(c),我会得到预期的行为并且程序打印'100'。从文档中我不清楚,为什么当我调用 send() 时这两个步骤不应该同时发生。

【问题讨论】:

    标签: python python-3.x coroutine


    【解决方案1】:

    那么为什么 x 变量保持“无”尽管我向它发送了 100 c.send(100)?

    因为现在的yield 不是你想的那个,它是以前的那个。

    当您发送 100 时,生成器仍然停在 yield 1,而不是 yield 42,因此 100 将是 yield 1 的结果,而不是 yield 42 的结果。

    为了更清楚地看到这一点,如果您修改生成器以便在z 变量中检索yield 1 的内容,您会看到z 确实包含100:

    >>> def gen():
    ...     z = yield 1
    ...     x = (yield 42)
    ...     print(x, z)
    ...     yield 2
    ... 
    >>> c=gen()
    >>> next(c)
    1
    >>> c.send(100)
    42
    >>> next(c)
    None 100
    2
    >>>
    

    这就是为什么额外的next() 会打印100 的原因。回到你的代码:

    def gen():
        yield 1  # first next(), stops here (before "getting out" of yield)
        x = (yield 42) # second next(), stops here (before "getting out" of yield),
                       # so, when you send(100), then 100 is given to x and execution goes on, so:
        print(x)  # 100 is printed
        yield 2  # ... and 2.
    

    【讨论】:

    • 所以当他们说函数将在下一个yield 处停止时,实际上是在yield 本身,甚至不像x=yield 42 那样在其左侧的部分?我认为这是违反直觉的部分,因为我们习惯于将每一行视为一个原子动作。
    • @Jason 是的,就是这样。 Python 尝试首先评估= 符号右侧的表达式,以便知道它应该存储在x 变量中的内容。右边的表达式是yield,它在评估这个表达式时停止。同一行内可能有“原子操作”(这里是表达式求值的几个步骤)。
    【解决方案2】:

    我想我已经明白了。

    c = gen()
    

    您在c 变量中创建了一个生成器,没有任何生成器的代码被执行。

    next(c)
    

    然后你使用next()函数,next函数转到下一个yield语句谁是yield 1这里。 所以这个yield 返回1 并停止生成器的执行以尝试捕获一个值。

    下一行是 c.send(100),所以 yield 1 正在等待一个值并且你提供了它,但这个值没有被保存。
    send 方法还执行生成器的其余部分,直到下一个 yield 语句是:

    x = (yield 42)
    

    所以这里的 yield 返回 42 并停止生成器程序以尝试捕获一个值。但在那之后你打电话

    next(c)
    

    而且您没有提供值,所以 x 现在是 None。然后执行剩下的代码(直到下一个yield语句,别忘了)

    print(x)
    yield 2
    

    所以它打印x who is None 然后yield 返回 2 并尝试捕获一个值。

    试着写这个

    c = gen()
    next(c)
    next(c)
    c.send(100)
    

    它会起作用(明白为什么!)

    【讨论】:

      【解决方案3】:

      来自文档: value 参数成为当前 yield 表达式的结果。 send() 方法返回生成器产生的下一个值。

      假设我们有以下无限生成器:

      def infgen():
          a = 0
          while True:
              a = yield a
              a += 1
      
      generato = infgen()
      
      generato.send(None)
      

      输出:0

      .send(None) 的执行与 next() 几乎相同

      现在我们得到 a = 0 的第一个值和

      代码在这里停止a = yield a

      因为 value 成为当前 yield 表达式的结果

      yield 表达式是 yield a。 yielded 表达式的结果是 a

      generato.send(5)
      

      .send(5) 我们将 5 发送到生成器中的 a 并继续:

      yield 表达式的结果是 a = 5

      a = 5

      a += 1

      使用 a = 5 + 1 = 6 进入 while 循环

      然后停止并产生一个,其中 a 是 6

      输出:6

      【讨论】:

        猜你喜欢
        • 2016-08-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-11-17
        • 1970-01-01
        • 2017-04-06
        • 1970-01-01
        • 2016-12-16
        相关资源
        最近更新 更多