[Antoine Pitrou <solip...@pitrou.net>] > This implementation doesn't work with Python 3.7 or 3.8. > I've tried it here: > https://gist.github.com/pitrou/b3991f638300edb6d06b5be23a4c66d6 > > and get: > Traceback (most recent call last): > File "mytee.py", line 14, in gen > mylast = last[1] = last = [next(it), None] > StopIteration > > The above exception was the direct cause of the following exception: > > Traceback (most recent call last): > File "mytee.py", line 47, in <module> > run(mytee1) > File "mytee.py", line 36, in run > lists[i].append(next(iters[i])) > RuntimeError: generator raised StopIteration > > (Yuck!)
Thanks for trying! I wonder whether that will break other code. I wrote PEP 255, and this part was intentional at the time: """ If an unhandled exception-- including, but not limited to, StopIteration --is raised by, OR PASSES THROUGH [emphasis added], a generator function, then the exception is passed on to the caller in the usual way, and subsequent attempts to resume the generator function raise StopIteration. """ I've exploited that a number of times. > In short, you want the following instead: > > try: > mylast = last[1] = last = [next(it), None] > except StopIteration: > return No, I don't ;-) If I have to catch StopIteration myself now, then I want the entire "white True:" loop in the "try" block. Setting up try/except machinery anew on each iteration would add significant overhead; doing it just once per derived generator wouldn't. >> def mytee(xs, n): >> last = [None, None] >> >> def gen(it, mylast): >> nonlocal last >> while True: >> mylast = mylast[1] >> if not mylast: >> mylast = last[1] = last = [next(it), None] > That's smart and obscure :-o > The way it works is that the `last` assignment changes the `last` value > seen by all derived generators, while the `last[1]` assignment updates > the bindings made in the other generators' `mylast` lists... It's > difficult to find the words to explain it. Which is why I didn't even try - I did warn people that if they thought it "was obvious", they hadn't yet thought hard enough ;-) Good job! > The chained assignment makes it more difficult to parse as well (when I > read this I don't know if `last[i]` or `last` gets assigned first; > apparently the answer is `last[i]`, otherwise the recipe wouldn't work > correctly). Ya, I had to look it up too :-) Although, like almost everything else in Python, chained assignments proceed "left to right". I was just trying to make it as short as possible, to increase the "huh - can something that tiny really work?!" healthy skepticism factor :-) > Perhaps like this: > > while True: > mylast = mylast[1] > if not mylast: > try: > # Create new list link > mylast = [next(it), None] > except StopIteration: > return > else: > # Append to other generators `mylast` linked lists > last[1] = mylast > # Update shared list link > last = last[1] > yield mylast[0] I certainly agree that's easier to follow. But that wasn't really the point ;-) _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/