【问题标题】:python - what does yield (yield) do?python - 产量(产量)做什么?
【发布时间】:2018-02-04 13:59:41
【问题描述】:

从 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

为什么每一秒的“发送”都不做任何事情?

【问题讨论】:

  • 难以重现:我在 send(3) 时得到“StopIteration”...,即当我将 gen2 切换为 gen
  • 也是你的问题“产量(产量)做什么?” (我可以回答)或者“为什么每一秒的‘发送’都不做任何事情?” (我无法回答)
  • @Jean-FrançoisFabre 我完全明白他所展示的内容。
  • 对于您使用各种.send 调用的示例,您是指在交互式提示符处一一执行这些命令时出现的“输出”吗?如果您以非交互方式运行代码,则根本不会有任何输出。

标签: python generator yield


【解决方案1】:

yield (yield) 首先从内部yield 产生None。然后它从sendnext 接收一个值。内部的yield 计算出这个接收到的值,而外部的yield 立即产生这个值。


每个yield 在概念上包含两个部分:

  1. sendnext的调用者传输一个值。
  2. 接收来自下一个sendnext 调用的值。

同样,每个sendnext 在概念上都有两个部分:

  1. 将值传输到生成器当前暂停的yield 表达式。 (对于next,此值为None。)
  2. 从下一个yield 表达式接收一个值。

系统中最令人困惑的部分可能是这些部分是交错的。 yield的两部分对应sendnext的两个不同的调用,sendnext的两部分对应两个不同的yields。

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

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

事情是这样发展的:

Inside the generator                      Outside the generator

第 1 步

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

第 2 步

                                          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

第 3 步

                                          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

第 4 步

                                          g.send(2) sends 2 to g
g executes the "receive" phase
  of the outer yield, receiving 2
g reaches the end of gen and raises
  a StopIteration
                                          g.send(2) raises the StopIteration
                                          from g

【讨论】:

    【解决方案2】:

    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 时,会产生一个值;在您的情况下,对于内部收益率,该值为 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“成为”发送的值,并立即由链中的下一个yield产生。这种情况一直持续到所有产量都用尽并引发 StopIteration。

    之前有人问过类似的问题。我的印象是,由于人们期望send“只发送”一个值,所以容易出现混淆。但事实并非如此。使用send 推进生成器并产生下一个结果,就像使用next 一样。你可以认为next(gen) 等同于gen.send(None)

    【讨论】:

      猜你喜欢
      • 2016-06-08
      • 2015-11-14
      • 2016-08-25
      • 2011-03-12
      • 2017-04-17
      • 1970-01-01
      • 2013-09-14
      • 2021-02-23
      • 2019-06-12
      相关资源
      最近更新 更多