Leodanis Pozo Ramos 2025-02-03
Python 的 for 循环允许你遍历集合中的元素,例如列表、元组、字符串和字典。for 循环的语法会声明一个循环变量,在每次迭代中从集合中取出一个元素。这种循环非常适合对集合中的每个元素重复执行一段代码。你还可以通过 break、continue 和 else 等特性进一步定制 for 循环。
学完本教程后,你将理解以下内容:
- Python 的
for循环用于遍历数据集合中的元素,从而对每个元素执行代码。 - 要从 0 迭代到 10,应使用
for index in range(11):结构。 - 如果只是想重复执行某段代码若干次而不处理可迭代对象中的数据,可以使用
for _ in range(times):。 - 若要进行基于索引的迭代,可以使用
for index, value in enumerate(iterable):同时访问索引和元素。
在本教程中,你将掌握如何使用 for 循环遍历各种集合,并学习 Pythonic 的循环技巧。此外,你还将了解如何处理异常,以及如何使用异步迭代来使你的 Python 代码更加健壮高效。
初识 Python 的 for 循环
在编程中,循环是一种控制流语句,用于重复执行一组操作若干次。实践中,主要有两种类型的循环:
for循环通常用于已知次数的迭代,常见于处理具有特定数量元素的数据集合。while循环通常用于未知次数的迭代,适用于迭代次数取决于某个条件的情况。
Python 同时支持这两种循环,而本教程将重点介绍 for 循环。在 Python 中,当你需要遍历数据集合中的元素时,通常会使用 for 循环。这种循环可以让你遍历不同的数据集合,并对集合中的每个元素运行特定的语句块。
在 Python 中,for 循环是一种复合语句,包含一个头部和一个会执行预定次数的代码块。其基本语法如下:
for variable in iterable:
<body>
在此语法中:
variable是循环变量。在每次迭代中,它会取得iterable中当前项的值。iterable表示你要遍历的数据集合。<body>是每次迭代要执行的一个或多个语句(必须正确缩进)。
更详细地分解该语法:
for是启动循环头部的关键字。variable是保存当前项的变量。in是连接循环变量与可迭代对象的关键字。iterable是一个可被遍历的数据集合。<body>是每次迭代要执行的一条或多条语句。
下面是一个快速示例,展示如何使用 for 循环遍历一个列表:
>>> colors = ["red", "green", "blue", "yellow"]
>>> for color in colors:
... print(color)
...
red
green
blue
yellow
在这个例子中,color 是循环变量,colors 列表是目标集合。每次循环时,color 依次取得 colors 中的下一个元素。循环体是一次 print() 调用,将值打印到屏幕上。这个循环会对集合中的每个元素运行一次。上述代码的写法就是 Pythonic 的写法。
那么,到底什么是 可迭代对象(iterable) 呢?在 Python 中,可迭代对象是指可以被遍历的对象——通常是数据集合。常见的可迭代对象包括列表、元组、字符串、字典和集合等内置数据类型。你也可以自定义支持迭代的类。
注意:Python 中有 可迭代对象(iterables) 和 迭代器(iterators) 之分。
- 可迭代对象支持
.__iter__()特殊方法(即可迭代协议)。- 迭代器则同时支持
.__iter__()和.__next__()方法(即迭代器协议)。
所有迭代器都是可迭代对象,但并非所有可迭代对象都是迭代器。for循环内部实际上是由迭代器驱动的。
关于可迭代对象和迭代器的深入讨论超出了本教程范围。
你也可以在循环中使用多个循环变量:
>>> points = [(1, 4), (3, 6), (7, 3)]
>>> for x, y in points:
... print(f"{x = } and {y = }")
...
x = 1 and y = 4
x = 3 and y = 6
x = 7 and y = 3
这里有两个循环变量 x 和 y。只要提供一个元组形式的循环变量,并确保可迭代对象中的每个元素能正确解包,就可以使用任意数量的循环变量。这种模式在遍历字典项或进行并行迭代时非常有用。
有时,输入的可迭代对象可能是空的。此时,循环头部会执行一次,但循环体不会运行:
>>> for item in []:
... print(item)
...
在这个例子中,目标可迭代对象是一个空列表。循环会检查是否有元素;如果没有,循环体不会执行,程序流程直接跳到循环之后的语句。
现在你已经掌握了 for 循环的基本语法,接下来我们将深入一些实际示例。在下一节中,你将学习如何使用 for 循环遍历 Python 中最常见的内置数据集合。
遍历 Python 内置集合
编写 Python 代码时,你经常需要遍历列表、元组、字符串、数值范围、字典和集合等内置数据类型。它们都支持迭代,可以直接用于 for 循环。接下来几节将教你如何以 Pythonic 的方式处理这些集合。
序列:列表、元组、字符串和范围(range)
遍历序列类型(如列表、元组、字符串和 range)时,迭代顺序与元素在序列中的定义顺序一致。例如,遍历一个数字列表:
>>> numbers = [1, 2, 3, 4]
>>> for number in numbers:
... print(number)
...
1
2
3
4
迭代按列表定义顺序进行,从 1 开始,到 4 结束。注意,在 Python 中遍历序列时,不需要像其他语言那样依赖索引。
通常,我们用复数名词命名列表,这样循环变量就可以使用单数名词,使代码更具描述性和可读性。
其他内置序列的行为类似:
>>> person = ("Jane", 25, "Python Dev", "Canada")
>>> for field in person:
... print(field)
...
Jane
25
Python Dev
Canada
>>> text = "abcde"
>>> for character in text:
... print(character)
...
a
b
c
d
e
>>> for index in range(5):
... print(index)
...
0
1
2
3
4
这些例子分别遍历了元组、字符串和数值范围。同样,迭代顺序与定义顺序一致。
元组常用于表示一行数据(如上例中的 person)。你可以用清晰的循环遍历每个字段。
对于字符串,for 循环允许你逐字符处理。
而遍历数值范围(range)在需要控制迭代次数或使用连续索引时非常有用。
集合:字典和集合(set)
遍历字典时,你可以选择遍历其键、值或键值对。
有两种方式遍历字典的键:
- 直接使用字典
- 使用
.keys()方法
示例如下:
>>> students = {
... "Alice": 89.5,
... "Bob": 76.0,
... "Charlie": 92.3,
... "Diana": 84.7,
... "Ethan": 88.9,
... }
>>> for student in students:
... print(student)
...
Alice
Bob
Charlie
Diana
Ethan
>>> for student in students.keys():
... print(student)
...
Alice
Bob
Charlie
Diana
Ethan
两种方式等价,但第一种更常用,第二种更显式、可读性更强。
在遍历时,你可以通过键访问对应的值:
>>> for student in students:
... print(student, "->", students[student])
...
Alice -> 89.5
Bob -> 76.0
Charlie -> 92.3
Diana -> 84.7
Ethan -> 88.9
若只需遍历值,可使用 .values() 方法:
>>> teams = {
... "Colorado": "Rockies",
... "Chicago": "White Sox",
... "Boston": "Red Sox",
... "Minnesota": "Twins",
... "Milwaukee": "Brewers",
... "Seattle": "Mariners",
... }
>>> for team in teams.values():
... print(team)
...
Rockies
White Sox
Red Sox
Twins
Brewers
Mariners
注意:使用 .values() 时无法访问键。
最常见且推荐的方式是同时遍历键和值,使用 .items() 方法:
>>> for place, team in teams.items():
... print(place, "->", team)
...
Colorado -> Rockies
Chicago -> White Sox
Boston -> Red Sox
Minnesota -> Twins
Milwaukee -> Brewers
Seattle -> Mariners
这里使用了元组形式的循环变量:第一个变量接收键,第二个接收值。变量名 place 和 team 使代码清晰易读。
至于集合(set),需注意它是无序的,因此遍历顺序不保证:
>>> tools = {"Django", "Flask", "pandas", "NumPy"}
>>> for tool in tools:
... print(tool)
...
NumPy
Flask
pandas
Django
可以看到,输出顺序与插入顺序不同。因此,不要依赖集合的遍历顺序。
高级 for 循环语法
Python 的 for 循环具有一些高级特性,使其更加灵活强大。这些特性包括 break、continue、else 子句,以及嵌套循环。
break 语句
break 会立即退出循环,跳转到循环后的第一条语句。例如,查找列表中是否存在某个数字,找到后立即停止:
>>> numbers = [1, 3, 5, 7, 9]
>>> target = 5
>>> for number in numbers:
... print(f"Processing {number}...")
... if number == target:
... print(f"Target found {target}!")
... break
...
Processing 1...
Processing 3...
Processing 5...
Target found 5!
break 在找到目标后立即终止循环,避免不必要的处理。
注意:
break应放在条件语句中。若直接放在循环体开头,循环只会运行一次。
continue 语句
continue 会跳过当前迭代,进入下一次循环。例如,只处理偶数:
>>> numbers = [1, 2, 3, 4, 5, 6]
>>> for number in numbers:
... print(f"{number = }")
... if number % 2 != 0:
... continue
... print(f"{number} is even!")
...
number = 1
number = 2
2 is even!
number = 3
number = 4
4 is even!
number = 5
number = 6
6 is even!
奇数被跳过,偶数才会执行后续代码。
同样,
continue也应放在条件中,否则其后的代码将永远无法执行。
else 子句
Python 的 for 循环可以带 else 子句。只有当循环因可迭代对象耗尽而正常结束时,else 才会执行。如果循环被 break 中断,则 else 不会运行。
例如,改进查找逻辑,明确告知用户未找到目标:
>>> numbers = [1, 3, 5, 7, 9]
>>> target = 42
>>> for number in numbers:
... print(f"Processing {number}...")
... if number == target:
... print(f"Target found {target}!")
... break
... else:
... print(f"Target not found {target}")
...
Processing 1...
Processing 3...
Processing 5...
Processing 7...
Processing 9...
Target not found 42
else 在未触发 break 时执行,非常适合“未找到”场景。
如果循环中没有
break,则else块的内容可以直接放在循环之后,无需缩进,代码更清晰。
嵌套 for 循环
for 循环可以嵌套。例如,生成乘法表:
>>> for number in range(1, 11):
... for product in range(number, number * 11, number):
... print(f"{product:>4d}", end="")
... print()
...
1 2 3 4 5 6 7 8 9 10
2 4 6 8 10 12 14 16 18 20
3 6 9 12 15 18 21 24 27 30
...
外层循环控制行,内层循环计算并打印每行的乘积。使用 end="" 避免换行,最后用 print() 换行。
Pythonic 的循环技巧
从其他语言转到 Python 的开发者常会写出“非 Pythonic”的循环。以下是一些更优雅、高效的 Pythonic 技巧。
带索引的迭代:Pythonic 方式
有时你需要同时获取元素及其索引。初学者可能这样写:
>>> fruits = ["orange", "apple", "mango", "lemon"]
>>> for index in range(len(fruits)):
... fruit = fruits[index]
... print(index, fruit)
...
0 orange
1 apple
2 mango
3 lemon
虽然可行,但不够优雅。更好的方式是使用内置函数 enumerate():
>>> for index, fruit in enumerate(fruits):
... print(index, fruit)
...
0 orange
1 apple
2 mango
3 lemon
enumerate() 返回 (index, item) 元组,代码读起来像自然语言,更加 Pythonic。
enumerate() 还支持 start 参数,用于自定义起始编号:
>>> def display_menu(options):
... print("Main Menu:")
... for position, option in enumerate(options, start=1):
... print(f"{position}. {option}")
...
>>> display_menu(["Open", "Save", "Settings", "Quit"])
Main Menu:
1. Open
2. Save
3. Settings
4. Quit
从用户角度看,菜单从 1 开始更自然。
并行遍历多个可迭代对象
使用 zip() 可以并行遍历多个可迭代对象:
>>> numbers = [1, 2, 3]
>>> letters = ["a", "b", "c"]
>>> for number, letter in zip(numbers, letters):
... print(number, "->", letter)
...
1 -> a
2 -> b
3 -> c
zip() 将多个可迭代对象打包成元组。当任一对象耗尽时,zip 停止。
顺序遍历多个可迭代对象
若要依次遍历多个可迭代对象(而非并行),可使用 itertools.chain():
>>> from itertools import chain
>>> first = [7, 6, 1]
>>> second = [4, 1]
>>> third = [8, 0, 6]
>>> for value in chain(first, second, third):
... print(value**2)
...
49
36
1
16
1
64
0
36
chain() 将多个可迭代对象连接成一个,避免嵌套循环。
对于二维列表(矩阵),可用解包操作符 *:
>>> matrix = [[9, 3, 8], [4, 5, 2], [6, 4, 3]]
>>> for value in chain(*matrix):
... print(value**2)
...
81
9
64
16
25
4
36
16
9
这相当于“展平”矩阵,简化了代码。
重复执行固定次数的操作
有时你只想重复执行某段代码 N 次,而不处理任何数据。这时可结合 range() 和下划线 _(表示丢弃变量):
>>> for _ in range(3):
... print("Knock, knock, knock")
... print("Penny!")
...
Knock, knock, knock
Penny!
Knock, knock, knock
Penny!
Knock, knock, knock
Penny!
_ 表示你不需要使用循环变量,这是一种约定俗成的写法。
反向或排序遍历
使用 reversed() 可反向遍历:
>>> actions = ["Type text", "Select text", "Cut text", "Paste text"]
>>> for action in reversed(actions):
... print(f"Undo: {action}")
...
Undo: Paste text
Undo: Cut text
Undo: Select text
Undo: Type text
使用 sorted() 可按排序顺序遍历。例如,按成绩从高到低打印学生名单:
>>> students = {
... "Alice": 89.5,
... "Bob": 76.0,
... "Charlie": 92.3,
... "Diana": 84.7,
... "Ethan": 88.9,
... "Fiona": 95.6,
... "George": 73.4,
... "Hannah": 81.2,
... }
>>> sorted_students = sorted(
... students.items(), key=lambda item: item[1], reverse=True
... )
>>> for name, grade in sorted_students:
... print(f"{name}'s average grade: {grade:->{20-len(name)}.1f}")
...
Fiona's average grade: -----------95.6
Charlie's average grade: ---------92.3
Alice's average grade: -----------89.5
Ethan's average grade: -----------88.9
Diana's average grade: -----------84.7
Hannah's average grade: ----------81.2
Bob's average grade: -------------76.0
George's average grade: ----------73.4
这里使用 lambda item: item[1] 按字典的值排序,并设置 reverse=True 实现降序。
for 循环的常见陷阱
使用 for 循环时,可能会遇到一些问题:
在迭代过程中修改集合
Python 的可变集合(如列表、字典)可以在原地修改,但在迭代过程中增删元素是危险的。
安全操作:仅修改现有元素(不改变长度):
>>> names = ["Alice", "Bob", "John", "Jane"]
>>> for index, name in enumerate(names):
... names[index] = name.upper()
...
>>> names
['ALICE', 'BOB', 'JOHN', 'JANE']
危险操作:在迭代中删除元素:
>>> numbers = [2, 4, 6, 8]
>>> for number in numbers:
... if number % 2 == 0:
... numbers.remove(number)
...
>>> numbers
[4, 8] # 错误!部分元素未被删除
原因:删除元素后列表左移,导致跳过某些元素。
解决方案:遍历副本:
>>> numbers = [2, 4, 6, 8]
>>> for number in numbers[:]: # [:] 创建副本
... if number % 2 == 0:
... numbers.remove(number)
...
>>> numbers
[]
但有时即使使用副本也不够。例如,同时删除偶数并平方奇数:
>>> numbers = [2, 1, 4, 6, 5, 8]
>>> processed_numbers = []
>>> for number in numbers:
... if number % 2 != 0:
... processed_numbers.append(number**2)
...
>>> processed_numbers
[1, 25]
更安全的做法是构建新列表,而不是修改原列表。
字典在迭代中禁止修改大小:
>>> values = {"one": 1, "two": 2}
>>> for v in values:
... del values[v] # RuntimeError!
解决方法:使用 .copy() 或构建新字典。
修改循环变量
修改循环变量不会影响原始数据:
>>> names = ["Alice", "Bob", "John", "Jane"]
>>> for name in names:
... name = name.upper() # 仅修改局部变量
... print(name)
...
ALICE
BOB
JOHN
JANE
>>> names
['Alice', 'Bob', 'John', 'Jane'] # 原列表未变
循环变量只是当前元素的引用,重新赋值不会改变原集合。
忽略可能的异常
如果循环体中发生未处理的异常,循环会提前终止:
>>> files = ["file1.txt", "file2.txt"]
>>> for file in files:
... with open(file) as f: # 若文件不存在,抛出异常
... print(f.read())
...
FileNotFoundError: ...
解决方案:使用 try-except 捕获异常:
>>> for file in files:
... try:
... with open(file) as f:
... print(f.read())
... except FileNotFoundError:
... print(f"Error: {file} not found. Skipping.")
这样即使某个文件出错,循环仍会继续处理其余文件。
for 循环 vs 推导式(Comprehensions)
当用 for 循环构建新集合时,常可用推导式替代。
例如,用循环构建立方数列表:
>>> cubes = []
>>> for number in range(10):
... cubes.append(number**3)
...
>>> cubes
[0, 1, 8, ..., 729]
可用列表推导式简化:
>>> cubes = [number**3 for number in range(10)]
推导式更简洁、高效,且更具 Pythonic 风格。
使用 async for 进行异步迭代
async for 用于遍历异步可迭代对象。循环集合必须是异步迭代器。
示例:异步生成数字范围
# async_range.py
import asyncio
class AsyncRange:
def __init__(self, start, end):
self.data = range(start, end)
async def __aiter__(self):
for index in self.data:
await asyncio.sleep(0.5)
yield index
async def main():
async for index in AsyncRange(0, 5):
print(index)
asyncio.run(main())
输出(每 0.5 秒打印一个数字):
0
1
2
3
4
async for 适用于 I/O 密集型任务,如网络请求、文件读取等。
总结
你已学会:
- 如何使用
for循环遍历各种 Python 集合 - 如何使用
break、continue、else和嵌套循环 - Pythonic 的循环技巧(
enumerate、zip、chain等) - 如何避免常见陷阱(修改集合、忽略异常等)
- 如何使用
async for进行异步迭代
掌握 for 循环是 Python 开发的核心技能。它能帮助你高效处理重复任务和数据,写出更 Pythonic、更易维护的代码。