基于yield的协程是真正的协程吗?

3

我只是为了练习而实现greenlet API。

from greenlet import greenlet

def test1():
    print 12
    gr2.switch()
    print 34

def test2():
    print 56
    gr1.switch()
   print 78

gr1 = greenlet(test1)
gr2 = greenlet(test2)
gr1.switch()

这是我的混乱代码

def test1():
    tmp1=yield
    print 12
    try:
        gv2.send(1)
    except StopIteration:
        pass
    tmp1=yield
    print 34

def test2():
    tmp2=yield
    print 56
    try:
        gv2.send(1)
    except StopIteration:
        pass
    tmp1=yield
    print 78

gv1=test1()
gv1.next()
gv2=test2()
gv2.next()

gv1.send(1)

显示,

12
56
Traceback (most recent call last):
  File "prog.py", line 26, in <module>
    gv1.send(1)
  File "prog.py", line 5, in test1
    gv2.send(1)
  File "prog.py", line 15, in test2
    gv2.send(1)
ValueError: generator already executing

我不确定是否正确,但看起来在test1向test2发送'1'后,它仍然有一些东西,

不像gevent那样进行控制流切换。 test1仍具有该流程。

如果不是这样,那么我不明白greenlet可以做什么,而python的"协程"无法存在。

我的问题是:

  1. Python协程(基于yield)是真实的事情吗?(与其他语言如lisp、ruby等相比)
  2. 如果是,请给出一些那个spaghetti代码的提示?

你在寻找什么“技巧”?关于什么具体内容? - phant0m
1个回答

7
test2() 的生成器实例正在向自身发送一个值。
def test2():
    tmp2=yield
    print 56
    try:
        gv2.send(1) # this is the offending line
    except StopIteration:
        pass
    tmp1=yield
    print 78

send() 函数会恢复生成器,但是当代码在 test2() 中执行时,它已经在运行了。这就是为什么会出现错误。

你想做的是: gv1.send(1) 吗? 那也不起作用。

原因如下:

  • 当前情况:在你的示例的最后,gv1.send(1) 之前
    • gv1 休眠
    • gv2 休眠
  • 调用 gv1.send(1),这会恢复 gv1
  • gv1 继续执行 gv2.send(1)
  • 这会恢复 gv2
  • gv2 继续执行 gv1.send(1)
  • 正在恢复 gv1,然而,自最后一次恢复以来,gv1 尚未到达 yield 语句。因此,它仍在运行,这也是为什么它会抛出异常的原因。

本质上,区别可以总结如下:

  • greenlets 相互之间具有内在的连接: .switch() 暂停当前正在执行的 greenlet 并将其恢复。
  • 生成器,则完全独立于彼此。其中没有共享的上下文来执行生成器。
    • yield 将 "暂停" 一个生成器。
    • next()/ send() 将恢复已暂停的生成器,调用正在运行的生成器会导致异常。

为什么要访问 gv2(它表示 test2 的一个特定实例)呢?生成器 test2() 应该是自包含的,并且不应对它如何使用做任何假设。如果你决定从其他范围调用生成器,这有什么意义呢?向自己发送值毫无意义:你已经拥有它们了。


哦,这只是个错误,我本意是“gv1.send(1)”,我的测试代码实际上就是这样的。我在想为什么“gv1.send(1)”不起作用。 - from __future__
关于如何成功实现基于yield的“协程”的示例的“提示” - from __future__
@from__future__ 在帖子中已经解释了为什么那样不起作用 ;) 请看“这就是为什么:” - phant0m
是的,我读了它,现在明白为什么会出现异常。那么,如果您不介意,有没有实现第一个的方法?如果Python可以做到这一点,它交互两个协程看起来非常有用。 - from __future__
使用@from__future__,您可以创建第三个生成器,用于在两个生成器之间来回混洗数据。 - phant0m

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接