4.1.8 序列上索引值迭代

在遍历序列时,有时需要跟踪被遍历元素的索引。对于这种需求,不少开发人员习惯根据序列长度,使用数组下标的方式取得元素索引。

在Python中,不使用数组下标也可以实现在迭代序列的同时跟踪正在被处理的元素索引。如使用内置的enumerate()函数,代码(index_iter.py)示例如下:


test_list = ['a', 'b', 'c']
for idx, str_val in enumerate(test_list):
    print(f'index is: {idx}, str value is: {str_val}')

执行py文件,输出结果如下:


index is: 0, str value is: a
index is: 1, str value is: b
index is: 2, str value is: c

为了按传统行号输出(行号从1开始),我们可以传递一个开始参数,代码(index_iter.py)示例如下:


for idx, str_val in enumerate(test_list, 1):
    print(f'index is: {idx}, str value is: {str_val}')

执行py文件,输出结果如下:


index is: 1, str value is: a
index is: 2, str value is: b
index is: 3, str value is: c

这种方式在错误消息中使用行号定位非常有用,示例如下:


def parse_data(file_name):
    with open(file_name, 'rt') as f:
        for lno, line in enumerate(f, 1):
            fields = line.split()
            try:
                count = int(fields[1])
            except ValueError as e:
                print(f'Line {lno}: Parse error: {e}')

enumerate()函数对于跟踪某些值在列表中出现的位置是很有用的。如果想将一个文件中出现的单词映射到它出现的行号上,可以利用enumerate()函数实现,代码如下:


from collections import defaultdict

word_summary = defaultdict(list)
with open('/etc/passwd', 'r') as f:
    line_list = f.readlines()

for idx, line in enumerate(line_list):
    # Create a list of words in current line
    words = [w.strip().lower() for w in line.split()]
    for word in words:
        word_summary[word].append(idx)

处理完文件后打印word_summary,我们会发现它是一个字典(准确来讲,word_summary是一个defaultdict)。每个单词有一个key,每个key对应的值是一个由该单词出现的行号组成的列表。如果某个单词在一行中出现两次,那么其行号也会出现两次,这可以作为文本的一个简单统计。

当额外定义一个计数变量的时候,使用enumerate()函数会更加简单。不使用enumerate()函数,可能会写出如下代码:


lno = 1
f = open('/etc/passwd')
for line in f:
    # Process line
    lno += 1

使用enumerate()函数就显得更加优雅了:


for lno, line in enumerate(f):
    # Process line
    pass

enumerate()函数返回的是一个enumerate对象实例,它是一个迭代器,返回连续的包含一个计数和一个值的元组。元组中的值通过在传入序列上调用next()函数返回。

注意 有时候,在一个已经解压的元组序列上使用enumerate()函数很容易掉入陷阱。

enumerate()函数正确与错误写法对比如下(index_iter.py):


data_list = [(1, 2), (3, 4), (5, 6), (7, 8)]

# Correct!
for n, (x, y) in enumerate(data_list):
    pass

# Error!
for n, x, y in enumerate(data_list):
    pass