Wednesday, October 7, 2020

Python Morsels: Looping with Indexes

Related Articles:

Transcript

If you've used another programming language before, you've probably used indexes while looping. Often when you're trying to loop with indexes in Python, you'll find that you actually care about counting upward as you're looping, not actual indexes.

Let’s say we have a variable, favorite_fruit that points to a list of strings:

>>> favorite_fruits = ["jujube", "pear", "watermelon", "apple", "blueberry"]

Using a counter to keep track of indices

We're looping over favorite_fruits list here:

>>> n = 1
>>> for fruit in favorite_fruits:
...     print(n, fruit)
...     n += 1
...
1 jujube
2 pear
3 watermelon
4 apple
5 blueberry

We're printing out n and fruit (which represents each of our fruits). The n variable is a counter that we're keeping track of ourselves. We start n at 1 and we're incrementing it by 1 in each iteration of our loop.

Using range of len (not recommended)

Instead of keeping track of counter ourselves, how about thinking in terms of indices and using range(len(favorite_fruits)) to grab each of the indexes of all the items in this list:

>>> favorite_fruits = ["jujube", "pear", "watermelon", "apple", "blueberry"]
>>>
>>> for i in range(len(favorite_fruits)):
...     print(i+1, favorite_fruits[i])
...
1 jujube
2 pear
3 watermelon
4 apple
5 blueberry

We’re using i to index the list manually and add one to each of these indices. This gives us the same result as before.

I wouldn't recommend doing this because the official Python style guide, PEP 8, recommends against it and instead of using range of len, there is usually one of two techniques you'll want to use.

We can entirely get rid of this and instead say for fruit in favorite_fruits:

>>> favorite_fruits = ["jujube", "pear", "watermelon", "apple", "blueberry"]
>>>
>>> for fruit in favorite_fruits:
...     print(fruit)
...
jujube
pear
watermelon
apple
blueberry

Here we've decided we don't need any counter in our loop at all.

If you do decide you actually need some kind of counting as you're looping, you'll want to use the built-in enumerate function.

enumerate returns both index and value

Python's enumerate function is for counting upward as you loop over an iterable.

If we give enumerate our iterable (our favorite_fruits list in this case) and an optional start value (which defaults to 0):

>>> favorite_fruits = ["jujube", "pear", "watermelon", "apple", "blueberry"]
>>>
>>> for fruit in enumerate(favorite_fruits):
...     print(fruit)
...
(0, 'jujube')
(1, 'pear')
(2, 'watermelon')
(3, 'apple')
(4, 'blueberry')

We'll get an iterable back that can be looped over to get tuples containing numbers counting upward along with each item in the iterable that we passed to enumerate.

If we want to start counting at a different number, we can set the start keyword argument when calling enumerate:

>>> favorite_fruits = ["jujube", "pear", "watermelon", "apple", "blueberry"]
>>>
>>> for fruit in enumerate(favorite_fruits, start=1):
...     print(fruit)
...
(1, 'jujube')
(2, 'pear')
(3, 'watermelon')
(4, 'apple')
(5, 'blueberry')

Tuple Unpacking

As we loop over enumerate, it's gives us back two-item tuples that have two things inside them:

>>> favorite_fruits = ["jujube", "pear", "watermelon", "apple", "blueberry"]
>>>
>>> for item in enumerate(favorite_fruits, start=1):
...     print(item)
...
(1, 'jujube')
(2, 'pear')
(3, 'watermelon')
(4, 'apple')
(5, 'blueberry')

It has a number that keeps track of counting upward and the actual item that it's grabbing from our favorite_fruits list as it's looping over it .

We could index this tuple, using item[0] and item[1], to grab these two values:

>>> favorite_fruits = ["jujube", "pear", "watermelon", "apple", "blueberry"]
>>>
>>> for item in enumerate(favorite_list, start=1):
...     print(item[0], item[1])
...
1 jujube
2 pear
3 watermelon
4 apple
5 blueberry

But a better way is to use tuple unpacking to give each of these two values a name:

>>> favorite_fruits = ["jujube", "pear", "watermelon", "apple", "blueberry"]
>>>
>>> for item in enumerate(favorite_fruits, start=1):
...     n, fruit = item
...     print(n, item)
...
1 (1, 'jujube')
2 (2, 'pear')
3 (3, 'watermelon')
4 (4, 'apple')
5 (5, 'blueberry')

The number (n) and that actual item (fruit) are both important here. If something is important, it deserves a name.

Tuple unpacking is allowed anywhere an assignment happens, and that for line has an assignment happening within it, so we can unpack at the same time as we loop:

>>> favorite_fruits = ["jujube", "pear", "watermelon", "apple", "blueberry"]
>>>
>>> for n, fruit in enumerate(favorite_fruits, start=1):
...     print(n, fruit)
...
1 jujube
2 pear
3 watermelon
4 apple
5 blueberry

This is the most common way you'll see enumerate used: you'll almost always see tuple unpacking used whenever enumerate is used.

Summary

If you think you need to loop with indices, first, ask yourself a question: do I even need some kind of counter as I'm looping? If you don't need a counter, just use a for loop without any frills. But if you do need to count upward while looping, you can use Python's built-in enumerate function to help you do that counting.



from Planet Python
via read more

No comments:

Post a Comment

TestDriven.io: Working with Static and Media Files in Django

This article looks at how to work with static and media files in a Django project, locally and in production. from Planet Python via read...