Python:send()在生成器中的行为

peo*_*les 5 python yield generator coroutine python-3.x

我正在python 3中尝试生成器,并编写了这个相当人为的生成器:

def send_gen():
    print("    send_gen(): will yield 1")
    x = yield 1
    print("    send_gen(): sent in '{}'".format(x))
    # yield  # causes StopIteration when left out


gen = send_gen()
print("yielded {}".format(gen.__next__()))

print("running gen.send()")
gen.send("a string")
Run Code Online (Sandbox Code Playgroud)

输出:

    send_gen(): will yield 1
yielded 1
running gen.send()
    send_gen(): sent in 'a string'
Traceback (most recent call last):
  File "gen_test.py", line 12, in <module>
    gen.send("a string")
StopIteration
Run Code Online (Sandbox Code Playgroud)

因此gen.__next__()到达该行x = yield 1并产生1。我认为x将被分配给None,然后gen.send()将查找下一个 yield因为x = yield 1“已使用”的语句,然后得到一个StopIteration

相反,什么似乎已经发生的是,x被发送“的字符串”,这是印刷,然后再蟒蛇试图寻找下一个yield,并得到一个StopIteration

所以我试试这个:

def send_gen():
    x = yield 1
    print("    send_gen(): sent in '{}'".format(x))


gen = send_gen()
print("yielded : {}".format(gen.send(None)))
Run Code Online (Sandbox Code Playgroud)

输出:

yielded : 1
Run Code Online (Sandbox Code Playgroud)

但是现在没有错误了。分配给后,send()似乎没有尝试寻找 yield一条语句。xNone

为什么行为略有不同?这与我如何启动发电机有关吗?

Mar*_*ers 5

行为没有什么不同;yield在第二种设置中,您从未超越生成器中的第一个表达式。请注意,StopIteration不是一个错误 ; 这是正常的行为,只要发电机结束,就会发出预期的信号。在第二个示例中,您从未到达生成器的末端。

每当生成器到达yield表达式时,执行就在那里暂停,直到恢复生成器之后,表达式才能在生成器内部不产生任何东西。任一gen.__next__()gen.send()将从该点既恢复执行,与yield表达任一制造在由传递的值gen.send(),或None。你可以看到gen.__next__()作为一个gen.send(None)没有什么帮助。在这里有一点要明白的是,gen.send()yield返回值SENT 第一,并随后发电机继续到下一个yield

因此,考虑到您的第一个示例生成器,将发生以下情况:

  1. gen = send_gen()创建生成器对象。代码在函数的最上方暂停,不执行任何操作。

  2. 您可以致电gen.__next__()gen.send(None); 生成器启动并执行直到第一个yield表达式:

    print("    send_gen(): will yield 1")
    yield 1
    
    Run Code Online (Sandbox Code Playgroud)

    现在执行暂停。该gen.__next__()gen.send(None)电话现在回到1,该值产生的yield 1。由于生成器现在已暂停,x = ...因此无法进行分配!只有在再次恢复生成器时才会发生这种情况。

  3. 你叫gen.send("a string")你的第一个例子,不作任何在第二个电话。因此,对于第一个示例,现在恢复了generator函数:

    x = <return value of the yield expression>  # 'a string' in this case
    print("    send_gen(): sent in '{}'".format(x))
    
    Run Code Online (Sandbox Code Playgroud)

    现在函数结束,因此StopIteration引发。

因为在第二个示例中您从未恢复过生成器,所以没有达到生成器的末尾并且不会StopIteration引发异常。

请注意,由于生成器从函数的顶部开始,因此此时没有yield表达式返回您发送的内容,gen.send()因此第一个gen.send()值必须始终为,None否则将引发异常。最好使用显式gen.__next__()(或next(gen)函数调用)来“生成”生成器,以使其在第一个yield表达式处暂停。