Iterator-driven for loops vs counter-driven for loops
A C-style for is three expressions around a counter: initialize, test, increment. It is syntactic sugar over a while loop and the index is the point of the construct. Python's for is a for-each over the iterator protocol: it calls iter() on the object once, then repeatedly calls next() on the resulting iterator, binds each value to the loop target, and stops when StopIteration is raised. There is no counter in the language construct at all; range(n) is simply an ordinary lazy iterable that happens to yield integers.
Why the difference matters: because for works on any iterable, the same loop handles lists, dicts, files, generators, database cursors and infinite streams, and it can be lazy and constant-memory. The loop code does not know or care how items are produced. The idiomatic replacements for index juggling are enumerate (index and value), zip (parallel iteration; zip(..., strict=True) from 3.10 raises on length mismatch), reversed, and sorted. Writing for i in range(len(x)): x[i] is the classic sign of C or Java habits.
Trade-offs and alternatives: you still want an index-based loop when you genuinely need to mutate by position, skip ahead, or walk two cursors at once; in that case a while loop with an explicit iterator or range with a step is clearer than contortions. Comprehensions and generator expressions are better than loops when you only build a new collection. Common mistakes: expecting that reassigning the loop variable affects the iteration (it doesn't), modifying a list while iterating over it (elements are skipped; for dicts and sets, changing size raises RuntimeError), expecting the loop variable to be scoped to the loop (it persists after the loop, and is undefined if the iterable was empty), and iterating a generator twice, because an iterator is exhausted after one pass.
Version notes: zip(strict=True) needs 3.10+, and async for with aiter/anext is the asynchronous variant of the same protocol. The semantics of the basic for statement have been stable across Python 3.
0-2 years experience
2-5 years experience
5-8 years experience
8+ years experience