7

I read from range(len(list)) or enumerate(list)? that using range(len(s)) is not very good way to write Python. How one can write for loops in alternative way if we do not need to loop len(s) times but for example len(s)//3 times or len(s)-5 times? Is it possible to convert those loops to use enumerate?

For example, I had a project where I had a list of 3n elements 's[0], s[1],...,s[3n-1]' and I needed to print them in a nx3 table. I wrote the code something like

for i in range(len(s)//3):
    row = str(s[3*i]) + " " + str(s[3*i+1]) + " " + str(s[3*i+2])
    print(row)
Jaakko Seppälä
  • 744
  • 2
  • 7
  • 21
  • 3
    Can you give a more concrete example of exactly what you want to do? – Chris_Rands Feb 27 '18 at 10:41
  • 1
    Possible duplicate of [Only index needed: enumerate or (x)range?](https://stackoverflow.com/questions/11901081/only-index-needed-enumerate-or-xrange) – Abdulrahman Bres Feb 27 '18 at 10:44
  • Not quite a duplicate, as this question also asks about iterating over _part_ of a list. – Adam Barnes Feb 27 '18 at 10:44
  • 1
    "using `range(len(s))` is not very good way to write Python" not sure where you got that idea, from that question or otherwise. It is perfectly ok Python. What is not advised is to iterate over indices and then get the element within the loop; then `enumerate` is preferred. – jdehesa Feb 27 '18 at 10:46
  • @jdehesa I read it from https://python-forum.io/Thread-Basic-Never-use-for-i-in-range-len-sequence – Jaakko Seppälä Feb 27 '18 at 10:52
  • 2
    @user2219896 Yes, so the point is that you _generally_ don't want to use `range(len(l))` when you want to iterate **the elements** of a list (although there may still be cases where it makes sense). But if you only want to iterate through a sequence of numbers, be it up to `len(l)` or any other number, it is the right way to do it (in fact, it is the very purpose of `range`/`xrange`). Imo using `enumerate` in that case and ignoring the list element is unnecessary and, most importantly, slightly less explicit about the fact that you are _not_ using the list element. – jdehesa Feb 27 '18 at 10:58

4 Answers4

14

If you're iterating over an entire list:

for x in lst:
    print(x)

If you're iterating over an entire list, but you only need the index:

for i, _ in enumerate(lst):
    print(i)

If you're iterating over an entire list, but you don't need the index or the data:

for _ in lst:
    print("hello")

If you're iterating over part of a list:

for x in lst[:-5]:
    print(x)

And so on.

I'm not sure why you want to iterate over part of a list though, that seems strange. I'd be interested to hear your use case, as it could probably be improved.

Looking over the code you've now posted, @Metareven has a good solution - iterating over the list in chunks of the size you want to process.

Adam Barnes
  • 2,922
  • 21
  • 27
2

Your code doesn't look that bad, but if you want to iterate over 3 elements at a time I would make a for loop that increments the i variable by 3 instead of one, like so:

for i in range(0,len(s),3):
  row = str(s[i]) + " " + str(s[i+1]) + " " + str(s[i+2])
  print(row)
Metareven
  • 822
  • 2
  • 7
  • 26
0

It seams you want to go through your collection with some sort of sliding window. In that case, I would suggest using itertools.islice.

>>> from itertools import islice
>>> 
>>> s = [i for i in range(10)] # Or whatever iterable you have
>>> 
>>> iter1 = islice(s, 1, None)
>>> iter2 = islice(s, 2, None)
>>> 
>>> for a, b, c in zip(s, iter1, iter2):
...     print('[{}, {}, {}]'.format(a, b, c))
... 
[0, 1, 2]
[1, 2, 3]
[2, 3, 4]
[3, 4, 5]
[4, 5, 6]
[5, 6, 7]
[6, 7, 8]
[7, 8, 9]

If you don't mind making copies of your data, you could use the traditional slicing notation:

>>> s = [i for i in range(10)] # Again, this could be any iterable
>>> 
>>> for a, b, c in zip(s, s[1:], s[2:]):
...     print('[{}, {}, {}]'.format(a, b, c))
... 
[0, 1, 2]
[1, 2, 3]
[2, 3, 4]
[3, 4, 5]
[4, 5, 6]
[5, 6, 7]
[6, 7, 8]
[7, 8, 9]
bla
  • 1,840
  • 1
  • 13
  • 17
0

Solution using itertools,

import itertools

def grouper(iterable, n, fillvalue=None):
    "Collect data into fixed-length chunks or blocks"
    args = [iter(iterable)] * n
    return itertools.zip_longest(*args, fillvalue=fillvalue)

s = list(range(10))

for row in list(grouper(s, 3)):
    print(row)

gives

(0, 1, 2)
(3, 4, 5)
(6, 7, 8)
(9, None, None)

[Program finished]

Other ideas


# if you're printing it straight away, you might as well
print(*s[3*i: 3*i+3], sep=' ')

for i in range(0, len(s), 3):
    print(' '.join(s[i:i + 3])

# so you can kind of cheat with numpy for this:

for row in numpy.array(s).reshape((3,-1)):
     print(row)
Subham
  • 397
  • 1
  • 6
  • 14