2

Often generator functions are simpler to write than iterators, e.g. consider this example generator function

def generator_fn():
    x = 0
    for y in range(10):
         x += y
         yield x

Is there a straightforward way to transform an arbitrary generator function into an iterator object, i.e.,

class IteratorFromGenerator(object):

    def __init__(self, generator_fn):
        self.generator_fn = generator_fn

    def __iter__(self):
        # ???
        pass

    def __next__(self):
        # ???
        pass

To me this seems favorable but impossible. Favorable, because generator functions are often simpler to write and require less code. Usually, I begin writing generator functions and at some point, I need an iterator object (e.g. when generator_fn is a member of my class Foo and I want to write for x in my_foo instead of for x in my_foo.generator_fn(). Impossible, because generator functions define the execution flow of the loop process, while iterator objects simply offer their __next__ interface to be requested by an undefined "outer" execution flow.

Am I right or is there a possibility to do this?

Of course, apart from the trivial, memory-consuming solution of consuming all generated elements into a list.

Thanks for any help.

flonk
  • 3,726
  • 3
  • 24
  • 37
  • 1
    You can get an iterator from a generator with the [`iter`](https://docs.python.org/3/library/functions.html#iter) function, if an iterator is actually what you want. – khelwood May 02 '21 at 21:05
  • built in function `iter` should do exactly this – Chris Doyle May 02 '21 at 21:07
  • @khelwood thanks, but how exactly, simply returning `iter(generator_fn())` in `__iter__` seems not to work – flonk May 02 '21 at 21:09
  • 3
    This question doesn't make a lot of sense. What are you actually trying to accomplish? Generator objects (which is what is returned from a generator object) **are already iterators**. Using `iter` on them is pointless, it just returns the exact same generator object (since all well-defined iterators should simply return `self` from `__iter__`) – juanpa.arrivillaga May 02 '21 at 21:14
  • If you are trying to make a class *iterable*, then just define a `__iter__` with `return self.generator_method()` – juanpa.arrivillaga May 02 '21 at 21:16
  • Read this answer I wrote to a related question: https://stackoverflow.com/a/45685692/5014455 – juanpa.arrivillaga May 02 '21 at 21:19
  • Yes, thanks and sorry, I was wrong, it just seemed not to work due to a subtle implementation problem of my algorithm, which brought me to this question ;) – flonk May 02 '21 at 21:19
  • Just to be clear, an IteratorFromGenerator class doesn't make sense. "Impossible, because generator functions define the execution flow of the loop process, while iterator objects simply offer their `__next__` interface to be requested by an undefined "outer" execution flow." Is totally wrong. Again, *generator functions return generator objects which **are iterators*** that is, generator objects provide a `__iter__` method (which simply returns itself as all iterators should) and a `__next__` method. Generator functions *are convenient ways of writing iterators* – juanpa.arrivillaga May 02 '21 at 21:27
  • 1
    @juanpa.arrivillaga Yes, that is exactly what I was missing, I didn't know the underlying mechanics and wrongly identified generator functions and generator objects. – flonk May 02 '21 at 21:30

1 Answers1

0

A function with yield is a function that, when called, returns an instance of a generator object (which is a specific type of iterator). So, as already mentioned in the comments, you only have to do:

def generator_fn():
    x = 0
    for y in range(10):
         x += y
         yield x

iterator_from_generator_fn = generator_fn()

However, by doing this, you will probably encounter this problem: Resetting generator object in Python, because by definition, you can only iterate over a generator once.

What you probably want is an iterable : an object which __iter__ method returns an iterator object. Something like this :

class IterableFromGenerator(object):

    def __init__(self, generator_fn):
        self.generator_fn = generator_fn

    def __iter__(self):
        return generator_fn()


iterable_from_generator_fn = IterableFromGenerator(generator_fn)
user18048269
  • 125
  • 8