8

When you instantiate a generator function, it won't execute any code until you call next on it.

It means that if generator function contains some kind of of initialization code, it won't be executed until it's iterated on.

Consider this example:

def generator(filename):
    with open(filename) as f:
        data = f.read()

    while True:
        yield data

gen = generator('/tmp/some_file')
# at this point, no generator code is executed

# initialization code is executed only at the first iteration
for x in gen:
    pass

If the file does not exist, the exception will be raised at the for loop. I'd like the code before the first yield to execute before generator is iterated over, so any exceptions during the initialization will be raised at generator instantiation.

Is there a clean pythonic way to do so?

WGH
  • 3,222
  • 2
  • 29
  • 42
  • @TadhgMcDonald-Jensen: Wow, nobody posted the "separate initialization function" solution for three years. The accepted answer on that question works, but it forces you to deal with all the annoyances of writing your iterator as a state machine that generators were intended to save you from. – user2357112 Apr 19 '16 at 18:29
  • @user2357112 then just don't use the accepted solution, you will find however that WGH is in fact asking for some code to run as soon as the generator is initialized so this is a dup. – Tadhg McDonald-Jensen Apr 19 '16 at 18:33
  • personally I found that [the `primed` function solution](http://stackoverflow.com/a/5725046/5827215) was the one I was looking for when I first found that question, although I modified it a bit to decorate the function instead of called with the iterator. – Tadhg McDonald-Jensen Apr 19 '16 at 18:38

2 Answers2

6

Wrap a regular function around the generator and put the initialization there:

def file_contents_repeated(filename):
    with open(filename) as f:
        data = f.read()
    return _gen(data)

def _gen(data):
    while True:
        yield data

(In this case, you could replace the inner generator with itertools.repeat, but not in general.)

user2357112
  • 260,549
  • 28
  • 431
  • 505
5

The function wrapper solution works fine. If you want to keep all of the code in the same function, you can create a closure.

def generator(filename):
    with open(filename) as f:
        data = f.read()

    def _generator():
        while True:
            yield data

    return _generator()
Jared Goguen
  • 8,772
  • 2
  • 18
  • 36