python

关注公众号 jb51net

关闭
首页 > 脚本专栏 > python > Python enumerate带索引遍历

Python中enumerate()函数带索引遍历的优雅写法

作者:星河耀银海

本文将带你全面掌握Python内置函数enumerate(),从基本语法到高级技巧,包括显示带编号列表、定位元素、与字典配合等场景,对比enumerate和range(len()),让你的代码更优雅高效

一、开篇:告别range(len()),拥抱enumerate()

每个Python程序员都经历过这个进化:

# 阶段1:C语言习惯
fruits = ["苹果", "香蕉", "橘子"]
i = 0
while i < len(fruits):
    print(f"{i}: {fruits[i]}")
    i += 1

# 阶段2:学会了range(len())
for i in range(len(fruits)):
    print(f"{i}: {fruits[i]}")

# 阶段3:遇见enumerate()——Pythonic的觉醒
for i, fruit in enumerate(fruits):
    print(f"{i}: {fruit}")

enumerate()是Python内置函数中最被低估的一个。它做的事情很简单——给可迭代对象的每个元素配上索引——但用好了,代码的优雅程度和可读性都会上一个台阶。

本文从基本用法到高级技巧,帮你成为enumerate()的使用高手。

二、enumerate()基本用法

2.1 语法和返回值

# enumerate(iterable, start=0)
# 返回一个enumerate对象,每次迭代产生 (index, value) 元组

fruits = ["苹果", "香蕉", "橘子", "葡萄"]

# 基本用法:默认从0开始
for index, fruit in enumerate(fruits):
    print(f"{index}: {fruit}")
# 输出:
# 0: 苹果
# 1: 香蕉
# 2: 橘子
# 3: 葡萄

# 指定起始索引
for index, fruit in enumerate(fruits, start=1):
    print(f"第{index}个: {fruit}")
# 输出:
# 第1个: 苹果
# 第2个: 香蕉
# 第3个: 橘子
# 第4个: 葡萄

# enumerate对象可以转为列表查看
print(list(enumerate(fruits)))
# [(0, '苹果'), (1, '香蕉'), (2, '橘子'), (3, '葡萄')]

print(list(enumerate(fruits, start=1)))
# [(1, '苹果'), (2, '香蕉'), (3, '橘子'), (4, '葡萄')]

2.2 enumerate()的本质

# enumerate() 是一个迭代器
# 它不会一次性创建所有(index, value)对
# 而是按需生成,内存效率高

# 手动实现一个简易的enumerate
def my_enumerate(iterable, start=0):
    index = start
    for item in iterable:
        yield index, item
        index += 1

# 测试
for i, char in my_enumerate("Python", start=1):
    print(f"{i}: {char}")

# enumerate对象是惰性的
enum = enumerate(range(10000000))  # 不会创建1000万元组!
print(next(enum))  # (0, 0)
print(next(enum))  # (1, 1)

三、enumerate()的常见应用场景

3.1 场景一:显示带编号的列表

def display_menu(options):
    """显示带编号的菜单"""
    print("请选择:")
    for i, option in enumerate(options, start=1):
        print(f"  {i}. {option}")

options = ["新建文件", "打开文件", "保存文件", "另存为", "退出"]
display_menu(options)

# 输出:
# 请选择:
#   1. 新建文件
#   2. 打开文件
#   3. 保存文件
#   4. 另存为
#   5. 退出

3.2 场景二:找到匹配元素的位置

def find_all(items, predicate):
    """找到所有满足条件的元素的索引"""
    return [i for i, item in enumerate(items) if predicate(item)]

scores = [85, 60, 92, 45, 78, 30, 95, 55]
# 找到所有不及格分数的位置
failing = find_all(scores, lambda x: x < 60)
print(f"不及格分数位置: {failing}")
print(f"不及格分数值: {[scores[i] for i in failing]}")

# 找到第一个满足条件的
def find_first(items, predicate):
    for i, item in enumerate(items):
        if predicate(item):
            return i, item
    return -1, None

idx, score = find_first(scores, lambda x: x >= 90)
print(f"第一个90分以上: 位置{idx}, 分数{score}")

3.3 场景三:同时遍历多个列表

names = ["张三", "李四", "王五", "赵六"]
ages = [25, 30, 28, 22]
cities = ["北京", "上海", "广州", "深圳"]

# enumerate + zip 同时获取索引和多个序列的值
for i, (name, age, city) in enumerate(zip(names, ages, cities), start=1):
    print(f"{i}. {name}, {age}岁, 来自{city}")

# 或者构建结构化数据
users = [
    {"id": i, "name": name, "age": age, "city": city}
    for i, (name, age, city) in enumerate(zip(names, ages, cities), start=1001)
]
for user in users:
    print(user)

