On Apr 11, 2020, at 08:22, Eric Fahlgren <ericfahlgren@gmail.com> wrote:

On Sat, Apr 11, 2020 at 4:21 AM Rhodri James <rhodri@kynesim.co.uk> wrote:
Since the introduction of enumerate() lo! these many moons ago, I find I
almost never write range(len(x)) as a loop iterable.

Out of curiosity, I just grepped about 300k lines of source:

234 - enumerate() in for loops
140 - zip() in for loops
12 - range(len()) in for loops

About half of those 12 range uses are interfacing to extension modules that only provide an indexing interface to array-like data, so really no way around it... 

Actually, there is usually a way around it: just use enumerate/zip/etc. anyway. Any type for which sq_item/__getitem__ raises IndexError on the len-th index automatically is iterable, unless it goes out of its way to not be by implementing a tp_iter/__iter__ that raises, or does something pathological. And there are a few extension modules that explicitly implement a raising tp_iter, usually by mistake, but most don’t.

In fact, this works even more generally than looping over range(len(x)). You don’t even have to provide tp_len/__len__ (e.g., because you’re presenting a dynamic Spam* array terminated by a sentinel value), and iteration still works.