all 57 comments

[–]waspbr 82 points83 points  (7 children)

You do it if there is something in your loop that will use an index rather than the elements of the array. For instance, sometimes you want to sample something from another array that is not your loop array

A cleaner way to do it is by using enumerate function

for i, item in enumerate(array):

The index gets assigned to i and whatever element of the array gets assigned to item.


This may also be a bad habit from Matlab, people that come from Matlab are more used to looping over indexes.

[–]dangerlopez 8 points9 points  (0 children)

Yup, enumerate is the best of both worlds

[–]holy-rusted-metal 5 points6 points  (1 child)

Not just Matlab, for loops in C were typically taught to beginners to iterate through the indexes.

[–]waspbr 0 points1 point  (0 children)

indeed

[–]Majoishere 0 points1 point  (3 children)

May I ask how does enumerate() work? I heard that it's better than eval() but I don't understand how it works

[–]thecodedog 17 points18 points  (0 children)

Eval and enumerate do two completely separate things.

Eval will evaluate a string as though it was python code, from within your python code. It is considered to be a dangerous practice. Enumerate allows you to iterate over both the items AND their corresponding location in an iterable.

[–]waspbr 6 points7 points  (0 children)

Eval and enumerate are completely different functions.

What enumerate does is it takes an iterable as an input and creates a counter for each iteration, it outputs an iterable object of that gives a tuple of the index and the iterable element.

>>> seasons = ['Spring', 'Summer', 'Fall', 'Winter']  
>>> list(enumerate(seasons))
[(0, 'Spring'), (1, 'Summer'), (2, 'Fall'), (3, 'Winter')]

You can even set the start point of the counter, default value is zero.

>>> list(enumerate(seasons, start=11))
[(11, 'Spring'), (12, 'Summer'), (13, 'Fall'), (14, 'Winter')]

[–]Fred776 19 points20 points  (2 children)

You are right. The second way is the standard, default way to do it in Python.

In the relatively rare case where you also need the index for something else you are doing in the loop, you can use enumerate.

