在同一函数中使用return和yield

103 投票
4 回答
57303 浏览
提问于 2025-04-30 00:10

在Python中,当在同一个函数里同时使用yield和return时,会发生什么呢?

def find_all(a_str, sub):
    start = 0
    while True:
        start = a_str.find(sub, start)
        if start == -1: return
        yield start
        start += len(sub) # use start += 1 to find overlapping matches

这样的话,这个函数还是生成器吗?

暂无标签

4 个回答

1

注意:在下面的例子中,你不会看到StopIteration这个错误。

def odd(max):
    n = 0
    while n < max:
        yield n
        n = n + 1
    return 'done'


for x in odd(3):
    print(x)

因为for循环会自动处理这个错误。这是它停止的信号。

不过,你可以用下面的方式来捕捉这个错误:

g = odd(3)

while True:
    try:
        x = next(g)
        print(x)
    except StopIteration as e:
        print("g return value:", e.value)
        break
18

有一种方法可以在一个函数中同时使用yield和return,这样你就可以返回一个值或者一个生成器。

这可能没有你想象中的那么简洁,但它确实能达到你想要的效果。

下面是一个例子:

def six(how_many=None):
    if how_many is None or how_many < 1:
        return None  # returns value

    if how_many == 1:
        return 6  # returns value

    def iter_func():
        for count in range(how_many):
            yield 6
    return iter_func()  # returns generator
45

没错,它仍然是一个生成器。你可以用空的 return 或者 return None 来结束一个生成器函数。这就相当于抛出了一个 StopIteration(想了解更多可以看看@NPE的回答)。

需要注意的是,在3.3版本之前的Python中,带有非None参数的 return 会导致 SyntaxError 错误。

正如@BrenBarn在评论中提到的,从Python 3.3开始,返回值现在会传递给 StopIteration

来自PEP 380的内容:

在生成器中,语句

return value

在语义上等同于

raise StopIteration(value)
101

没错,它仍然是一个生成器。这里的 return 几乎等同于抛出 StopIteration

PEP 255 中有详细说明:

说明:返回

生成器函数也可以包含这样的返回语句:

"return"

需要注意的是,在生成器的主体中,返回语句后面不能有表达式列表(当然,它们可以出现在生成器内部的非生成器函数中)。

当遇到返回语句时,控制流程就像在任何函数返回时一样,会执行相应的 finally 语句(如果有的话)。然后会抛出一个 StopIteration 异常,表示迭代器已经用完了。如果控制流在没有明确返回的情况下到达生成器的末尾,也会抛出 StopIteration 异常。

需要注意的是,返回的意思是“我完成了,没有什么有趣的东西可以返回”,这对于生成器函数和非生成器函数都是一样的。

另外,返回并不总是等同于抛出 StopIteration:它们的区别在于如何处理外部的 try/except 结构。例如,

>>> def f1():
...     try:
...         return
...     except:
...        yield 1
>>> print list(f1())
[]

因为在任何函数中,返回只是简单地退出,但

>>> def f2():
...     try:
...         raise StopIteration
...     except:
...         yield 42
>>> print list(f2())
[42]

因为 StopIteration 会被一个简单的 "except" 捕获,就像捕获任何异常一样。

撰写回答