在 Python 中重构协程

Refactor a coroutine in Python

我想重构一个协同程序,将它的一些代码移到另一个function/coroutine。但是我需要在包含协程中检索它的最后一个提要,但我不知道如何正确地做到这一点。

这是一个更简单的协程示例。

def coroutine():
    """Yield a string containing the number sent. If 5 is sent, block for 5 turns."""
    feed = yield 0
    while True:
        if feed == 5:
            for i in range(5, 0, -1):
                feed = yield f"Blocked for {i} turns"
        feed = yield f"Received {feed}"


if __name__ == "__main__":
    my_coroutine = coroutine()
    next(my_coroutine)  # Init the coroutine

    # Print the coroutine results for inputs from 0 to 14
    for j in range(15):
        print(f"{j} -> {my_coroutine.send(j)}")

我期望(并得到)的结果是

0 -> Received 0
1 -> Received 1
2 -> Received 2
3 -> Received 3
4 -> Received 4
5 -> Blocked for 5 turns
6 -> Blocked for 4 turns
7 -> Blocked for 3 turns
8 -> Blocked for 2 turns
9 -> Blocked for 1 turns
10 -> Received 10
11 -> Received 11
12 -> Received 12
13 -> Received 13
14 -> Received 14

现在,我想通过将可重用部分移动到协程来改进我的代码 wait_for_five_turns :

def wait_for_five_turns():
    """Block for 5 turns."""
    for i in range(5, 0, -1):
        feed = yield f"Blocked for {i} turns"


def coroutine():
    """Yield a string containing the number sent. If 5 is sent, block for 5 turns."""
    feed = yield 0
    while True:
        if feed == 5:
            yield from wait_for_five_turns()
        # "feed" is not up to date ! And it's logic, but bothersome.
        feed = yield f"Received {feed}"
0 -> Received 0
1 -> Received 1
2 -> Received 2
3 -> Received 3
4 -> Received 4
5 -> Blocked for 5 turns
6 -> Blocked for 4 turns
7 -> Blocked for 3 turns
8 -> Blocked for 2 turns
9 -> Blocked for 1 turns
10 -> Received 5  <----- Not what I want
11 -> Received 11
12 -> Received 12
13 -> Received 13
14 -> Received 14

有没有办法检索 feed 的正确值? 或者也许是更好的重构方法? 祝你有个愉快的一天。

也可以 return 来自协程的值。在当前示例中,这将是:

def wait_for_five_turns():
    """Block for 5 turns."""
    for i in range(5, 0, -1):
        feed = yield f"Blocked for {i} turns"
    return feed


def coroutine():
    """Yield a string containing the number sent. If 5 is sent, block for 5 turns."""
    feed = yield 0
    while True:
        if feed == 5:
            feed = yield from wait_for_five_turns()
        feed = yield f"Received {feed}"

这会打印正确的预期输出:

0 -> Received 0
1 -> Received 1
2 -> Received 2
3 -> Received 3
4 -> Received 4
5 -> Blocked for 5 turns
6 -> Blocked for 4 turns
7 -> Blocked for 3 turns
8 -> Blocked for 2 turns
9 -> Blocked for 1 turns
10 -> Received 10  <-- Yeah.
11 -> Received 11
12 -> Received 12
13 -> Received 13
14 -> Received 14

我使用了错误的类型注释(Generator 类型提示的第 3 个元素),这让我认为协程只能 return None.

有关信息,这是这两个函数的类型提示:

def wait_for_five_turns() -> Generator[float, str, float]: ...
def coroutine() -> Generator[float, str, float]: ...