在 Python 中重构协程

发布于 2025-01-17 12:05:51 字数 2118 浏览 3 评论 0原文

我想通过在其他功能/coroutine中移动其一些代码来重构Coroutine。但是我需要在包含的coroutine中检索其最后一个供稿,而且我不知道如何正确地做到这一点。

这是一个简单的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

,我想通过将可重复使用的零件移至coroutine 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值? 还是更好的重构方法? 祝你今天过得愉快。

I want to refactor a coroutine by moving some of its code in another function/coroutine. But I would need to retrieve its last feed in the containing coroutine, and I've got no idea how to properly do that.

Here's an example with a simpler 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)}")

The result that I expect (and get) is

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

Now, I want to improve my code by moving the reusable part to a coroutine 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

Is there a way to retrieve the correct value of feed ?
Or maybe a better way to refactor ?
Have a nice day.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

征﹌骨岁月お 2025-01-24 12:05:51

也可以从Coroutine返回值。在当前的示例中,这将是:

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

我使用了错误的类型注释(发电机类型提示的第三个元素),这使我认为Coroutine只能返回。

有关信息,这是这两个功能的类型提示:

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

It possible to also return a value from a coroutine. In the the current example, this will be :

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}"

This prints the correct expected output :

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

I was using a wrong type annotation (3rd element of the Generator type hint) that made me think a coroutine could only return None.

For info, here's the type hint for those two functions :

def wait_for_five_turns() -> Generator[float, str, float]: ...
def coroutine() -> Generator[float, str, float]: ...
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文