问题背景: enumerate() 函数在 Python 2.3 中出现,至今是并行访问元素及其索引的标准方法。在 enumerate() 出现之前,程序员常常自己创建计数器或使用函数 range(len(sequence)),这既不方便又难读。
问题: 普通的 for 循环只遍历值。通常使用 range(len(...)) 访问索引,但它不适用于所有可迭代对象(例如,生成器、字符串和可变长度的元组,或在过滤时)。这会导致错误并使代码变得复杂。
解决方案: enumerate() 返回一对 (索引, 元素),这使得即使对于非标准集合或过滤生成器也能获得当前元素的索引。该函数接受一个可选的第二个参数——计数器的起始值。
示例代码:
words = ['apple', 'banana', 'cherry'] for idx, word in enumerate(words, 1): print(f"{idx}: {word}") # 输出: # 1: apple # 2: banana # 3: cherry
关键特点:
为什么在函数中常使用 enumerate 而不是 range(len(seq))?
回答:range(len(seq)) 仅适用于可以通过索引访问的序列,并且不考虑迭代过程中长度的变化。此外,它的可读性较差,且对生成器的性能更差,可能无法正常工作。enumerate() 提供了对任何可迭代集合的索引-值对的安全访问。
示例代码:
# 不适用于生成器: gen = (x for x in range(5)) for i in range(len(gen)): print(i) # 错误:生成器没有长度
是否可以在遍历过程中使用 enumerate 来修改列表中的元素?
回答:可以,但需要按索引迭代,以便写入值。否则,如果仅按值迭代,您将修改对象的副本,而不是原始对象。
示例代码:
nums = [1, 2, 3] for idx, val in enumerate(nums): nums[idx] = val * 2 # nums = [2, 4, 6]
如果传递给 enumerate 的对象在迭代过程中发生变化,结果会是什么?
回答:如果集合在遍历过程中发生变化(例如,删除元素),行为可能会出乎意料,因为 enumerate 根据内部迭代器进行迭代,该迭代器可能会失效。因此,不建议在迭代时修改集合。
负面案例
程序员使用 range(len(list)) 遍历列表,并在过程中删除元素。结果——索引混乱,部分元素被跳过。
优点:
缺点:
正面案例
使用 enumerate(),并且生成新列表或通过索引更改值,但列表的大小在循环中不会变化。
优点:
缺点: