GEEK
← 返回首页

什么是 enumerate() 和 zip()?它们是如何工作的?

#随笔

enumerate() 和 zip() 是 Python 中两个非常常用的内置函数,经常与 for 循环一起使用。enumerate() 用于遍历数据时同时获得索引和值,而 zip() 用于将多个可迭代对象按位置组合,并同时遍历它们。掌握这两个函数,可以让循环代码更加简洁、易读,不需要手动维护索引或多个循环。

为什么需要 enumerate()zip()

在学习 for 循环之后,我们已经可以很方便地遍历列表中的元素。

例如:

languages = ["Spanish", "English", "Russian", "Chinese"]

for language in languages:
    print(language)

输出:

Spanish
English
Russian
Chinese

这种写法可以直接获取列表中的元素。

但是,在实际编程时,我们经常会遇到另外两种情况:

  1. 遍历元素的同时,还需要知道当前元素的索引
  2. 需要同时遍历两个或多个可迭代对象。

Python 分别提供了两个非常方便的函数:

函数 作用
enumerate() 遍历时同时获取索引和值
zip() 按位置组合多个可迭代对象

下面分别来看它们是如何工作的。

enumerate():遍历时获取索引和值

enumerate() 是 Python 的内置函数。

它的主要作用是:

在遍历一个可迭代对象时,同时提供每个元素的索引和值。

基本语法:

enumerate(iterable, start=0)

参数说明:

参数 说明
iterable 要遍历的可迭代对象,例如列表、元组、字符串等
start 索引的起始值,默认为 0

不使用 enumerate() 获取索引

假设现在有一个语言列表:

languages = ["Spanish", "English", "Russian", "Chinese"]

如果我们希望同时打印每种语言和它的索引,可以手动创建一个 index 变量:

languages = ["Spanish", "English", "Russian", "Chinese"]

index = 0

for language in languages:
    print(f"Index {index} and language {language}")
    index += 1

输出:

Index 0 and language Spanish
Index 1 and language English
Index 2 and language Russian
Index 3 and language Chinese

这种写法当然没有问题,但需要自己完成两件事情:

  • 创建 index 变量。
  • 每次循环结束后手动执行 index += 1

如果只是为了获得列表索引,这种写法就显得比较繁琐。

使用 enumerate() 获取索引

使用 enumerate() 后,可以直接获得索引和值:

languages = ["Spanish", "English", "Russian", "Chinese"]

for index, language in enumerate(languages):
    print(f"Index {index} and language {language}")

输出:

Index 0 and language Spanish
Index 1 and language English
Index 2 and language Russian
Index 3 and language Chinese

可以看到,我们不再需要手动创建和更新 index

enumerate() 会自动帮我们记录当前元素的索引。

enumerate() 是怎么工作的?

来看下面这段代码:

languages = ["Spanish", "English", "Russian", "Chinese"]

result = enumerate(languages)

print(list(result))

输出:

[(0, "Spanish"), (1, "English"), (2, "Russian"), (3, "Chinese")]

可以把结果理解成一组“索引 + 元素”的组合:

组合 含义
(0, "Spanish") 索引 0 对应 "Spanish"
(1, "English") 索引 1 对应 "English"
(2, "Russian") 索引 2 对应 "Russian"
(3, "Chinese") 索引 3 对应 "Chinese"

这里需要注意:

enumerate() 返回的是一个 enumerate 对象,而不是列表。

我们使用:

list(enumerate(languages))

只是为了把它转换成列表,从而方便查看里面的数据。

为什么 for 中可以写 index, language

可能有初学者会疑惑:

for index, language in enumerate(languages):

为什么一次循环可以得到两个变量?

原因是 enumerate() 产生的每一项实际上都是一个包含两个元素的元组。

例如第一次循环得到:

(0, "Spanish")

然后 for 会自动进行元组解包(Tuple Unpacking)

(0, "Spanish")
      ↓
    解包
      ↓
index = 0
language = "Spanish"

第二次循环:

(1, "English")
      ↓
    解包
      ↓
index = 1
language = "English"

所以:

for index, language in enumerate(languages):

可以理解为:

每次从 enumerate() 中取出一个 (索引, 元素) 元组,然后分别赋值给 indexlanguage

