我非常希望有以下行为:
def foo(bar=None):
if bar:
return other_function(other_thing[bar])
else:
for i in other_thing:
yield other_function(i)
这个想法是,该函数可以用作构建所有实例的生成器,或者它可以用于返回特定实例。这是在Python中执行此操作的好方法吗?如果没有,是否有更好的方法。
答案 0 :(得分:10)
只有在Python 3中,在语法上可以在同一个函数中使用return value
和yield
,在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]
通过这种方式,如果您只需要一次迭代,您可以获得结果,但如果您需要多次迭代,仍然返回一个生成器。