I was experimenting with creating a function that returns an object or works as a generator.
This is a bad idea because, as a best practice, you want functions to reliably return the same types of values, but in the interest of science...
I'm using Python 2, and so range
returns a list, and xrange
is an iterable (that interestingly also provides a __len__
).
def xr(start, stop=None, step=1, gen=True):
if stop is None:
start, stop = 0, start
if gen == True:
for i in xrange(start, stop, step):
yield i
else:
return range(start, stop, step)
I get this error:
File "<stdin>", line 8
SyntaxError: 'return' with argument inside generator
Why (beyond the obvious "you can't have both yield and return in a function," assuming that's right) does it do this? Looking at my code, it is not readily apparent why it would be bad to do this.
How would I attempt to get around this? I know I can return xrange instead of yielding each item from xrange, and so I could return a generator created in another function, but is there a better way?
Any object, even a list, can be returned by a Python function. Create the list object within the function body, assign it to a variable, and then use the keyword "return" to return the list to the function's caller. In this article we will discuss various ways to return a list from a Python function.
It uses yield instead of return keyword. So, this will return the value against the yield keyword each time it is called. However, you need to create an iterator for this function, as shown below. The generator function cannot include the return keyword.
You can assign this generator to a variable in order to use it. When you call special methods on the generator, such as next() , the code within the function is executed up to yield . When the Python yield statement is hit, the program suspends function execution and returns the yielded value to the caller.
So what's the difference between Generator Expressions and List Comprehensions? The generator yields one item at a time and generates item only when in demand. Whereas, in a list comprehension, Python reserves memory for the whole list. Thus we can say that the generator expressions are memory efficient than the lists.
How about using generator expression?
>>> def xr(start, stop=None, step=1, gen=True):
... if stop is None:
... start, stop = 0, start
... if gen == True:
... return (i for i in xrange(start, stop, step)) # <----
... else:
... return range(start, stop, step)
...
>>> xr(2, gen=False)
[0, 1]
>>> xr(2, gen=True)
<generator object <genexpr> at 0x0000000002C1C828>
>>> list(xr(2, gen=True))
[0, 1]
BTW, I would rather define a generator function only. Then use list(xr(..))
if I need a list.
UPDATE Alternatively you can use iter(xrange(start, stop, step))
instead of the generator expression as @DSM commented. See Built-in functions -- iter
.
falsetru has given you a way to have a function that returns a generator or a list. I'm answering your other question about why you can't have a return and a yield in the same function.
When you call a generator function, it doesn't actually do anything immediately (see this question). It doesn't execute the function body, but waits until you start iterating over it (or call next
on it). Therefore, Python has to know if the function is a generator function or not at the beginning, when you call it, to know whether to run the function body or not.
It doesn't make sense to then have it return some value, because if it's a generator function what it returns is the generator (i.e., the thing you iterate over). If you could have a return inside a generator function, it wouldn't be reached when you called the function, so the function would have to "spontaneously" return a value at some later point (when the return
statement was reached as the generator was consumed), which would either be the same as yielding a value at that time, or would be something bizarre and confusing.
I agree with falsetru that it's probably not a great idea to have a function that sometimes returns a generator and sometimes a list. Just call list
on the generator if you want a list.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With