Python 中的产量问题 |使用辅助函数

Problems with yield in Python | Using helper functions

给定一个包含很多单词的字符串,我想颠倒单词的顺序。如果输入是 Thanks for all the fish 输出应该是 fish the all for Thanks.

我正在尝试使用 生成器 来解决此问题,以避免创建新列表。 我提出了以下解决方案:

from itertools import islice

def foo(s, begin, end):
    for elem in islice(s, begin, end):
        yield elem

def baz(s):
    i = len(s)
    j = len(s)

    while True:
        i -= 1
        if i == -1:
            foo(s, i+1, j)
            break
        if s[i] == ' ':
            foo(s, i+1, j)
            yield ' '
            j = i


s = "Thanks for all the fish"
g = baz(s)
for x in g: print(x, end='')

然而,这段代码的输出是" "(一个只有空格的字符串)。

另一方面,如果我直接 print 元素而不是 yield 它们,它会起作用:

from itertools import islice

def foo(s, begin, end):
    for elem in islice(s, begin, end):
        print(elem, end='')

def baz(s):
    i = len(s)
    j = len(s)

    while True:
        i -= 1
        if i == -1:
            foo(s, i+1, j)
            break
        if s[i] == ' ':
            foo(s, i+1, j)
            print(' ', end='')
            j = i


s = "Thanks for all the fish"
g = baz(s)

这有效并有输出 fish the all for Thanks。但我不想仅仅能够打印它,我想要一个正确的生成器。

最后,我发现如果避免调用 foo 它也有效:

from itertools import islice

def baz(s):
    i = len(s)
    j = len(s)

    while True:
        i -= 1
        if i == -1:
            for elem in islice(s, i+1, j):
                yield elem
            break
        if s[i] == ' ':
            for elem in islice(s, i+1, j):
                yield elem
            yield ' '
            j = i


s = "Thanks for all the fish"
g = baz(s)
for x in g: print(x, end='')

这个输出是fish the all for Thanks。 这个版本给了我我想要的(一个正确的生成器),但我在它的函数中重复代码。

关于 yield 的作用,我在堆栈交换线程上写了红色,但我可能理解错了。 我的理解是,生成器将 运行 直到找到下一个 yield 语句(或函数结束)。 这个问题让我觉得这不是它的工作原理,但除此之外我一无所知。

如何使用第一个代码片段中的辅助函数来解决这个问题?

您可以使用 yield from foo(...)(另请参见 PEP-380 on "Syntax for Delegating to a Subgenerator")或 for whatever in foo(...): yield whatever 来成功地 yield 来自生成器的元素:

def baz(s):
    i = len(s)
    j = len(s)

    while True:
        i -= 1
        if i == -1:
            yield from foo(s, i+1, j)    # yield from syntax
            break
        if s[i] == ' ':
            for item in foo(s, i+1, j):  # for-loop over generator
                yield item
            yield ' '
            j = i

但是,正如@trentcl 在评论中指出的那样,在您的情况下,您不需要 foo-helper 函数,因为它基本上也执行 islice 所做的事情。所以你可以简单地将 foo 替换为 islice:

def baz(s):
    i = len(s)
    j = len(s)

    while True:
        i -= 1
        if i == -1:
            yield from islice(s, i+1, j)    # yield from syntax
            break
        if s[i] == ' ':
            for item in islice(s, i+1, j):  # for-loop over generator
                yield item
            yield ' '
            j = i