Skip to content

Instantly share code, notes, and snippets.

@Descent098
Last active August 22, 2021 18:52
Show Gist options
  • Save Descent098/f7200fe8ef6484229b507c21c55d8985 to your computer and use it in GitHub Desktop.
Save Descent098/f7200fe8ef6484229b507c21c55d8985 to your computer and use it in GitHub Desktop.
An overview of python for loops
from timeit import timeit
from random import randint
items = [randint(0,100) for i in range(10000)]
native_loop="""
for item in items:
a = item + 3
"""
range_loop="""
for i in range(len(items)):
a = items[i] + 3
"""
print(timeit(stmt=native_loop, number=1000, globals=globals()))
print(timeit(stmt=range_loop, number=1000, globals=globals()))

Many people will use c-style index loops when accessing variables in python, such as

shopping_list = ["eggs", "ham", "cheese"]

for i in range(len(shopping_list)):
    print( shopping_list[i] )

Typically this is out of habit for doing so in C-style languages like Java. However for read-only loops where you are just reading variables (like printing out the variables in a list above), is this really the best way to do it?

In python unless you need to do modifications at a given index (i.e. add 3 to current index value in the list and put it back in the list), or do index manipulations (i.e. look at next item and compare to current item) native loops are said to be preferred:

shopping_list = ["eggs", "ham", "cheese"]

for item in shopping_list:
    print( item )

The question is whether these loops actually give tangible performance benefits.

Performance

Using the above example code natively looping is rougly twice as fast as index based lookups:

Run Number Native time range time
1 0.2184892 0.413237
2 0.2162527 0.42889309999999997
3 0.2056418 0.389417
4 0.2205241 0.4253023000000001
5 0.2072469 0.42477750000000003

Enumerate overhead

In the case where the index is necessary for some other operation for example printing a string with a numbered list of items:

for index, item in enumerate(items):
    print(f"{index}: {item}"

I added an additional test to the original code and retested:

enumerate_loop="""
for index, item in enumerate(items):
    a = item + index + 3
"""

print(timeit(stmt=enumerate_loop, number=1000, globals=globals()))
Run Number Native time range time Enumerate time
1 0.1933021 0.6091707 0.5894371
2 0.2083234 0.6091707 0.5934314
3 0.20959899999999998 0.39127399999999996 0.6037809
4 0.20570290000000002 0.4209888 0.5844912
5 0.19014240000000002 0.4295017 0.6006898

Does enumerate slowdown the loop to the same speed as a normal ranged loop?

Short answer is basically yes. In fact sometimes it's actually slower, so between C-style loops and enumerate loops it's basically down to preference.

Conclusion

Unless you need to modify a collection as you are iterating through it then native looping is the better option.

Native itteration also allows you to use Generators which are significantly faster for single-iteration scenarios, and it allows you to use custom iterables (since most don't implement index lookups unless they are direct subclasses of collections, but will have the __next__() method for native iteration).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment