在python中混合yield和return语句是一种好习惯吗?

时间:2015-03-02 09:41:38

标签: python

我非常希望有以下行为:

def foo(bar=None):
    if bar:
        return other_function(other_thing[bar])
    else:
        for i in other_thing:
            yield other_function(i)

这个想法是,该函数可以用作构建所有实例的生成器,或者它可以用于返回特定实例。这是在Python中执行此操作的好方法吗?如果没有,是否有更好的方法。

2 个答案:

答案 0 :(得分:10)

只有在Python 3中,在语法上可以在同一个函数中使用return valueyield,在Python 2中它将导致:

SyntaxError: 'return' with argument inside generator

在Python 3中,生成器return value实际上是raise StopIteration(value)的语法糖,yield from子句也支持它:

def f():
    yield from iter('123')
    return 'ok'

def g():
    message = yield from f()
    print('Message:', message)

In [1]: list(g)
Message: ok
Out[1]: ['1', '2', '3']

所以,这个结构不符合你的预期。此外,基于参数值改变函数的返回类型(或者更确切地说,界面)似乎不是一个非常好(“pythonic”)的想法。这可能是一个品味问题,但这就是Guido van Rossum在书中Masterminds of Programming采访时所说的:

  

我有一些个人的烦恼:首先,这是特定于动态语言,不要使方法的返回类型取决于其中一个参数的值;否则,如果您不知道关系,可能很难理解返回的内容 - 也许类型决定参数是从一个变量中传入的,该变量的内容在阅读代码时无法轻易猜到。

答案 1 :(得分:0)

可以返回实例或生成器,例如,如果您有一个可以接受数字或可迭代的函数。对于 number 参数,您可能只想要结果而不是生成器。

def f(n):
    if not hasattr(n, '__iter__'):
        n = (n,)

    for i in n:
        yield(i)

list(f(range(5)))
>>> [0, 1, 2, 3, 4]

list(f(5))
>>> [5]

在这种情况下,列表中的单个数字可能不是您想要的。

正如其他评论者在这篇文章中所描述的那样,包含 yield 的函数将始终返回一个生成器,因此向该函数添加 return 不起作用:

def f(n):
    if not hasattr(n, '__iter__'):
        n = (n,)

    if len(n) == 1:
        return n
    else:
        for i in n:
           yield(i)

f(5)
>>> <generator object f at 0x7f939460e3c0>

一种解决方案是将执行工作的函数包装在另一个函数中:

def _f(n):
    for i in n:
        yield(i)

def f(n):
    if not hasattr(n, '__iter__'):
        n = (n,)
    
    # do 'heavy' lifting in private function
    out = _f(n)
    
    # work out whether to return just the value or the generator
    if len(n) == 1:
        return next(out)
    else:
        return out

f(5)
>>> 5

f(range(5))
>>> <generator object _f at 0x7f939460e900>

list(f(range(5))
>>> [0, 1, 2, 3, 4]

通过这种方式,如果您只需要一次迭代,您可以获得结果,但如果您需要多次迭代,仍然返回一个生成器。