python - yield(yield)有什么作用?

时间:2017-08-26 21:07:35

标签: python generator yield

自python 2.5以来,send()throw()close()能够进入生成器。在定义的生成器内部,可以通过执行以下操作来“捕获”发送的数据:

def gen():
    while True:
        x = (yield)
        if x == 3:
            print('received 3!!')
            break
        else:
            yield x

我想要玩的是做类似的事情:

def gen2():
    while True:
        yield (yield)

注意到它是一个合法的发电机做某事.. 我想弄清楚的第一件事是:

这样的写作有用吗?

同样做以下事情:

g = gen2()
next(g)
g.send(10) # output: 10
g.send(2) # output: nothing
g.send(3) # output: 3
g.send(44) # output: nothing

为什么每秒'发送'都没有做任何事情?

2 个答案:

答案 0 :(得分:7)

yield (yield)首先从内部None获得yield。然后,它会收到sendnext的值。内部yield评估此收到的值,外部yield立即产生该值。

每个yield在概念上分为两部分:

  1. 将值传送给sendnext
  2. 的来电者
  3. 从下一个sendnext来电中收到一个值。
  4. 同样,每个sendnext在概念上分为两部分:

    1. 将值传输到生成器当前暂停的yield表达式。 (None的此值为next。)
    2. 从下一个yield表达式中接收值。
    3. 系统中最令人困惑的部分可能是这些部分交错。 yield的两个部分对应sendnext的两个不同调用,sendnext的两个部分对应两个不同{ {1}}秒。

      如果我们通过一个简单的例子:

      yield

      以下是事情的解决方法:

      def gen():
          print('Not ran at first')
          yield (yield)
      
      g = gen()  # Step 1
      print(next(g))  # Step 2
      print(g.send(1)  # Step 3
      g.send(2)  # Step 4
      

      第1步

      Inside the generator                      Outside the generator
      

      第2步

                                                g calls gen()
      g returns a generator object 
      without executing the print
      just yet statement.
                                                >>> g
                                                <generator object gen at 0x7efe286d54f8>
      

      第3步

                                                next(g) sends None to g
      g receives None, ignores it
        (since it is paused at the start
         of the function)
      
      g prints ('not ran at first')
      
      g executes the "transmit" phase
        of the inner yield, transmitting
        None
                                                next(g) receives None
      

      第4步

                                                g.send(1) sends 1 to g
      g executes the "receive" phase
        of the inner yield, receiving 1
      g executes the "transmit" phase
        of the outer yield, transmitting 1
                                                g.send(1) receives 1 from g
      

答案 1 :(得分:3)

yield是一个表达式。表达式的值是使用.send发送的值的值,如果没有发送任何内容,则为None(包括使用next而不是.send)。 .send是方法调用,因此当然也返回一个值,该值是生成器产生的值。换句话说,每次.send时,都会产生一个值(可能是None),并且每次yield时,都会发送一个值(可能为None)。

这是一个简单的例子:

def gen():
    sent1 = yield 1
    print(sent1, "was sent")
    sent2 = yield 2
    print(sent2, "was sent")
    print("Reached end of generator")

g = gen()
print(next(g), "was yielded")
print(g.send("A"), "was yielded")
print(g.send("B"), "was yielded")
next(g)

# output
1 was yielded
A was sent
2 was yielded
B was sent
Reached end of generator
# StopIteration is raised here

在您的示例中,第一个next产生无,因为第一个yieldyield (yield)中的内部产量(即括号中的一个)。第一个send将10作为值yield传递。您send的每个后续值都将成为其中一个收益的值。某些send调用没有产生输出的原因是内部yield不指定任何值,因此它产生None。如上所述,当您致电send时,会产生一个值;在您的情况下,内部yield的值为None,因此在交互式提示符处不显示任何输出。另一方面,外部收益率确定了一个值,即内部收益率的结果。因此,当您send内部yield的值时,它将在下一次迭代时由外部yield产生。 (我假设您在交互式提示符处引用输出;如果您将代码作为脚本运行,则根本不会有输出,因为您从不print任何内容或以其他方式生成显式内容输出。)

这是另一个可能有启发性的例子:

def gen():
    yield (yield (yield (yield "WHOA")))

>>> g = gen()
>>> next(g)
'WHOA'
>>> g.send(1)
1
>>> g.send(2)
2
>>> g.send(3)
3
>>> g.send(4)
Traceback (most recent call last):
  File "<pyshell#11>", line 1, in <module>
    g.send(4)
StopIteration

请注意,每次发送值时,都会立即返回。这是因为每个yield都会产生更深层嵌套的yield的值。每个yield&#34;变为&#34;发送的值,并立即由链中的下一个yield产生。这种情况一直持续到所有产量都用尽并且提高了StopIteration。

以前曾有人问过类似的问题。我的印象是,人们期望send发送&#34;只是发送&#34;一个值。但事实并非如此。使用send推进生成器并生成下一个结果,就像使用next一样。您可以将next(gen)视为等同于gen.send(None)