我正在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")

输出:
    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

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

取而代之的是,似乎发生了什么事:x被发送了“字符串”,并打印出来,然后python尝试寻找下一个yield并获得StopIteration

所以我试试这个:
def send_gen():
    x = yield 1
    print("    send_gen(): sent in '{}'".format(x))


gen = send_gen()
print("yielded : {}".format(gen.send(None)))

输出 :
yielded : 1

但是现在没有错误了。将send()分配给yield之后,x似乎没有尝试寻找下一个None语句。

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

最佳答案

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

每当生成器到达yield表达式时,执行就在那里暂停,该表达式在恢复到生成器之前不会在生成器中产生任何东西。 gen.__next__()gen.send()都将从该点恢复执行,yield表达式将生成gen.send()None传递的值。如果有帮助,您可以将gen.__next__()视为gen.send(None)。在这里要实现的一件事是gen.send()使yield首先返回发送值,然后再返回到下一个yield

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

  • gen = send_gen()创建生成器对象。代码在函数的最上方暂停,不执行任何操作。
  • 您可以调用gen.__next__()gen.send(None);生成器启动并执行,直到第一个yield表达式:
    print("    send_gen(): will yield 1")
    yield 1
    

    现在执行暂停。 gen.__next__()gen.send(None)调用现在返回1,即yield 1产生的值。因为生成器现在已暂停,所以尚未进行x = ...分配!只有在再次恢复生成器时才会发生这种情况。
  • 您在第一个示例中调用gen.send("a string"),在第二个示例中不进行任何调用。因此,对于第一个示例,现在恢复了generator函数:
    x = <return value of the yield expression>  # 'a string' in this case
    print("    send_gen(): sent in '{}'".format(x))
    

    现在函数结束,因此StopIteration升高了。

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

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

    关于python - Python : Behaviour of send() in generators,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/36997078/

    10-12 18:38