[Python-ideas] Rewriting the "roundrobin" recipe in the itertools documentation

Neil Girdhar mistersheik at gmail.com
Thu Nov 16 15:16:29 EST 2017

I like yours better.  Plenty of recipes sit on top of other recipes, e.g., 
pairwise sits on top of tee.

On Thursday, November 16, 2017 at 8:57:29 AM UTC-5, bunslow wrote:
> For taking values alternately from a series of iterables, there's two 
> primary functions:
> builtin.zip
> itertools.zip_longest
> zip of course stops when the shortest iterable ends. zip_longest is 
> generally a useful substitute for when you don't want the zip behavior, but 
> it fills extra values in the blanks rather than just ignoring a finished 
> iterator and moving on with the rest.
> This latter most use case is at least somewhat common, according to 
> this[1] StackOverflow question (and other duplicates), in addition to the 
> existence of the `roundrobin` recipe[2] in the itertools docs. The recipe 
> satisfies this use case, and its code is repeated in the StackOverflow 
> answer.
> However, it is remarkably unpythonic, in my opinion, which is one thing 
> when such is necessary to achieve a goal, but for this functionality, such 
> is most definitely *not* necessary.  I'll paste the code here for quick 
> reference:
> def roundrobin(*iterables):
>     "roundrobin('ABC', 'D', 'EF') --> A D E B F C"
>     pending = len(iterables)
>     nexts = cycle(iter(it).__next__ for it in iterables)
>     while pending:
>         try:
>             for next in nexts:
>                 yield next()
>         except StopIteration:
>             pending -= 1
>             nexts = cycle(islice(nexts, pending))
> Things that strike me as unpythonic: 1) requiring the total number of 
> input iterables 2) making gratuitous use of `next`, 3) using a while loop 
> in code dealing with iterables, 4) combining loops, exceptions, and 
> composed itertools functions in non-obvious ways that make control flow 
> difficult to determine
> Now, I get it, looking at the "roughly equivalent to" code for zip_longest 
> in the docs, there doesn't seem to be much way around it for generally 
> similar goals, and as I said above, unpythonic is fine when necessary 
> (practicality beats purity), but in this case, for being a "recipe" in the 
> itertools docs, it should *make use* of the zip_longest which already does 
> all the unpythonic stuff for you (though honestly I'm not convinced either 
> that the zip_longest code in the docs is the most possible pythonic-ness). 
> Instead, the following recipe (which I also submitted to the StackOverflow 
> question, and which is generally similar to several other later answers, 
> all remarking that they believe it's more pythonic) is much cleaner and 
> more suited to demonstrating the power of itertools to new developers than 
> the mess of a "recipe" pasted above.
> def roundrobin(*iters):
>     "roundrobin('ABC', 'D', 'EF') --> A D E B F C"
>     # Perhaps "flat_zip_nofill" is a better name, or something similar
>     sentinel = object()
>     for tup in it.zip_longest(*iters, fillvalue=sentinel):
>         yield from (x for x in tup if x is not sentinel)
> In particular, this is just an extremely thin wrapper around zip_longest, 
> whose primary purpose is to eliminate the otherwise-mandatory "fillvalues" 
> that zip_longest requires to produce uniform-length tuples. It's also an 
> excellent example of how to make best pythonic use of iterables in general, 
> and itertools in particular, and as such a much better implementation to be 
> demonstrated in documentation.
> I would thus advocate that the former recipe is replaced with the latter 
> recipe, being much more pythonic, understandable, and useful for helping 
> new developers acquire the style of python. (Using the common linguistics 
> analogy: a dictionary and grammar for a spoken language may be enough to 
> communicate, but we rely on a large body of literature -- fiction, 
> research, poetry, etc -- as children to get that special flavor and most 
> expressive taste to the language. The stdlib is no Shakespeare, but it and 
> its docs still form an important part of the formative literature of the 
> Python language.)
> I realize at the end of the day this is a pretty trivial and ultimately 
> meaningless nit to pick, but I've never contributed before and have a 
> variety of similar minor pain points in the docs/stdlib, and I'm trying to 
> gauge 1) how well this sort of minor QoL improvement is wanted, and 2) even 
> if it is wanted, am I going about it the right way. If the answers to both 
> of these questions are positive regarding this particular case, then I'll 
> look into making a BPO issue and pull request on GitHub, which IIUC is the 
> standard path for contributions.
> Thank you for your consideration.
> ~~~~
> [1]: 
> https://stackoverflow.com/questions/3678869/pythonic-way-to-combine-two-lists-in-an-alternating-fashion/
> [2]: https://docs.python.org/3/library/itertools.html#itertools-recipes
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-ideas/attachments/20171116/40b6e498/attachment.html>

More information about the Python-ideas mailing list