enumerate(start=...):修改索引起点

默认情况下,enumerate()0 开始计数。

例如:

languages = ["Spanish", "English", "Russian", "Chinese"]

for index, language in enumerate(languages):
    print(index, language)

输出:

0 Spanish
1 English
2 Russian
3 Chinese

如果希望从 1 开始编号,可以设置 start 参数:

languages = ["Spanish", "English", "Russian", "Chinese"]

for index, language in enumerate(languages, start=1):
    print(index, language)

输出:

1 Spanish
2 English
3 Russian
4 Chinese

也可以写成:

enumerate(languages, 1)

这里的 1 就是 start 参数。

start 参数是如何工作的?

例如:

letters = ["A", "B", "C"]

print(list(enumerate(letters, start=5)))

输出:

[(5, "A"), (6, "B"), (7, "C")]

可以看到:

索引 元素
5 "A"
6 "B"
7 "C"

start 只决定第一个索引是多少,之后的索引仍然会依次递增。

enumerate() 不只可以遍历列表

只要对象是可迭代对象(Iterable),通常都可以使用 enumerate()

例如,可以遍历字符串:

for index, char in enumerate("code"):
    print(index, char)

输出:

0 c
1 o
2 d
3 e

这里:

  • index 表示字符的位置。
  • char 表示当前字符。

也可以遍历元组:

numbers = (10, 20, 30)

for index, number in enumerate(numbers):
    print(index, number)

输出:

0 10
1 20
2 30

因此,enumerate() 并不是只能用于列表。


zip():同时遍历多个对象

前面介绍的 enumerate() 主要解决的是:

遍历一个对象时,如何同时获得索引和值?

那么,如果我们需要同时遍历多个可迭代对象呢?

这时候可以使用 zip()

zip() 是 Python 的内置函数,它可以:

按照位置,将多个可迭代对象中的元素组合起来。

基本语法:

zip(iterable1, iterable2, ...)

例如:

developers = ["Naomi", "Dario", "Jessica", "Tom"]
ids = [1, 2, 3, 4]

print(list(zip(developers, ids)))

输出:

[("Naomi", 1), ("Dario", 2), ("Jessica", 3), ("Tom", 4)]

zip() 是怎么工作的?

假设有两个列表:

developers

索引:  0       1         2         3
        ↓       ↓         ↓         ↓
       Naomi   Dario    Jessica     Tom


ids

索引:  0    1    2    3
        ↓    ↓    ↓    ↓
        1    2    3    4

zip() 会按照相同的索引位置进行配对:

位置 0
   ↓
("Naomi", 1)

位置 1
   ↓
("Dario", 2)

位置 2
   ↓
("Jessica", 3)

位置 3
   ↓
("Tom", 4)

最终得到:

[
    ("Naomi", 1),
    ("Dario", 2),
    ("Jessica", 3),
    ("Tom", 4)
]

所以,zip() 可以简单理解成:

把多个对象中相同位置的元素“拉”到一起。

这也是 zip(拉链)这个名字非常形象的地方。

使用 zip() 配合 for 循环

zip() 最常见的使用方式,就是直接配合 for 循环:

developers = ["Naomi", "Dario", "Jessica", "Tom"]
ids = [1, 2, 3, 4]

for name, dev_id in zip(developers, ids):
    print(f"Name: {name}")
    print(f"ID: {dev_id}")

输出:

Name: Naomi
ID: 1
Name: Dario
ID: 2
Name: Jessica
ID: 3
Name: Tom
ID: 4

这里同样发生了元组解包

第一次循环时,zip() 产生:

("Naomi", 1)

然后自动解包为:

name = "Naomi"
dev_id = 1

第二次循环:

("Dario", 2)

自动变成:

name = "Dario"
dev_id = 2

所以:

for name, dev_id in zip(developers, ids):

可以理解为:

每次从 zip() 中取出一个 (姓名, ID) 元组,然后分别赋值给 namedev_id

zip() 返回的是什么?

enumerate() 一样,zip() 默认也不会直接返回列表。

例如:

developers = ["Naomi", "Dario"]
ids = [1, 2]

pairs = zip(developers, ids)

print(type(pairs))

