On Wed, 7 Jun 2017 05:09 pm, Jussi Piitulainen wrote: > Frank Millman writes: > >> It would be nice to write a generator in such a way that, in addition >> to 'yielding' each value, it performs some additional work and then >> 'returns' a final result at the end. >> >>> From Python 3.3, anything 'returned' becomes the value of the >>> StopIteration >> exception, so it is possible, but not pretty. >> >> Instead of - >> my_gen = generator() >> for item in my_gen(): >> do_something(item) >> [how to get the final result?]
Currently, I don't believe there is a way. >> you can write - >> my_gen = generator() >> while True: >> try: >> item = next(my_gen()) >> do_something(item) >> except StopIteration as e: >> final_result = e.value >> >> Is this the best way to achieve it, or is there a nicer alternative? I don't think there are currently any nice alternatives. > Like this, and imagination is the limit: > > def generator(box): > yield 1 > box.append('won') > yield 2 > box.append('too') > yield 3 > box.append('tree') > > mabox = [] > for item in generator(mabox): pass > print(*mabox) > # prints: won too tree Well, that's a little bit better than using a global variable, but it's still pretty ugly, and it doesn't capture the return value so you've answered a completely different question. Here is why I think it is an ugly solution. There are three obvious alternatives to this API: Alternative one: force the caller to provide the "box" argument, whether they care about these extra values or not. for item in generator([]): # argument is ignored, and garbage collected process(item) That's not *too* bad, but still a bit manky. Alternative two: provide a default value for box: def generator(box=[]): ... Pros: now the caller doesn't need to care about box if they don't care about it. Cons: it will leak memory. Every call to generator() with no box argument will collect values in the default list. Alternative three: provide a non-mutable default for box: def generator(box=None): ... Procs: the caller doesn't need to care about box. Cons: writing generator is a lot more complex, you have to check for box is None before appending. There may be clever ways around this, but either way, the complexity of the generator is significantly increased. I'm not saying I'd *never* use this solution. I've used a similar solution myself, treating the argument as a "pass by reference" output parameter, similar to "var" arguments in Pascal. But not often, because it feels ugly. Otherwise, I guess using a while loop is the least-worst existing solution. But here's a neat solution for a feature request: # this doesn't actually work, yet it = generator() for value in it: # run the generator through to completion, as normal process(value) extra_value = it.result where the property lookup it.result: - captures the return value, if the generator has returned; - raise an exception if the generator is still running. -- Steve “Cheer up,” they said, “things could be worse.” So I cheered up, and sure enough, things got worse. -- https://mail.python.org/mailman/listinfo/python-list