3.4 场景四:与字典配合

# 从列表构建字典
fruits = ["苹果", "香蕉", "橘子", "葡萄"]
fruit_dict = {i: fruit for i, fruit in enumerate(fruits, start=1)}
print(fruit_dict)  # {1: '苹果', 2: '香蕉', 3: '橘子', 4: '葡萄'}

# 反转:值→索引
fruit_to_index = {fruit: i for i, fruit in enumerate(fruits)}
print(fruit_to_index)  # {'苹果': 0, '香蕉': 1, '橘子': 2, '葡萄': 3}

# 给字典的遍历加上序号
user = {"name": "张三", "age": 25, "city": "北京", "email": "zs@test.com"}
for i, (key, value) in enumerate(user.items(), start=1):
    print(f"{i}. {key}: {value}")

3.5 场景五:文件行号

# 读取文件并标注行号
def read_file_with_line_numbers(filename):
    """读取文件,每行带行号"""
    try:
        with open(filename, 'r', encoding='utf-8') as f:
            for line_no, line in enumerate(f, start=1):
                yield line_no, line.rstrip('\n')
    except FileNotFoundError:
        print(f"文件不存在: {filename}")

# 使用
# for line_no, content in read_file_with_line_numbers("example.py"):
#     print(f"{line_no:4d} | {content}")

# 查找特定行
def find_lines_containing(filename, keyword):
    """在文件中搜索包含关键词的行"""
    results = []
    for line_no, line in read_file_with_line_numbers(filename):
        if keyword in line:
            results.append((line_no, line))
    return results

四、enumerate()的高级技巧

4.1 enumerate与列表推导式

# 创建带索引的数据结构
words = ["hello", "world", "python", "code"]
indexed_words = [(i, word.upper()) for i, word in enumerate(words)]
print(indexed_words)

# 条件筛选同时保留原索引
data = [3, 7, 0, 2, 8, 1, 4, 9, 5, 6]
# 找出所有值大于5的元素及其原位置
large_items = [(i, v) for i, v in enumerate(data) if v > 5]
print(large_items)  # [(1, 7), (4, 8), (7, 9)]

# enumerate中嵌套解包
students = [("张三", 85), ("李四", 92), ("王五", 78)]
for rank, (name, score) in enumerate(students, start=1):
    print(f"第{rank}名: {name} ({score}分)")

4.2 enumerate的对象可以多次迭代吗?

# enumerate是一个迭代器,只能遍历一次
enum = enumerate(["a", "b", "c"])

print("第一次遍历:")
for item in enum:
    print(f"  {item}")

print("第二次遍历:")
for item in enum:
    print(f"  {item}")    # 什么都不输出!迭代器已耗尽

# ✅ 如果需要多次遍历,可以转为列表
enum_list = list(enumerate(["a", "b", "c"]))
# 现在可以多次遍历
for item in enum_list:
    print(item)

4.3 enumerate与字典的妙用

# 从两个列表创建编号字典
names = ["张三", "李四", "王五"]
# 创建 {编号: 姓名} 的映射
id_to_name = dict(enumerate(names, start=1001))
print(id_to_name)  # {1001: '张三', 1002: '李四', 1003: '王五'}

# 创建 {姓名: 编号} 的映射
name_to_id = {name: i for i, name in enumerate(names, start=1001)}
print(name_to_id)  # {'张三': 1001, '李四': 1002, '王五': 1003}

4.4 在数据科学中的使用

# enumerate在数据预处理中很实用
# 例如:给数据集添加行号
raw_data = [
    ["张三", 25, "北京"],
    ["李四", 30, "上海"],
    ["王五", None, "广州"],   # 缺失数据
    ["赵六", 22, "深圳"],
]

# 添加行号列,并标记异常行
cleaned = []
for idx, row in enumerate(raw_data, start=1):
    name, age, city = row
    status = "正常" if age is not None else "缺失年龄"
    cleaned.append([idx, name, age, city, status])

for row in cleaned:
    print(row)

# 找出所有问题行的序号
problem_rows = [i for i, (_, age, _) in enumerate(raw_data) if age is None]
print(f"问题行: {problem_rows}")

五、enumerate vs range(len()) 对比

5.1 为什么enumerate更好

items = ["a", "b", "c", "d", "e"]

# ❌ range(len()) —— 老式写法
for i in range(len(items)):
    print(f"{i}: {items[i]}")

# ✅ enumerate() —— Pythonic写法
for i, item in enumerate(items):
    print(f"{i}: {item}")