输出类似:

<class 'zip'>

说明 pairs 是一个 zip 对象。

如果想查看其中的数据,可以转换成列表:

print(list(zip(developers, ids)))

输出:

[("Naomi", 1), ("Dario", 2)]

这里的 list() 主要是为了方便查看结果。

在实际循环中,通常不需要先转换成列表:

for name, dev_id in zip(developers, ids):
    print(name, dev_id)

zip() 可以组合多个对象

zip() 并不限制只能组合两个对象。

例如,可以同时组合姓名、年龄和职业:

names = ["Alice", "Bob", "Charlie"]
ages = [20, 22, 25]
jobs = ["Python Developer", "Web Developer", "Data Analyst"]

for name, age, job in zip(names, ages, jobs):
    print(f"{name} is {age} years old and works as a {job}.")

输出:

Alice is 20 years old and works as a Python Developer.
Bob is 22 years old and works as a Web Developer.
Charlie is 25 years old and works as a Data Analyst.

实际上,zip() 会产生:

("Alice", 20, "Python Developer")
("Bob", 22, "Web Developer")
("Charlie", 25, "Data Analyst")

然后 for 循环再把每个元组解包成三个变量。

zip() 遇到长度不同的对象怎么办?

这是使用 zip() 时非常重要的一点。

例如:

names = ["Alice", "Bob", "Charlie"]
ages = [20, 22]

print(list(zip(names, ages)))

输出:

[("Alice", 20), ("Bob", 22)]

为什么 "Charlie" 没有出现在结果中?

因为两个列表的长度不同:

names:3 个元素
ages: 2 个元素

zip() 默认会在最短的可迭代对象耗尽时停止

可以理解为:

Alice   ↔ 20      ✅
Bob     ↔ 22      ✅
Charlie ↔ 没有数据 ❌

因此,"Charlie" 会被忽略。

这意味着:

使用 zip() 时,如果多个数据源长度不同,默认只会处理能够一一对应的部分。


enumerate()zip() 可以一起使用

有时候,我们既需要:

  • 当前数据的索引;
  • 又需要同时遍历多个对象。

这时可以把 enumerate()zip() 组合起来。

例如:

names = ["Alice", "Bob", "Charlie"]
ages = [20, 22, 25]

for index, (name, age) in enumerate(zip(names, ages), start=1):
    print(f"{index}. {name} - {age} years old")

输出:

1. Alice - 20 years old
2. Bob - 22 years old
3. Charlie - 25 years old

这个写法可以分成两步理解。

第一步,zip() 负责把姓名和年龄配对:

("Alice", 20)
("Bob", 22)
("Charlie", 25)

第二步,enumerate() 再为这些数据添加索引:

1 → ("Alice", 20)
2 → ("Bob", 22)
3 → ("Charlie", 25)

最后通过元组解包:

for index, (name, age) in enumerate(zip(names, ages), start=1):

得到三个变量:

index
name
age

因此,这种组合方式可以理解成:

zip() 负责“配对数据”,enumerate() 负责“添加索引”。

enumerate()zip() 的区别

虽然这两个函数都经常与 for 循环一起使用,但解决的问题并不相同。

函数 主要作用 结果特点
enumerate() 获取索引和值 (索引, 元素)
zip() 同时遍历多个对象 (元素1, 元素2, ...)

例如:

languages = ["Python", "Java", "Rust"]

for index, language in enumerate(languages):
    print(index, language)

这里关注的是:

索引 + 元素

而:

languages = ["Python", "Java", "Rust"]
versions = [3, 21, 1]

for language, version in zip(languages, versions):
    print(language, version)

这里关注的是:

元素 + 元素

可以用一句话区分:

enumerate() 解决“我想知道它排第几个”的问题。

zip() 解决“我想同时处理几组对应数据”的问题。

enumerate()range() 有什么区别?

初学者还容易把 enumerate()range() 混淆。

假设要遍历一个列表,并同时获取索引和值。

可以使用 range()

languages = ["Python", "Java", "Rust"]

for index in range(len(languages)):
    print(index, languages[index])

也可以使用 enumerate()

languages = ["Python", "Java", "Rust"]

for index, language in enumerate(languages):
    print(index, language)

