I'm trying to decide between the following two definitions of my generator. Which is better? Which is "more pythonic"? And is there anyway to mitigate the drawbacks of each one?
def myGenerator1(howMany):
result = [0,0,0]
yield result
for i in range(howMany)
modifyListInPlace(result)
yield result
for val in myGenerator1(1000):
useValThenForgetIt(val)
def myGenerator2(howMany):
result = (0,0,0)
yield result
for i in range(howMany)
result = createNewUpdatedTuple(result)
yield result
for val in myGenerator2(1000):
useValThenForgetIt(val)
The first one modifies a value that has been returned by the generator, possibly messing with calling code that I haven't foreseen yet. The second produces 1000 tuples worth of garbage in this case, or more if I increase "howMany"(which I might).
The loops I give as an example are just my current use of the generator. I don't think i would ever save the values that come out of it, but it is a bit of a utility that could possibly be useful elsewhere.
Looking to the standard library as a guide, the combinatoric functions in the itertools module all return tuples eventhough the underlying algorithm is a mutate-in-place algorithm. For example, look at the code for itertools.permutations.
This design (returning tuples instead of lists) has proven to be robust. I worry that the mutating list approach will create some hard-to-find bugs depending on what the caller is doing with the iterator's return value.
One other thought. I wouldn't worry too much about "creating thousands of tuples worth of garbage" for the unused results. Python's tuple implementation is very good at reusing previously disposed tuples (by using an array of freelists, it can create a new tuple from a previously used one without making a call to the memory allocator). So, the tuple version make be just a performant as the list version or even a little better.