Python For Loops, using range vs enumerate
Python for loops are different to the conventional programming languages in a key aspect, its syntax doesn't use an iterator.
But that is a feature not a bug, the 'Pythonic' way to write for loop is to run it over a range or a list by item and not by using an index to refer to the next element like C or Java. E.g.
fruits = ['apple', 'orange', 'banana', 'tomato', 'cucumber']
for fruit in fruits:
print(fruit)
Will print out
apple
orange
banana
tomato
cucumber
If we wanted to do the same in C, not withstanding the complexities, it would look like,
for (int i = 0, i < 5, i++)
{
printf("%s", fruits[i])
}
Range and Enumerate functions
But there are many scenarios where you might need to iterate using index. For such cases, Python has two in-builts functions range()
and enumerate()
that provides this feature.
range()
Range is used to iterate over a sequence of numbers, e.g. to print 0 - 4,
for i in range(5):
print(i)
We can use this to iterate over out fruits list with an iterator
fruits = ['apple', 'orange', 'banana', 'tomato', 'cucumber']
for i in range(5):
print(fruits[i])
But you now have a new problem, in the first example, we didn't have to worry about the length of the list, with range you do. You can still solve it by usng len()
method
fruits = ['apple', 'orange', 'banana', 'tomato', 'cucumber']
for i in range(len(fruits)):
print(fruits[i])
enumerate()
Instead of calculating the length and iterating over the list, we can also use enumerate() to get the same results.
fruits = ['apple', 'orange', 'banana', 'tomato', 'cucumber']
for i, item in enumerate(fruits):
print(f"Using iterator: {fruits[i]}")
print(f"Using item: {item}")
Range vs Enumerate: What should you use?
As with most things, the answer is, it depends! And more often than not, it will end up being a personal choice.
But from a performance perspective, we can test it.
Performance testing range() and enumerate()
Let's start by setting up a baseline. We will generate a list with 10000 integers, and then compute if each one of them are prime number of not and add them to an output list.
Baseline: Simple iteration
from time import time
# start the time counter
start = time()
def prime(num):
'''Function to compute if a number is prime'''
for i in range(2, int(num/2)+1):
if num % i == 0:
return False
return True
# Generating a list of integers
n = 9999
inputs = [i for i in range(n)]
outputs = []
for number in inputs:
outputs.append(f"{number} is prime? {prime(number)}")
end = time()
print(end - start)
For me it took ~44.6 secs
Using range()
from time import time
# start the time counter
start = time()
def prime(num):
'''Function to compute if a number is prime'''
for i in range(2, int(num/2)+1):
if num % i == 0:
return False
return True
# Generating a list of integers
n = 9999
inputs = [i for i in range(n)]
outputs = []
for i in range(len(inputs)):
outputs.append(f"{inputs[i]} is prime? {prime(inputs[i])}")
end = time()
print(end - start)
Took roughly ~47.7 seconds
Using enumerate()
from time import time
# start the time counter
start = time()
def prime(num):
'''Function to compute if a number is prime'''
for i in range(2, int(num/2)+1):
if num % i == 0:
return False
return True
# Generating a list of integers
n = 9999
inputs = [i for i in range(n)]
outputs = []
for i, num in enumerate(inputs):
outputs.append(f"{i} is prime? {prime(num)}")
end = time()
print(end - start)
This took about ~45.76 seconds
Conclusion
If you're iterating over a list, enumerate is probably the most optimal option.