收益和收益在同一个函数中


77

像这样在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

还是发电机吗?

Answers:


75

是的,它仍然是发电机。该return是(几乎),相当于提高StopIteration

PEP 255清楚地说明了这一点:

规格:返回

生成器函数还可以包含以下形式的return语句:

"return"

请注意,在生成器主体中的return语句上不允许使用expression_list(当然,它们可能会出现在嵌套在生成器中的非生成器函数的主体中)。

当遇到return语句时,控制将像在任何函数return中一样继续进行,并执行适当的finally子句(如果存在)。然后引发StopIteration异常,表示迭代器已耗尽。如果控制从生成器的末端流出而没有显式返回,则还会引发StopIteration异常。

请注意,对于生成器函数和非生成器函数,return的意思是“我完成了,没有有趣的返回值”。

请注意,return并不总是等同于提高StopIteration:不同之处在于如何处理try / except构造。例如,

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

因为像在任何函数中一样,return只是退出,但是

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

因为StopIteration和任何异常一样都被一个裸露的“ except”捕获。


4
您知道如果吵架会怎样return
zwol14年

15
@Zack在Python 2.x中,这将会是一个语法错误:SyntaxError: 'return' with argument inside generator。它在Python 3.x中是允许的,但主要是要与协程一起使用-您使用yield coroutine()(或yield from coroutine(),取决于您使用的异步框架)对其他协程进行异步调用,然后返回要从协程返回的任何内容使用return value。在Python 2.x中,您需要使用一种技巧,例如raise Return(value)从协程返回值。
dano 2014年

31

是的,它仍然是发电机。空returnreturn None可用于结束生成器功能。这等效于提高a StopIteration(有关详细信息,请参见@NPE的答案)。

请注意,SyntaxError在3.3之前的Python版本中,带有non-None参数的返回是。

正如@BrenBarn在从Python 3.3开始的注释中指出的那样,现在将返回值传递给 StopIteration.

PEP 380

在生成器中,该语句

return value

在语义上等同于

raise StopIteration(value)

1
您知道如果return有参数(不是None)会发生什么?
zwol14年

6
在Python 3.3及更高版本中,可以return与参数一起使用,以将参数传递给引发的StopIteration。看到这个问题
BrenBarn 2014年

@BrenBarn有趣,不知道。
Ashwini Chaudhary 2014年

1
@AshwiniChaudhary新asyncio模块中的协程实现基于该功能(以及yield from关键字)构建。
dano 2014年

3
@AshwiniChaudhary启用了Python中的基本协程-将值/异常发送到生成器,并通过value = yield等接收它们yield from的能力。PEP 380引入了return生成器的值并从生成器中获取值的能力是PEP 380所提供的,两者都利用了PEP 380asyncio。仅使用PEP 343提供的功能,您仍然可以使用健壮的协程实现,编写它们的过程要简单一些。
dano 2014年

11

有一种方法可以在函数中具有yield and 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

不清楚“它确实做了您期望的事情”。您能否举例说明如何善用您的方法?
ShpielMeister

问题“在同一个函数中的收益和收益”中的“它确实做了您所期望的”。就我个人而言,我已切换到Haskell,并且可以很好地与代数数据类型一起使用/管理它,但是对于Python,它很难管理您的类型,因此它不适用于Python类型声明。因此,如果您要问如何充分利用此问题,请不要使用它。否则,这使您可以返回no,单个或多个值。这可以用于有效地遍历树。
威廉·鲁斯纳克

0

注意: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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.