Is there a need for range(len(a))?
Solution 1
If you need to work with indices of a sequence, then yes - you use it... eg for the equivalent of numpy.argsort...:
>>> a = [6, 3, 1, 2, 5, 4]
>>> sorted(range(len(a)), key=a.__getitem__)
[2, 3, 1, 5, 4, 0]
Solution 2
Short answer: mathematically speaking, no, in practical terms, yes, for example for Intentional Programming.
Technically, the answer would be "no, it's not needed" because it's expressible using other constructs. But in practice, I use for i in range(len(a)
(or for _ in range(len(a))
if I don't need the index) to make it explicit that I want to iterate as many times as there are items in a sequence without needing to use the items in the sequence for anything.
So: "Is there a need?"? — yes, I need it to express the meaning/intent of the code for readability purposes.
See also: https://en.wikipedia.org/wiki/Intentional_programming
And obviously, if there is no collection that is associated with the iteration at all, for ... in range(len(N))
is the only option, so as to not resort to i = 0; while i < N; i += 1 ...
Solution 3
What if you need to access two elements of the list simultaneously?
for i in range(len(a[0:-1])):
something_new[i] = a[i] * a[i+1]
You can use this, but it's probably less clear:
for i, _ in enumerate(a[0:-1]):
something_new[i] = a[i] * a[i+1]
Personally I'm not 100% happy with either!
Solution 4
It's nice to have when you need to use the index for some kind of manipulation and having the current element doesn't suffice. Take for instance a binary tree that's stored in an array. If you have a method that asks you to return a list of tuples that contains each nodes direct children then you need the index.
#0 -> 1,2 : 1 -> 3,4 : 2 -> 5,6 : 3 -> 7,8 ...
nodes = [0,1,2,3,4,5,6,7,8,9,10]
children = []
for i in range(len(nodes)):
leftNode = None
rightNode = None
if i*2 + 1 < len(nodes):
leftNode = nodes[i*2 + 1]
if i*2 + 2 < len(nodes):
rightNode = nodes[i*2 + 2]
children.append((leftNode,rightNode))
return children
Of course if the element you're working on is an object, you can just call a get children method. But yea, you only really need the index if you're doing some sort of manipulation.
Solution 5
Going by the comments as well as personal experience, I say no, there is no need for range(len(a))
. Everything you can do with range(len(a))
can be done in another (usually far more efficient) way.
You gave many examples in your post, so I won't repeat them here. Instead, I will give an example for those who say "What if I want just the length of a
, not the items?". This is one of the only times you might consider using range(len(a))
. However, even this can be done like so:
>>> a = [1, 2, 3, 4]
>>> for _ in a:
... print True
...
True
True
True
True
>>>
Clements answer (as shown by Allik) can also be reworked to remove range(len(a))
:
>>> a = [6, 3, 1, 2, 5, 4]
>>> sorted(range(len(a)), key=a.__getitem__)
[2, 3, 1, 5, 4, 0]
>>> # Note however that, in this case, range(len(a)) is more efficient.
>>> [x for x, _ in sorted(enumerate(a), key=lambda i: i[1])]
[2, 3, 1, 5, 4, 0]
>>>
So, in conclusion, range(len(a))
is not needed. Its only upside is readability (its intention is clear). But that is just preference and code style.
Related videos on Youtube
Hyperboreus
Obsessive compulsive coder. PEP8: it is not a guide, it is a law graven in stone handed down from the heavens by Allah/Yahwe/God/Junjunahpu/Moctezuma/your-favourite-imaginary-buddy. Everytime you write in python for i in range(len(someList)) you tear down the veil between our world and the nether realms and HE who waits behind the wall feasts and gores on the tears of ravaged minds.
Updated on August 24, 2022Comments
-
Hyperboreus over 1 year
One frequently finds expressions of this type in python questions on SO. Either for just accessing all items of the iterable
for i in range(len(a)): print(a[i])
Which is just a clumbersome way of writing:
for e in a: print(e)
Or for assigning to elements of the iterable:
for i in range(len(a)): a[i] = a[i] * 2
Which should be the same as:
for i, e in enumerate(a): a[i] = e * 2 # Or if it isn't too expensive to create a new iterable a = [e * 2 for e in a]
Or for filtering over the indices:
for i in range(len(a)): if i % 2 == 1: continue print(a[i])
Which could be expressed like this:
for e in a [::2]: print(e)
Or when you just need the length of the list, and not its content:
for _ in range(len(a)): doSomethingUnrelatedToA()
Which could be:
for _ in a: doSomethingUnrelatedToA()
In python we have
enumerate
, slicing,filter
,sorted
, etc... As pythonfor
constructs are intended to iterate over iterables and not only ranges of integers, are there real-world use-cases where you needin range(len(a))
?-
rlms over 10 yearsI think
range(len(a))
is usually people who are fairly inexperienced with Python (although not necessarily with programming in general). -
Admin over 10 yearsI only used
range(len(a))
when I was learning Python. Nowadays, I don't because, as you stated, it's quite easy to replace. -
aIKid over 10 yearsnot really. I use
range(len(a))
often, because i don't need the content of list a, but only the length. -
Admin over 10 years@aIKamili - That is one use of
range(len(a))
. However, even that can be replaced by:for _ in a:
. The code I gave will only iterate for as many times as there are items in lista
. -
rlms over 10 years@alKamili I'd personally use
enumerate
orfor _in list_
instead, but I guess in that case it is mostly just personal preference. -
Zhang18 over 7 yearsWhat if in the loop I need to access the element before and after the current one? I usually have
for i in range(len(a)): doSomethingAbout(a[i+1] - a[i])
How to get around that? -
Jaakko Seppälä almost 6 years@Zhang18 I think your example gives an IndexError. If you have for example
a=range(10)
then your loops goes fromdoSomethingAbout(a[1] - a[0])
todoSomethingAbout(a[10] - a[9])
but there is no elementa[10]
. You can slice the list and then iterate over the sliced list:a = range(10) for i in a[:-1]: print(a[i+1]-a[i])
-
Zhang18 almost 6 years@JaakkoSeppälä agreed. I was just giving an example to illustrate the main issue of having to loop through indices, not just values, understanding there is a corner case at the end which is besides the main point.
-
ytu over 4 yearsShould be an use case here: stackoverflow.com/questions/59112685/…
-
Roly almost 3 yearsThis is a great question.
-
jerclarke almost 3 yearsMy suspicion: Old-fashioned tutorials are stupid and teach the
range(len(list))
solution when they should be using enumerate as the starting point! Just suffered through 30 mins of trying to explain therange(len(list))
chorizo to someone I'm tutoring, just because that's what Codecademy decided to burden us with. Why??? Obscure excuses aside, the default should beenumerate()
butrange(len(list))
persists. This standard makes PHP'sforeach()
look good, for goodness sake (^_^) -
jerclarke almost 3 yearsFWIW
enumerate()
was only added to Python in 2003 (python.org/dev/peps/pep-0279), 3 years after Python 2 came out and 12 years after the original release in 1991. I suspect thatrange(len(list))
was the standard for so long that it's just hard to get rid of.
-
-
Hyperboreus over 10 yearsWhat advantages has
for _ in range(len(a))
overfor _ in a
? -
Erik Kaplun over 10 years@Hyperboreus: yeah, I just amended my answer a few seconds before your comment... so I guess the difference is whether you want to be really explicit about "repeat AS MANY TIMES as there are items in
a
" as opposed to "for every element ina
, regardless of the content ofa
"... so just an Intentional Programming nuance. -
Hyperboreus over 10 yearsThank you for your example. I've included it in my question.
-
Hyperboreus over 10 yearsOK, this looks sensible. Thank you very much. But the question is: what will you do with your newly sorted list of indices. If via this list again you access some iterable, the dog bites its own tail.
-
Erik Kaplun over 10 yearsEquivalent to:
[ix for ix, _ in sorted(enumerate(a), key=lambda i: i[1])]
though, although yours is arguably nicer/geekier. -
Hyperboreus over 10 yearsThank you very much. And then again, readability is (partially) in the eye of the beholder. I interpret
for _ in a:
as "Iterate over a but ignore its content", but I interpretfor _ in range(len(a))
as "Get the length of a, then create a number of integers of the same length, and then finally ignore the content". -
Admin over 10 years@Hyperboreus - Very true. It's just code style. My goal was to show there will never be a "I must use
range(len(a))
or I can't do this" scenario. -
Hyperboreus over 10 yearsOn a side note: E.g. in erlang the single underscore is the anonymous variable. It is the only variable which can be re-assigned (or "matched"), unlike other variables, as erlang doesn't allow destructive assignment (which generally speaking is an abomination and weakens the veil between us and the nether realms, where HE waits behind the wall in his palace built of tortured glass).
-
Jim about 9 yearsMy ignorance. There's zip, a much more pythonic way of iterating over 2 lists in parallel.
-
saulspatz over 8 years
if id < len(self.itemList)
Buttry...except
is better, as you say. -
IARI over 8 yearsThis does not account for id < 0.
-
steabert over 8 yearsTo get a list of
'hello'
with as many items as in lista
, useb = ['hello'] * len(a)
-
kevlarr over 7 yearsHah, I came here with a really similar use case...
[a - b for a, b in zip(list1, list2)]
is so much nicer than[list1[i] - list2[i] for i in range(len(list1))]
.. Thanks! -
aquirdturtle over 6 yearsThis might be clearer:
for b_elem in b[:len(a)]:...
-
lukas_o about 6 yearsI am new to Python, what do the ** do in this case? I have read about *args and **kwargs, but this looks different.
-
Mateen Ulhaq about 6 yearsExponentiation. phi to the power of n.
-
Erik Kaplun almost 6 years
for ix, i in enumerate(a)
seems to be equivalent, no? -
flying sheep over 4 yearsOne should use pairwise instead.
-
PM 2Ring over 4 years@aquirdturtle Perhaps it's clearer, but your solution creates a new list, which may be expensive if b & a are large.
-
MisterMiyagi over 4 yearsThis should be handled using
itertools.islice
instead. -
Luca over 4 yearsIn those situations I do:
for a1,a2 in zip(a[:-1],a[1:])
-
Matiiss about 3 years
for index, item in enumerate(a)
-
stidmatt over 2 yearszip is the preferred method. Please use zip.
-
Joshua P. Swanson almost 2 years@stidmatt Why would zip be the preferred method when it is much less readable?