How to Access the Index in 'Foreach' Loops in Python

Aliaksei Yursha Feb 02, 2024
How to Access the Index in 'Foreach' Loops in Python

Often times when you are looping over a collection of items in Python you also need to have an ordinal number associated with each item. Such a number is usually referred to as the index.

The pythonic way of accessing the index while doing a foreach-loop is to use the built-in function enumerate().

enumerate() adds an index to each item in an iterable collection.
You then can use the list() built-in function to get back a list of tuples.

>>> names = ['Bob', 'Alice', 'John', 'Cindy']
>>> list(enumerate(names))
[(0, 'Bob'), (1, 'Alice'), (2, 'John'), (3, 'Cindy')]

Note, Python indexing starts from 0.

However, if you want to start indexing from 1 (for example, when printing a report data), it’s trivially achieved by passing an additional start argument to enumerate.

>>> names = ['Bob', 'Alice', 'John', 'Cindy']
>>> list(enumerate(names), start=1)
[(1, 'Bob'), (2, 'Alice'), (3, 'John'), (4, 'Cindy')]

The above approach is also useful if you’re printing data from different data sources but want to have a consistent index across all data items.

>>> data_source1 = ['Bob', 'Alice', 'John', 'Cindy']
>>> data_source2 = ['Sarah', 'Jeff', 'Emily', 'Adam']
>>> list(enumerate(data_source1, start=1))
[(1, 'Bob'), (2, 'Alice'), (3, 'John'), (4, 'Cindy')]
>>> list(enumerate(data_source2, start=len(data_source1) + 1))
[(5, 'Sarah'), (6, 'Jeff'), (7, 'Emily'), (8, 'Adam')]

Note, that you don’t have to convert an Enumerate object to a list.
It is an iterable itself so can iterate over it directly.

>>> names = ['Bob', 'Alice', 'John', 'Cindy']
>>> for idx, name in enumerate(names):
...   print(name)
...
Bob
Alice
John
Cindy

There may be other ways to get a hold of the index while iterating over a collection of items,
but using enumerate() is considered canonical and is preferred.

Another option would be to use a separate index variable, initialize it manually and
increment it manually at each iteration.
Such an approach, however, would be considered to be a reinvention of the wheel and frowned upon.

Here is a quote from PEP 279 in which enumerate() function was introduced.

This PEP introduces a new built-in function, enumerate() to simplify a commonly used looping idiom. It provides all iterable collections with the same advantage that iteritems() affords to dictionaries – a compact, readable, reliable index notation.

You may ask if there are any performance implications of using enumerate in your code.
Well, you are free to do all the measurements, but please, keep in mind, that enumerate is an idiomatic way to access an index of an iterable in a for-each loop.
Idiomatic code is recommended by the Python maintainers and they will make everything to
make it run efficiently.

And for those of you, who are most curious below is a possible implementation of enumerate() function from the above PEP.

def enumerate(collection):
    # Generates an indexed series:  (0,coll[0]), (1,coll[1]) ...
    i = 0
    it = iter(collection)
    while 1:
        yield (i, it.next())
        i += 1

Related Article - Python Loop