Use enumerate() and zip() in Python

In Python, enumerate() and zip() are simple and useful when iterating elements of iterable (list,tuple, etc.) in afor loop.
You can get the index with enumerate(), and get the elements of multiple iterables with zip().

enumerate()

enumerate(iterable, start=0)

Equivalent to:

def enumerate(sequence, start=0):
    n = start
    for elem in sequence:
        yield n, elem
        n += 1

for loop with enumerate()

Normal for loop:

names = ['Alice', 'Bob', 'Charlie']

for name in names:
    print(name)
# Alice
# Bob
# Charlie

While by passing an iterable object in the argument of enumerate(), you can get index, element.

for i, name in enumerate(names):
    print(i, name)
# 0 Alice
# 1 Bob
# 2 Charlie

Start index from 1 with enumerate()

As in the example above, by default the index of the enumerate() starts from 0.
If you want to start from another number, pass it in the second argument of the enumerate().

Example starting from 1:

for i, name in enumerate(names, 1):
    print(i, name)
# 1 Alice
# 2 Bob
# 3 Charlie

Of course it can start from other number

zip()

In Python, the built-in function zip() aggregates the elements from multiple iterable objects (lists, tuples, etc.). It is used when iterating multiple list elements in a for loop.

Get elements from multiple lists in for loop

By passing an iterable object (lists, tuples, etc.) as an argument of zip(), multiple elements can be obtained simultaneously in the for loop.

names = ['Alice', 'Bob', 'Charlie']
ages = [24, 50, 18]

for name, age in zip(names, ages):
    print(name, age)
# Alice 24
# Bob 50
# Charlie 18

The same applies not only to two, but also to three or more.

points = [100, 85, 90]

for name, age, point in zip(names, ages, points):
    print(name, age, point)
# Alice 24 100
# Bob 50 85
# Charlie 18 90

In the case that number of elements is different

  1. zip() ignores the extra elements

When the number of elements of each iterable object is different, zip() ignores the extra elements.

names = ['Alice', 'Bob', 'Charlie', 'Dave']
ages = [24, 50, 18]

for name, age in zip(names, ages):
    print(name, age)
# Alice 24
# Bob 50
# Charlie 18
  1. itertools.zip_longest() fills in the missing elements

By itertools.zip_longest(), you can fill the missing elements with any values.

By default it is filled with None.

from itertools import zip_longest

names = ['Alice', 'Bob', 'Charlie', 'Dave']
ages = [24, 50, 18]

for name, age in zip_longest(names, ages):
    print(name, age)
# Alice 24
# Bob 50
# Charlie 18
# Dave None

You can specify the value to fill in the argument fillvalue.

for name, age in zip_longest(names, ages, fillvalue=20):
    print(name, age)
# Alice 24
# Bob 50
# Charlie 18
# Dave 20

The value to be filled is uniform even if there are multiple lists with insufficient elements. You can't specify different values.

points = [100, 85]

for name, age, point in zip_longest(names, ages, points, fillvalue=20):
    print(name, age, point)
# Alice 24 100
# Bob 50 85
# Charlie 18 20
# Dave 20 20

If you want to fill multiple lists with unknown numbers of elements with different values, follow the procedure below.

  1. Define the value to fill for all lists
  2. Get maximum number of elements
  3. Fill all lists up to maximum number of elements
  4. Aggregate with zip()
fill_name = 'XXX'
fill_age = 20
fill_point = 50

len_names = len(names)
len_ages = len(ages)
len_points = len(points)

max_len = max(len_names, len_ages, len_points)

names = names + [fill_name] * (max_len - len_names)
ages = ages + [fill_age] * (max_len - len_ages)
points = points + [fill_point] * (max_len - len_points)

print(names)
print(ages)
print(points)
# ['Alice', 'Bob', 'Charlie', 'Dave']
# [24, 50, 18, 20]
# [100, 85, 50, 50]

for name, age, point in zip(names, ages, points):
    print(name, age, point)
# Alice 24 100
# Bob 50 85
# Charlie 18 50
# Dave 20 50

The process of filling the list to the maximum number of elements uses initialization and concatenation of lists.
It can be a function like this:

def my_zip_longest(iterables, fillvalues):
    max_len = max(len(i) for i in iterables)
    return zip(*[list(i) + [v] * (max_len - len(i)) for i, v in zip(iterables, fillvalues)])

for name, age, point in my_zip_longest((names, ages, points), ('XXX', 20, 50)):
    print(name, age, point)
# Alice 24 100
# Bob 50 85
# Charlie 18 50
# Dave 20 50

Get a list of multiple iterable elements

zip returns an iterator (zip object) that contains tuple with the elements of multiple iterable objects.

names = ['Alice', 'Bob', 'Charlie']
ages = (24, 50, 18)

z = zip(names, ages)
print(z)
print(type(z))
# <zip object at 0x10b57b888>
# <class 'zip'>

It can be converted to a list with list().

l = list(zip(names, ages))
print(l)
print(type(l))
print(type(l[0]))
# [('Alice', 24), ('Bob', 50), ('Charlie', 18)]
# <class 'list'>
# <class 'tuple'>

Using enumerate() and zip() together

If you want to get the elements of multiple lists and indexes, you can use enumerate() and zip() together.
In this case, you need to enclose the elements of zip() in parentheses, like for i, (a, b, ...) in enumerate(zip( ... )).

names = ['Alice', 'Bob', 'Charlie']
ages = [24, 50, 18]

for i, (name, age) in enumerate(zip(names, ages)):
    print(i, name, age)
# 0 Alice 24
# 1 Bob 50
# 2 Charlie 18

You can also receive the elements of zip() as a tuple.

for i, t in enumerate(zip(names, ages)):
    print(i, t)
# 0 ('Alice', 24)
# 1 ('Bob', 50)
# 2 ('Charlie', 18)
for i, t in enumerate(zip(names, ages)):
    print(i, t[0], t[1])
# 0 Alice 24
# 1 Bob 50
# 2 Charlie 18

Other

itertools.product() && List comprehensions

for loop in Python (with range, enumerate, zip, etc.)

itertools.count(), itertools.cycle(), and itertools.repeat()

Infinite iterators in Python (itertools.count, cycle, repeat)

References

  1. enumerate() in Python: Get the element and index from a list
  2. zip() in Python: Get elements from multiple lists
posted @ 2021-12-28 23:10  Rogn  阅读(122)  评论(0编辑  收藏  举报