# enumerate 的优点:
# 1. 不需要用索引取元素(items[i]),更简洁
# 2. 避免索引越界错误
# 3. 对不支持索引的可迭代对象也能工作(如生成器、集合)
# 4. 代码意图更明确——"我要遍历并编号"

# enumerate 可以处理任何可迭代对象
def number_generator():
    yield "x"
    yield "y"
    yield "z"

# range(len()) 不适用于生成器!
# for i in range(len(number_generator())):  # TypeError!生成器没有len()

# enumerate() 完美适用
for i, val in enumerate(number_generator()):
    print(f"{i}: {val}")

5.2 何时仍用range(len())

# 存几种场景range(len())更合适:

# 1. 修改原列表元素
numbers = [1, 2, 3, 4, 5]
for i in range(len(numbers)):
    numbers[i] *= 10  # 直接修改原列表
# 如果用enumerate,修改的是迭代变量,不影响原列表

# 2. 访问相邻元素
data = [3, 1, 4, 1, 5, 9]
for i in range(len(data) - 1):
    diff = data[i+1] - data[i]
    print(f"data[{i}] → data[{i+1}]: {diff:+d}")

# 3. 跳跃遍历
for i in range(0, len(data), 2):
    print(data[i])

# 但即便如此,有时enumerate配合条件也能做到
# 只是range(len())在这些场景下更直观

六、实战案例

6.1 排行榜生成器

class Leaderboard:
    """排行榜 —— enumerate()的自然使用场景"""
    
    def __init__(self):
        self.entries = []
    
    def add_score(self, name, score):
        self.entries.append({"name": name, "score": score})
        # 保持降序排列
        self.entries.sort(key=lambda x: x["score"], reverse=True)
    
    def display(self, top_n=10):
        """显示排行榜"""
        print("=" * 40)
        print("🏆  排行榜 TOP", min(top_n, len(self.entries)))
        print("=" * 40)
        
        medals = {1: "🥇", 2: "🥈", 3: "🥉"}
        
        for rank, entry in enumerate(self.entries[:top_n], start=1):
            medal = medals.get(rank, f"{rank:2d}.")
            name = entry["name"]
            score = entry["score"]
            bar = "█" * min(score // 10, 30)
            print(f"{medal} {name:<10} {score:>5}分 {bar}")
    
    def get_rank(self, name):
        """获取指定用户的排名"""
        for rank, entry in enumerate(self.entries, start=1):
            if entry["name"] == name:
                return rank
        return None

# 使用
lb = Leaderboard()
lb.add_score("张三", 950)
lb.add_score("李四", 820)
lb.add_score("王五", 780)
lb.add_score("赵六", 910)
lb.add_score("钱七", 880)
lb.add_score("孙八", 650)

lb.display(5)
print(f"\n李四的排名: 第{lb.get_rank('李四')}名")

6.2 差异对比工具

def compare_sequences(seq1, seq2):
    """对比两个序列,找出不同之处"""
    differences = []
    
    # 对比相同位置
    min_len = min(len(seq1), len(seq2))
    for i, (a, b) in enumerate(zip(seq1[:min_len], seq2[:min_len])):
        if a != b:
            differences.append({
                "position": i,
                "type": "mismatch",
                "value1": a,
                "value2": b,
            })
    
    # 处理长度差异
    if len(seq1) > len(seq2):
        for i, val in enumerate(seq1[min_len:], start=min_len):
            differences.append({
                "position": i,
                "type": "extra_in_first",
                "value1": val,
                "value2": None,
            })
    elif len(seq2) > len(seq1):
        for i, val in enumerate(seq2[min_len:], start=min_len):
            differences.append({
                "position": i,
                "type": "extra_in_second",
                "value1": None,
                "value2": val,
            })
    
    return differences

# 测试
list1 = [1, 2, 3, 4, 5, 6, 7]
list2 = [1, 2, 0, 4, 0, 6]

diffs = compare_sequences(list1, list2)
for diff in diffs:
    pos, typ = diff["position"], diff["type"]
    if typ == "mismatch":
        print(f"位置{pos}: {diff['value1']} ≠ {diff['value2']}")
    elif typ == "extra_in_first":
        print(f"位置{pos}: {diff['value1']} (仅在第一个序列中)")
    elif typ == "extra_in_second":
        print(f"位置{pos}: {diff['value2']} (仅在第二个序列中)")

七、本章小结

本文我们全面掌握了enumerate()函数:

一个简单的原则:当你发现自己写了range(len(something))时,停下来想一想——是不是应该用enumerate()?99%的情况下答案是"是"。⌨️ 下一篇文章,我们将学习zip()函数——并行遍历多个序列的利器。

以上就是Python中enumerate()函数带索引遍历的优雅写法的详细内容,更多关于Python enumerate带索引遍历的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文