Python For Loops Do Not Behave As Expected

For loops in Python are baffling to a VBA programmer.

NumLst = [8, 1, 3, 2]
for I in NumLst:
    print (I, end = ' ')

Output from this is
8 1 3 2

Not what I expected. I thought was asking Python to print the loop counter, not the contents of the loop.
I expected 0 1 2 3

Thought maybe `enumerate’ would get the result I expected. And it does - Kind of.

NumLst = [8, 1, 3, 2]
for I in enumerate (NumLst):
    print (I)

This code gives us
(0, 8)
(1, 1)
(2, 3)
(3, 2)

So it does print the loop counter, even if still not what I had in mind.

But all of the above is immaterial. I got sidetracked on trying to grasp for loops because I was unable to do what I originally set out to do. I wanted
to replace one or more of the values in NumLst with a different value. Tried this code:

for I in enumerate (NumLst):
    NumLst [I] = NumLst [I] + 1
    print (I)
print (NumLst)    

I expected

Instead I got:
Traceback (most recent call last):
File “C:/Users/Sam/AppData/Local/Programs/Python/Python39/Python OutPut/Prac 02.Py”, line 50, in
NumLst [I] = NumLst [I] + 1
TypeError: list indices must be integers or slices, not tuple

Well, the list indices ARE integers. Aren’t they??

So once more I’m asking you guys to point me in the right direction.

for value in some_list lets value iterate over all the elements of the list, not the possible indices for the list. Think for cake in all_pancakes: eat(cake). Indeed, this is different from the idioms found in other languages. In Python, the for loop works with an iterator, which yields the values. Lists are iterators, yielding their own values.

If you want an index, use range() (which yields integers):

for i in range(len(NumLst)):
    NumLst[i] += 1

The enumerate function is a convenient way to iterate over the indices and values at the same time. When you write for thing in enumerate(NumLst):, then at every step, thing is a 2-tuple containing an index and a value. It is not valid to pass this as an index to a list. You want to deconstruct it, which is most convenient in the line of the for:

for i, value in enumerate(NumLst):
    NumLst[i] = value + 1

There is no “loop counter” in Python, unless you specifically ask for
one. The usual way to do that is with enumerate.

for i, x in enumerate('abc'):
    print(i, x, end=' -- ')
# prints 0 a -- 1 b -- 2 c

Instead, Python for loops are more like “for each” loops in some other
languages, including VBA.

If you just want a loop counter alone, use range:

for i in range(10):
    print(i, end=' ')
# will print 0 1 2 3 4 5 6 7 8 9

for i in range(10, 101, 10):
    print(i, end=' ')
# will print 10 20 30 40 50 60 70 80 90 100

The signature of range is:

range(start=0, end, step=1)

where start defaults to 0, step defaults to 1, and end is excluded from
the values generated. Note that range isn’t magic syntax, it actually
returns an object holding a sequence of values:

>>> obj = range(10, 31, 5)
>>> len(obj)
>>> obj[0]
>>> obj[1]
>>> 21 in obj

The for each loop just iterates over the values of the range object.

Thanks Guys. I’m catching on. Slowly, to be sure, but I think I’m getting it.

I realize I’m rather late, and this may be a bit besides the point, but for situations like this, it is often simpler, more elegant and more performant to use Python’s comprehensions feature. However, one important note: This will create a new list object, rather than modifying your existing one in place, which may or may not be what you want if you have other code that references it. See your previous question for an excellent example of where this comes into play.

NumList = [num + 1 for num in NumLst]

For reference, a corrected form of your original logic:

for idx, num in enumerate(NumLst):
    NumLst[idx] = NumLst[idx] + 1