I would like you to consider the following code:
def func(alist):
if len(alist) == 1:
return arg * 2
for item in alist:
yield item * 2
When I run it, I get this error:
SyntaxError: 'return' with argument inside generator
Now, I realize that I cannot do this. However, I would like to know why. What exactly is going on behind the scenes that is causing Python to throw the SyntaxError
?
Python has to decide whether a function is a generator at bytecode compilation time. This is because the semantics of generators say that none of the code in a generator function runs before the first next
call; the generator function returns a generator iterator that, when next
is called, runs the generator code. Thus, Python can't decide whether a function should be a generator or not by running it until it hits a yield
or a return
; instead, the presence of a yield
in a function signals that the function is a generator.
See here: What does the "yield" keyword do in Python?
This describes your issue in detail. In short, because the way Python will interprate the function (either as an generating object or a returning object, everything in Python is objects and some things will define that object as a specific type. In this case Yield is a generating object)
TL;DR: You can't use yield
and return
within the same function. Use either one as many times as you wan't within the function, just not both.
Is not that, you cannot use a return with yield, but rather, you cannot use return with argument, when your function has a yield making it a generator
You might want to change your implementation to
def func(lis):
for item in lis:
yield item * 2
What you want is indeed possible:
def func(lis):
if len(lis) == 1:
return lis[0] * 2
else:
return (item * 2 for item in lis)
This returns a value in the one case and a generator in the other case.
But be aware that this will lead to problems: On every call, you will have to make the distinction
res = func(lis)
if len(lis) == 1:
print res # my value
else:
for i in res: print i # several values
It is much more advisable to make the two use cases distinct:
You can do
def func(val): return val * 2
and, when you need, do either of
reslist = [func(i) for i in lis] resgen = (func(i) for i in lis)
or
you can do
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
The other answers already explained the yield/return issue. You can just use map
to make this much simpler:
def func(lis):
return map(lambda item: item * 2, lis)
# similar to: return [item * 2 for item in lis]
Of course, this will always return a list, because lis
is a list, even if it only has one item. You can change it to a compound statement:
def func(lis):
return map(lambda item: item * 2, lis) if len(lis) > 1 else lis[0] * 2
来源:https://stackoverflow.com/questions/18215828/why-cant-i-use-yield-with-return