我希望您考虑以下代码:
def func(alist):
if len(alist) == 1:
return arg * 2
for item in alist:
yield item * 2
当我运行它时,我收到此错误:
SyntaxError: 'return' with argument inside generator
现在,我意识到我不能这样做。不过,我想知道为什么。幕后究竟发生了什么导致 Python 抛出
SyntaxError
?
Python 必须在字节码编译时决定函数是否是生成器。这是因为生成器的语义表明,生成器函数中的任何代码都不会在第一次
next
调用之前运行;生成器函数返回一个生成器迭代器,当调用 next
时,它会运行生成器代码。因此,Python 无法通过运行函数直到遇到 yield
或 return
来决定它是否应该是生成器;相反,函数中存在 yield
表示该函数是生成器。
在 Python 3 上,这不再是 SyntaxError,但你的函数仍然无条件地是一个生成器。带有参数的
return
现在只意味着生成器中的某些内容,而不是语法错误 - 它决定了生成器上的 yield from
将返回什么。迭代 func([1])
不会产生任何元素,因为生成器永远不会 yield
s。
参见这里:Python 中的“yield”关键字有什么作用?
这详细描述了您的问题。 简而言之,因为 Python 解释函数的方式(无论是作为生成对象还是返回对象,Python 中的一切都是对象,并且有些东西会将该对象定义为特定类型。在这种情况下,Yield 是一个生成对象)
TL;DR:您不能在同一函数中使用
yield
和 return
。在函数中使用其中一个任意多次即可,但不能同时使用两个。
不是说,你不能将 return 与yield 一起使用,而是当你的函数有一个yield 使其成为生成器时,你不能将 return 与参数一起使用
您可能想将您的实现更改为
def func(lis):
for item in lis:
yield item * 2
你想要的确实是可能的:
def func(lis):
if len(lis) == 1:
return lis[0] * 2
else:
return (item * 2 for item in lis)
这在一种情况下返回一个值,在另一种情况下返回一个生成器。
但请注意,这会导致问题:在每次通话时,您都必须做出区分
res = func(lis)
if len(lis) == 1:
print res # my value
else:
for i in res: print i # several values
更明智的做法是区分这两个用例:
你可以的
def func(val):
return val * 2
并且,当您需要时,执行以下任一操作
reslist = [func(i) for i in lis]
resgen = (func(i) for i in lis)
或
你可以的
def func(lis):
for item in lis:
yield lis[0] * 2
def func1(val):
for i in func([val]):
return i # returns the first value
其他答案已经解释了收益率/回报问题。您可以使用
map
使这变得更简单:
def func(lis):
return map(lambda item: item * 2, lis)
# similar to: return [item * 2 for item in lis]
当然,这将始终返回一个列表,因为
lis
是一个列表,即使它只有一个项目。您可以将其更改为复合语句:
def func(lis):
return map(lambda item: item * 2, lis) if len(lis) > 1 else lis[0] * 2