Idiomatic loops

Looping in general

In [1]:
data = ['John', 'Doe', 'was', 'here']

Don't do it like this. While loops are actually really rarely needed.

In [2]:
idx = 0
while idx < len(data):
    print(data[idx])
    idx += 1
John
Doe
was
here

Don't do like this either.

In [3]:
for idx in range(len(data)):
    print(data[idx])
John
Doe
was
here

Do it like this!

In [4]:
for item in data:
    print(item)
John
Doe
was
here

If you need the index as well, you can use enumerate.

In [5]:
for idx, val in enumerate(data):
    print('{}: {}'.format(idx, val))
0: John
1: Doe
2: was
3: here

Looping over a range of numbers

Don't do this.

In [6]:
i = 0
while i < 6:
    print(i)
    i += 1
0
1
2
3
4
5

Don't do this either.

In [7]:
for val in [0, 1, 2, 3, 4, 5]:
    print(val)
0
1
2
3
4
5

Do it like this!

In [8]:
for val in range(6):
    print(val)
0
1
2
3
4
5

Reversed looping

In [9]:
data = ['first', 'to', 'last', 'from'] 

This is no good.

In [10]:
i = len(data) - 1
while i >= 0:
    print(data[i])
    i -= 1
from
last
to
first

Do it like this!

In [11]:
for item in reversed(data):
    print(item)
from
last
to
first

Looping over n collections simultaneously

In [12]:
collection1 = ['a', 'b', 'c']
collection2 = (10, 20, 30, 40, 50)
collection3 = ['John', 'Doe', True]

Oh boy, not like this.

In [13]:
shortest = len(collection1)
if len(collection2) < shortest:
    shortest = len(collection2)
if len(collection3) < shortest:
    shortest = len(collection3)
    
i = 0
while i < shortest:
    print(collection1[i], collection2[i], collection3[i])
    i += 1
a 10 John
b 20 Doe
c 30 True

This is getting better but there's even a better way!

In [14]:
shortest = min(len(collection1), len(collection2), len(collection3))
for i in range(shortest):
    print(collection1[i], collection2[i], collection3[i])
a 10 John
b 20 Doe
c 30 True

Do it like this!

In [15]:
for first, second, third in zip(collection1, collection2, collection3):
    print(first, second, third)
a 10 John
b 20 Doe
c 30 True

You can also create a dict out of two collections!

In [16]:
my_dict = dict(zip(collection1, collection2))
print(my_dict)
{'a': 10, 'b': 20, 'c': 30}

for - else - Checking for a match in a collection

Let's say we want to verify a certain condition is met by at least one element in a collection. Let's consider the following relatively naive example where we want to verify that at least one item is "python" (case insensitive) in data. If not, we'll raise a ValueError.

In [17]:
data = [1, 2, 3, 'This', 'is', 'just', 'a', 'random', 'Python', 'list']

Don't do it like this

In [18]:
found = False
for val in data:
    if str(val).lower() == 'python':
        found = True
        break
if not found:
    raise ValueError("Nope, couldn't find.")

Do it like this!

In [19]:
for val in data:
    if str(val).lower() == 'python':
        break
else:
    raise ValueError("Nope, couldn't find.")