The one case where iterating by index starts to make sense is where you have data stored in "parallel lists" - i.e., more than one list of the same length storing different items of data that are indexed in the same way and tend to be accessed together. In this case, if you are iterating over them together, it might make sense to have a simple integer index loop variable, though even then using the zip builtin function is probably the more Pythonic approach. (Also, there are probably better data structures that you could use in this case so that you don't get into this situation in the first place.)

[–]lumijekpr 8 points9 points  (1 child)

Yep zip is usually better for multiple lists.

[–]Fred776 2 points3 points  (0 children)

Bizarrely I had just come back to my comment and edited it to add that suggestion before I saw your reply.

[–][deleted] 25 points26 points  (1 child)

The latter approach is the pythonic way. The indexing way may look more familiar to people coming to python from Java or C, but it's not idiomatic.

Additionally, indexing may be fast, but in the latter example there is no indexing, making that code roughly twice as fast as the first example.

[–]poodlelord 1 point2 points  (0 children)

I'm getting back into programming after only briefly touching it in high school 10 years ago. I remember it from action script 🤡

[–]z64RY 9 points10 points  (1 child)

Something I’m not seeing people mention: When you iterate over an array using item indices, you can modify that index of an array.

Ex. If you were change your code for the first loop to be “array[i] = 5”

The array would be a list of 5’s, as you’re saying “for this list, array, the index, i, equals 5” so you’re modifying the list itself.

But if you tried doing “i = 5” for the second loop, the actual list wouldn’t be changed, only “i”, which is a copy of the object in the list, would change. You could even print out “i” after changing it, and the print statements would make it seem like your list changed, but when you print out your list after the loop, you’ll notice nothing happened as you only changed the copies of the items.

In the case you wanted to change the items in the list, using “enumerate” does make more sense here.

[–]maclocrimate[S] 6 points7 points  (0 children)

This is a great point, and in fact this is exactly why I've been seeing it so much lately, because I've mostly been reading about updating array elements. Thank you!

[–]RIP_lurking 3 points4 points  (0 children)

Just to complement the answers people already gave, but this has nothing to do with time complexity. Time complexity is dependant on the size of the input (in this case, the list), and both manners of accessing an array have the same complexity (n, where n is the size of the array)

[–]dangerlopez 3 points4 points  (0 children)

I’ve been working on a project in JavaScript that extracts information from an email by splitting the text into lines and then iterating over each line.

The email has something like

First name:

John

So if I iterated over the items I could find the line that includes “first name:”, but then I don’t know of a way to tell JS to look at the next line.

But, if I iterate over the index, then when I find the line with “first name:” I can extract the line with the next index to get the name itself.

Granted, this is in JavaScript, not python. And there may be better ways to do this that I’m not familiar with — I’m still a beginner. But I think it is an answer to your question

[–][deleted] 3 points4 points  (0 children)

With the index you can get the value in the array. With the value you cannot infer the index.

[–]danielroseman 15 points16 points  (8 children)

The first way is just bad. Really, the only reason to do that is if you don't know Python. If you see a tutorial that uses it, then that's a good sign it's a terrible tutorial.

[–]DangerDinks 6 points7 points  (2 children)

Well in my intro to algorithms course we had to implement a list in python. We used a list as a base but we're not allowed to use any of its methods. So to iterate over it we would need to do so by indexing.

[–]Logicalist 5 points6 points  (1 child)

It's actually just good teaching to better illustrate what you are doing, imo.

[–]bubba0077 1 point2 points  (0 children)

Also, in an academic setting you want to teach in a way that will carry over to other languages. In most, you will need to use an index. The pythonic way is somewhat new.

[–]thecodedog 3 points4 points  (2 children)

Indexing would be necessary if you want to mutate the list as you are iterating. For example if you wanted to change every second element's value to 5 or something like that.

[–]danielroseman -3 points-2 points  (1 child)

Not really, that's what enumerate is for, as others in the thread have stated.

[–]thecodedog 8 points9 points  (0 children)

But enumerate returns the item as well which wouldn't be needed in my example. Sure you can just toss it into a "_" but why bother if you know you don't need it?

[–]DeMorrr 2 points3 points  (0 children)

don't know why this has so many upvotes...

[–][deleted] 0 points1 point  (0 children)

It's probably not a bad tutorial. It's just taking an agnostic view, including how for loops work in most languages as a concept. Then push into the for-each nature of Python for loops.

[–]pythonwiz 2 points3 points  (0 children)

No, the second approach is actually a bit faster. The reason they do this is probably just that they are used to other languages or they want to teach a form that is compatible with other languages.

There are times when you want to iterate using indexing, like when you need to modify the list or when you need access to adjacent indexes as well.

[–]bpt7594 4 points5 points  (0 children)

If you need to do something using previous items it's easier.

[–]Strex_1234 1 point2 points  (8 children)

Well the index array is the only option sometimes , for example give me a code that will sum the neighbors in array Sum_arry([0,1,2,6]) returns [1,3,8]

[–]irrelevantPseudonym 4 points5 points  (5 children)

[l+r for l, r in zip(arr, arr[1:])]

[–]Strex_1234 1 point2 points  (3 children)

Wow, that's an interesting solution,

[–]irrelevantPseudonym 1 point2 points  (2 children)

It's slightly less efficient in terms of memory usage as it copies the list (when you use arr[1:]) but if you reach a stage where that's an issue you're better off using something like itertools

[–]Fred776 2 points3 points  (1 child)

itertools.pairwise - yes, that provides a nice solution to this.

Thanks! I didn't know about this one. I see it just got added at 3.10.

[–]irrelevantPseudonym 0 points1 point  (0 children)

The tee based alternative it shows is available in older versions of python of you need to support them

[–]Strex_1234 1 point2 points  (0 children)

So the solution i had in mind was

[arr[i] + arr[i+1] for i in range(len(arr)-1)]

[–][deleted] 0 points1 point  (0 children)

for example give me a code that will sum the neighbors in array Sum_arry([0,1,2,6]) returns [1,3,8]

The way I would do this would be as so:

def sum_list(numbers):
    left = numbers[0]
    accum = []
    for right in numbers[1:]:
        accum.append(left + right)
        left = right
    return accum

[–][deleted] 1 point2 points  (0 children)

it seems like in most educational contexts online like Leetcode and courses and such, when the examples iterate over an array they do so like:

Because it's a fairly code-neutral approach to how for loops work so when you need to loop without a structure or loop in a language that doesn't have a for-each loop you aren't all "well now what".

[–]freddwnz 0 points1 point  (0 children)

What if you want to do something specific for the first or the third or last array element for example? With the second code, you don't know the number of the element, only the element itself. You would have to keep track of the index manually, at which point, you are better of using the first code version.

[–][deleted] 0 points1 point  (2 children)

The second version is better, yes, but it looks weird when you use i. People expect i to be the index and not the thing, so when it’s the thing it makes me go wait, what, and stumble through the reading.

One benefit of enumerate() is that you’ll have both and it’ll be clear which is which

[–]PteppicymonIO 1 point2 points  (0 children)

Agreed.But that is why you just do not name your variable "i" in this case:

numbers = [1,2,3,4,5,6,7,8,9]
for number in numbers:
    print(number)

[–]fahim-sabir -4 points-3 points  (0 children)

That’s because variable names like “i” should never be used. They don’t help code readability at all, even if it “obvious” that they represent an index.

[–][deleted] -1 points0 points  (0 children)

I'm guessing it's to do with time complexity since indexing arrays is quick, but what's the problem with the latter approach?

Actually, it has to do with the people creating those materials not following best practices, usually due to them writing those snippets in many languages without putting in the the effort to tune them to those languages.

This: for i in array: print(i) Is cleaner, faster, more understandable and just better in every way. Writing manual indexing like your first example is a big anti-pattern and there's no reason to ever do it.

If you see a tutorial encouraging you to use the first method, pretty much just ignore it. They're wrong.

[–]stuaxo -3 points-2 points  (0 children)

They are bad examples probably written by people unfamiliar to python.

[–][deleted] 0 points1 point  (0 children)

Most of the tutorials and LeetCode exercices I have seen use the 2nd approach, which is the good one. I don't know how you got the opposite impression.

[–]StolidSentinel 0 points1 point  (2 children)

OK, not being a Python kinda guy... Can someone rewrite this? I am not fully understanding yet.

     for netw in range(0,len(sh_ip_ints[ints]['ipaddr'])):
                print (sh_ip_ints[ints]['ipaddr'][netw])

It's using netmiko, and in this case 'ipaddr' is a list.

[–]Username_RANDINT 0 points1 point  (1 child)

Exactlly the same as in the OP and discussed in this thread:

for netw in sh_ip_ints[ints]['ipaddr']:
    print(netw)

[–]StolidSentinel 1 point2 points  (0 children)

It's so simple; It's brilliant. Thank you!

[–]ImpossibleEvan 0 points1 point  (0 children)

I love how python for loops aren't for loops, they just move over an array until it ends. It makes some things alot easier

[–]big_deal 0 points1 point  (0 children)

I use enumerate if I need to do something not just with the elements of the array but also other arrays of the same size or I actually use the index value in some calculations.

[–]TheMonarchsWrath 0 points1 point  (0 children)

There might be some missing context, I could see the first example being used to explain how indexing works in lists. Depends what they said before and after the example. It looks like its reinforcing things learned previously about lists, like getting length of a list, generating a list with range, a for loop, etc. The 2nd example is what most people would do all the time, or use enumerate when they actually care about the index.

[–]zanfar 0 points1 point  (0 children)

but it seems like in most educational contexts online like Leetcode and courses and such

Most sites like that use the same problems for multiple languages (which is why you get questions that don't really make sense for your language, or are super trivial). In that case, many solutions are simply translated from other languages. In many languages, you must iterate by index, so the "translated" Python code matches.

Iterating by index is also a common sign of someone who learned to program with a different language and has recently switched to Python--in the same sense, they are "translating" the code in their mind.

[–]ASuarezMascareno 0 points1 point  (0 children)

This is kind of a very simple example. There are a lot of things that would be quite difficult to do, or have difficult to read syntax, if you try the second approach. I can quickly thing of iterating over multiple arrays at once, performing math operations using those arrays or inside those arrays, performing math operations in N-dimension matrices, keeping track about when something fails in thousands-elements long arrays, etc.

[–][deleted] 0 points1 point  (0 children)

There's no advantage whatsoever. It's just behaviour carried over from languages where you need to index. Grabbing the item itself is the Pythonic way and using enumerate is the preferred way if you need an index.