Learning Python - Mark Lutz [198]
>>> for i in range(3):
... print(i, 'Pythons')
...
0 Pythons
1 Pythons
2 Pythons
range is also commonly used to iterate over a sequence indirectly. The easiest and fastest way to step through a sequence exhaustively is always with a simple for, as Python handles most of the details for you:
>>> X = 'spam'
>>> for item in X: print(item, end=' ') # Simple iteration
...
s p a m
Internally, the for loop handles the details of the iteration automatically when used this way. If you really need to take over the indexing logic explicitly, you can do it with a while loop:
>>> i = 0
>>> while i < len(X): # while loop iteration
... print(X[i], end=' ')
... i += 1
...
s p a m
You can also do manual indexing with a for, though, if you use range to generate a list of indexes to iterate through. It’s a multistep process, but it’s sufficient to generate offsets, rather than the items at those offsets:
>>> X
'spam'
>>> len(X) # Length of string
4
>>> list(range(len(X))) # All legal offsets into X
[0, 1, 2, 3]
>>>
>>> for i in range(len(X)): print(X[i], end=' ') # Manual for indexing
...
s p a m
Note that because this example is stepping over a list of offsets into X, not the actual items of X, we need to index back into X within the loop to fetch each item.
Nonexhaustive Traversals: range and Slices
The last example in the prior section works, but it’s not the fastest option. It’s also more work than we need to do. Unless you have a special indexing requirement, you’re always better off using the simple for loop form in Python—as a general rule, use for instead of while whenever possible, and don’t use range calls in for loops except as a last resort. This simpler solution is better:
>>> for item in X: print(item) # Simple iteration
...
However, the coding pattern used in the prior example does allow us to do more specialized sorts of traversals. For instance, we can skip items as we go:
>>> S = 'abcdefghijk'
>>> list(range(0, len(S), 2))
[0, 2, 4, 6, 8, 10]
>>> for i in range(0, len(S), 2): print(S[i], end=' ')
...
a c e g i k
Here, we visit every second item in the string S by stepping over the generated range list. To visit every third item, change the third range argument to be 3, and so on. In effect, using range this way lets you skip items in loops while still retaining the simplicity of the for loop construct.
Still, this is probably not the ideal best-practice technique in Python today. If you really want to skip items in a sequence, the extended three-limit form of the slice expression, presented in Chapter 7, provides a simpler route to the same goal. To visit every second character in S, for example, slice with a stride of 2:
>>> S = 'abcdefghijk'
>>> for c in S[::2]: print(c, end=' ')
...
a c e g i k
The result is the same, but substantially easier for you to write and for others to read. The only real advantage to using range here instead is that it does not copy the string and does not create a list in 3.0; for very large strings, it may save memory.
Changing Lists: range
Another common place where you may use the range and for combination is in loops that change a list as it is being traversed. Suppose, for example, that you need to add 1 to every item in a list. You can try this with a simple for loop, but the result probably won’t be exactly what you want:
>>> L = [1, 2, 3, 4, 5]
>>> for x in L:
... x += 1
...
>>> L
[1, 2, 3, 4, 5]
>>> x
6
This doesn’t quite work—it changes the loop variable x, not the list L. The reason is somewhat subtle. Each time through the loop, x refers to the next integer already pulled out of the list. In the first iteration, for example, x is integer 1. In the next iteration, the loop body sets x to a different object, integer 2, but it does not update the list where 1 originally came from.
To really change the list