0

Is is possible in Python to have a generator that yields values in a loop be alternatively called as a normal function where the final value of that loop is returned? I tried setting a flag as argument and then choose to yield or return depending on that flag. But the mere existence of the keyword yield in a function transforms it automatically into a generator and Python complains that there was a return statement in a generator.

Here an example of such a function:

def function(generator=True):
    a = 0
    for i in range(10):
        a = i
        if generator:
            yield a
    if not generator:
        return a

Such a function would be useful for me when in some cases I just need the final result (eg. using it as residual function for optimization) while in other cases I need the incremental results after each iteration (for example using a differential model for a robot, updating the robot's pose with each new velocity command). For now I am having two functions where one has the yield and the other has the return. So is it possible to combine those two?

Mehdi
  • 1,370
  • 3
  • 15
  • 26
  • Duplicate? http://stackoverflow.com/questions/26595895/return-and-yield-in-the-same-function – Chris_Rands Aug 16 '16 at 09:21
  • 1
    Create a wrapper which iterates the entire generator and returns the last result?! It seems like madness that the same function could act like both a generator *and* a normal function; split that responsibility. Having said this, it seems even madder to iterate the entire generator for a single value. Can't you calculate the last value directly, *really* splitting the responsibility of those two functions? – deceze Aug 16 '16 at 09:24
  • as far as I know it is not possible as the data from the robot represents motor encoder ticks that are periodic (reset at zero when reaching 65536) and computing the pose update step by step is necessary, it is called "Differential" model after all. A wrapper is still another function so I was wondering if fusing was possible. And madness is not always a bad term :) – Mehdi Aug 16 '16 at 09:30
  • In your code replace `return` with `yield`. Now when you want to use the function as a "simple function" you just use `next(function(generator=False))` while when you want to use it as generator you use it as `for x in function(generator=True)`. – Bakuriu Aug 16 '16 at 10:44

2 Answers2

1

it's still a generator, even calling return. I'd to not mix both generator/regular function anyway.

You can wrap something on top of the iterator, in case you need to loop through the results anyway.

A simple code that could do what you want:

last = None
for last in function(): pass

Now last holds the value you want and you can use it on your code.

The return inside a generator was added to Python 3.3 And it's equivalent to StopIteration(value)

return expr in a generator causes StopIteration(expr) to be raised upon exit from the generator.

In a generator, the statement

return value is semantically equivalent to

raise StopIteration(value) except that, as currently, the exception cannot be caught by except clauses within the returning generator.

dfranca
  • 5,156
  • 2
  • 32
  • 60
  • Even in the suggested question as duplicate, people say return can be called inside a generator but in my case it leads my program to crash. SyntaxError: 'return' with argument inside generator – Mehdi Aug 16 '16 at 09:33
  • It seems that the "return" inside a generator was added on Python 3.3 https://www.python.org/dev/peps/pep-0380/#use-of-stopiteration-to-return-values Which version are you using? – dfranca Aug 16 '16 at 09:37
  • Seems to make sense, return is not allowed to have a value in Python 2.7, which I am using. – Mehdi Aug 16 '16 at 09:38
1

You'll still have to iterate over it to get its single value, but you can use yield again instead of return to accomplish this.

>>> def function(generator=True):
...     a = 0
...     for i in range(10):
...         a = i
...         if generator:
...             yield a
...     if not generator:
...         yield a
...
>>> a = function()
>>> print(*a)
0 1 2 3 4 5 6 7 8 9
>>> a = function(0)
>>> print(a)
<generator object function at 0x0000000001603240>
>>> print(*a)
9
>>> a = function(0)

Note that having return inside a generator is a SyntaxError in Python 2, but not Python 3. Replacing the return with yield produces the same result in 2 and 3.

TigerhawkT3
  • 48,464
  • 6
  • 60
  • 97