两种方式都能实现相同的效果,但 enumerate() 更直接。

对比:

range(len(...)) enumerate()
先生成索引 直接获得索引和值
需要通过索引访问元素 直接获得元素
代码相对繁琐 代码更加简洁
容易出现索引相关问题 更符合 Python 的常见写法

因此,一般来说:

如果只是为了在遍历时同时获取索引和值,优先使用 enumerate()

enumerate()zip() 的常见使用场景

可以简单记住下面这张表:

需求 推荐使用
只遍历一个对象 for
遍历时需要索引和值 enumerate()
同时遍历两个对象 zip()
同时遍历多个对象 zip()
同时遍历多个对象,并且需要索引 enumerate(zip(...))

例如:

遍历列表,并显示编号
        ↓
   enumerate()

遍历姓名和成绩
        ↓
      zip()

遍历姓名、成绩,并显示编号
        ↓
enumerate(zip(...))

常见错误

❌ 为了获取索引而手动维护变量

这种写法虽然可以使用:

index = 0

for language in languages:
    print(index, language)
    index += 1

但如果只是为了获取索引,更推荐:

for index, language in enumerate(languages):
    print(index, language)

这样不需要自己管理 index

❌ 误以为 enumerate() 返回列表

例如:

result = enumerate(["A", "B", "C"])

print(type(result))

得到的是:

<class 'enumerate'>

如果只是想查看里面的数据,可以:

print(list(enumerate(["A", "B", "C"])))

输出:

[(0, "A"), (1, "B"), (2, "C")]

所以要区分:

enumerate(...)
      ↓
enumerate 对象

list(enumerate(...))
      ↓
列表

❌ 误以为 zip() 会自动保留较长对象的数据

例如:

names = ["Alice", "Bob", "Charlie"]
ages = [20, 22]

for name, age in zip(names, ages):
    print(name, age)

输出:

Alice 20
Bob 22

Charlie 不会被处理,因为 ages 已经没有第三个元素。

因此使用 zip() 时,要注意多个数据源之间的长度是否符合预期。

enumerate()zip() 知识速查表

写法 作用 返回对象
enumerate(iterable) 获取索引和值 enumerate 对象
enumerate(iterable, start) 指定索引起点 enumerate 对象
zip(a, b) 按位置组合两个对象 zip 对象
zip(a, b, c) 按位置组合多个对象 zip 对象
list(enumerate(...)) 查看枚举结果 列表
list(zip(...)) 查看组合结果 列表

学习小结

enumerate()

enumerate() 适合在遍历时同时获取索引和值

languages = ["Python", "Java", "Rust"]

for index, language in enumerate(languages):
    print(index, language)

需要记住:

  • 默认从 0 开始计数。
  • 可以通过 start 修改起始值。
  • 每次产生的数据可以理解为 (索引, 元素)
  • 返回的是 enumerate 对象,而不是列表。
  • 经常与 for 循环搭配使用。

zip()

zip() 适合同时遍历多个可迭代对象:

names = ["Alice", "Bob"]
ages = [20, 22]

for name, age in zip(names, ages):
    print(name, age)

需要记住:

  • 按位置将多个对象中的元素组合起来。
  • 每次产生的数据可以理解为一个元组。
  • 可以同时组合两个或多个对象。
  • 返回的是 zip 对象,而不是列表。
  • 默认在最短的可迭代对象耗尽时停止。

总结

  • enumerate() 用于在遍历可迭代对象时,同时获得索引和值
  • enumerate() 默认从 0 开始计数,可以通过 start 修改起始值。
  • enumerate() 返回 enumerate 对象,每一项可以理解为 (索引, 元素)
  • zip() 用于将多个可迭代对象按照位置进行组合。
  • zip() 返回 zip 对象,每一项可以理解为由多个对象对应元素组成的元组。
  • zip() 可以同时组合两个或多个可迭代对象。
  • 当对象长度不一致时,zip() 默认以最短对象为准停止。
  • enumerate()zip() 都非常适合与 for 循环配合使用。
  • 两者还可以组合起来,实现“索引 + 多组对应数据”的遍历。

一句话记忆:enumerate() = 索引 + 元素zip() = 多个对象按位置配对