1

I would like create a function, whose primary purpose is to do some task for each input in a big list, and append the output to a file after each iteration.

I also want the function to yield its output just in case I need to store the output in some variable.

So my solution is to make the function a generator as follows:

def print_squares(n):
    """Print the squares of all integers from 1 to n."""
    num = 1
    while num < n:
        result = num**2
        with open('results.txt', 'a') as f:
            f.write(result)
        yield result
        num += 1

But I need to find a way to exhaust the generator, so that it can write all the outputs. What would be the best practice to do so?

Ideas that I've considered:

  1. Unpack the generator into a list, and discard the list:
    • N = 1000000000
      [*print_squares(N)]
      
    • This works, but it looks a bit ugly/"hacky" to me, because my original intent is not to create a list.
  2. Use a for loop which iterates through the generator:
    • for result in print_squares(N):
          pass
      
    • This works, but it uses two lines.

I wish python could pick up that there is no "sink" for the function output, and so exhaust it automatically, rather than just outputting a generator object. I.e: print_squares(N) should exhaust the generator.

Alternatively, is there a function that can exhaust the generator in a readable way (maybe in itertools)? I.e. exhaust(print_squares(N))

abrac
  • 521
  • 5
  • 12
  • Why do you want to 'exhaust' the generator? – JeffUK Dec 10 '20 at 20:04
  • Does this answer your question? [Simpler way to run a generator function without caring about items](https://stackoverflow.com/questions/47456631/simpler-way-to-run-a-generator-function-without-caring-about-items) – thshea Dec 10 '20 at 20:04
  • the itertools module have a recipe for that, look the [consume recipe](https://docs.python.org/3/library/itertools.html#itertools-recipes) – Copperfield Dec 10 '20 at 20:06
  • 2
    @JeffUK because they are using the generator for side-effects – juanpa.arrivillaga Dec 10 '20 at 20:09
  • Couldn't you just have an e.g. `itersquares` generator function that you use when you want an iterable, and a `printsquares` function when you want a function? It sounds like the problem here is that you have one function trying to do two things at the same time. – JeffUK Dec 10 '20 at 21:30
  • Thank you all for your comments! After reading the links and the duplicate answers, I've decided to use a `for _ in print_squares(N): pass`. Although the consume recipe is good, it seems that it relies on a deque optimisation specific to CPython (as explained by [abarnert](https://stackoverflow.com/a/50938287/10462623)). However, I would like my scripts to be portable to PyPy in the future. Sorry that I couldn't find these duplicate answers beforehand. – abrac Dec 12 '20 at 04:51
  • @JeffUK: Unfortunately I don't want to have two functions, as I would have to duplicate my code. My actual generator is more complex than the one in my question. It was just an MWE. – abrac Dec 12 '20 at 04:55
  • You don't have to duplicate your business logic, you have one function which holds the logic, and two functions that access that logic in different ways. See my answer here https://stackoverflow.com/questions/47456631/simpler-way-to-run-a-generator-function-without-caring-about-items – JeffUK Dec 12 '20 at 10:57
  • 1
    Thanks @JeffUK! I understand now. I've ended up implementing it in the way that you've explained. :) – abrac Dec 12 '20 at 19:51

0 Answers0