循环语句
循环语句是编程中的一个基本结构,它允许我们多次执行同一组指令,直到满足某个条件才结束。比如下面的这些应用场合,都非常适合使用循环:
- 处理大量数据: 有一个包含数千或数百万条数据的列表,并且需要对每条数据进行处理,不可能手动为每条数据写一个处理代码。使用循环,可以为所有数据编写一次处理逻辑。
- 重复执行: 例如,编写一个游戏,允许玩家可以尝试多次直到他们赢得比赛或放弃。可以使用一个循环来允许玩家多次尝试。
- 搜索和查找: 例如,有一个名单,需要查找名单里是否有特定的名字。可以使用循环遍历整个名单直到找到该名字或到达名单的末尾。
- 算法和模拟: 例如,模拟一个实验 1000 次来计算某种结果的概率。可以使用循环来执行每一次的模拟。
Python 提供了几种循环机制,主要包括 for 循环和 while 循环。
for 循环
可迭代对象和迭代器
Python 中的迭代器(iterator)是一种数据对象,它们都有一个共同的方法,叫做“得到下一个”,调用这个方法,就可以得到下一个数据。可以想象,如果有一组数据,如果使用迭代器,每次找到当前数据的下一个数据,这样持续下去,就可以遍历这组数据中的每一个数据。目前,我们只需要大致了解其概念,后面还会单开一节,专门讲解迭代器的工作原理。
可迭代对象(iterable)指的是任何可以被迭代(即遍历其成员元素)的对象。可迭代对象可以返回一个迭代器,使用迭代器访问可迭代对象中的每个数据。我们之前介绍过的列表、元组、字符串都是可迭代对象;将来会介绍的一些数据,比如字典、集合、文件对象等,也都是可迭代对象。
遍历可迭代对象的数据
for 循环是 Python 中最常用的循环结构之一。它用于迭代一个序列(列表、元组、字典、集合、字符串)或其他可迭代对象,并在每次迭代中执行代码块。以下是 for 循环的基本结构:
for variable in iterable:
# 循环体代码
其中 variable 是每次迭代中从 iterable 获取的当前元素。
比如,使用 for 循环遍历列表:
fruits = ['苹果', '香蕉', '桔子']
for fruit in fruits:
print(fruit)
# 输出:
# 苹果
# 香蕉
# 桔子
使用 for 循环遍历字符串:
word = "python"
for letter in word:
print(letter)
# 输出:
# p
# y
# t
# h
# o
# n
我们通常可以在循环中显示迭代对象的数据、将数据处理后保存、发送到其他设备,或生成一个新的列表等。但是一般不会在循环中修改、删除、增添被迭代对象,因为这种操作会立刻产生一个迷惑结果,循环会迭代修改前的数据还是修改后的数据呢,如果被迭代对象长度发生变化,那么下一个数据应该是谁呢?比如,读者可以猜测下面的程序运行结果是什么:
fruits = ['苹果', '香蕉', '桔子']
for fruit in fruits:
fruits[2:2] = '西瓜'
print(fruit)
上面这个程序会导致一个死循环。所以,最好不要在循环内增删被迭代对象,如果需要修改,可以使用列表的切片等相应方法,或者推导式、高阶函数等方法进行修改。
range() 函数
range() 是 Python 内置的一个函数,它返回一个迭代器,用于生成一系列连续的整数。这个函数在循环中特别有用。
range() 函数的基本语法如下: range([start,] stop [, step])
。其中
- start (可选):起始值,默认为0。
- stop:结束值(不包括)。
- step (可选):步进值,默认为1。
使用示例:
# 生成从0开始的连续整数
for i in range(5):
print(i)
# 输出: 0 1 2 3 4
# 指定开始和结束值
for i in range(2, 5):
print(i)
# 输出: 2 3 4
# 指定开始、结束和步进值
for i in range(0, 10, 2):
print(i)
# 输出: 0 2 4 6 8
# 使用负数作为步进值
for i in range(5, 1, -1):
print(i)
# 输出: 5 4 3 2
需要注意的是: range() 生成的序列不包括 stop 指定的值。虽然在 Python 2 中,range() 返回的是一个列表,也就是一个可迭代对象,但是在 Python 3 中,range() 返回的是一个迭代器,它不实际存储整个数字序列,这样可以节省内存。因此,如果直接打印 range() 函数,是不会看到期望的数列的。需要使用 for 循环来迭代 range() 函数生成的所有数据。或者可以使用 list() 函数把它转换成列表:
print(range(3)) # 输出: range(0, 3)
print(list(range(3))) # 输出: [0, 1, 2]
enumerate() 函数
很多编程语言,比如 C 语言,在循环中,主要使用一个由变量表示的索引值去获取所需数据。比如:
for (int i=0; i<10; i++) {
printf(array[i]);
}
上面的 C 代码中,整数变量 i 的值会在每次迭代时,从 0 依次增加到 9,然后循环中使用 i 去索引输入数据 array,从而得到每次迭代所需的数据。
有的用户可能会在 Python 中沿用这样的习惯,写出如下代码:
fruits = ['苹果', '香蕉', '草莓', '桔子']
for i in range(len(fruits)):
print(f"第 {i} 个水果是: {fruits[i]}")
# 输出:
# 第 0 个水果是: 苹果
# 第 1 个水果是: 香蕉
# 第 2 个水果是: 草莓
# 第 3 个水果是: 桔子
但实际上,Python 是利用可迭代数据来控制循环的,它并不需要一个索引变量。硬要使用索引变量,代码效率不高,也不符合 Python 的编码习惯,会降低程序可读性。如果在循环体内,只需要用到列表里的数据,可以使用 for fruit in fruits:
直接得到每个数据;如果在循环体内需要知道每个数据的索引值,可以使用 enumerate 函数得到一个带索引的可迭代对象:
fruits = ['苹果', '香蕉', '草莓', '桔子']
for i, fruit in enumerate(fruits):
print(f"Element {i} is {fruit}")
# 输出:
# 第 0 个水果是: 苹果
# ......
虽然计算机总是从 0 开始索引,但我们的自然习惯还是从 1 开始计数,如果需要打印结果更符合人类阅读习惯,可以为 enumerate 添加一个输入,指定索引从几开始,比如:
fruits = ['苹果', '香蕉', '草莓', '桔子']
for i, fruit in enumerate(fruits, 1):
print(f"Element {i} is {fruit}")
# 输出:
# 第 1 个水果是: 苹果
# 第 2 个水果是: 香蕉
# 第 3 个水果是: 草莓
# 第 4 个水果是: 桔子
zip() 函数
在一 个循环内处理多个可迭代对象,也同样不需要使用索引变量。符合 Python 习惯的代码是使用 zip() 函数。zip() 函数用于将多个可迭代对象的元素配对,返回一个新的迭代器,产生由各个可迭代对象中对应位置的元素组成的元组。比如:
a = [1, 2, 3]
b = ['a', 'b', 'c']
result = list(zip(a, b))
print(result) # 输出:[(1, 'a'), (2, 'b'), (3, 'c')]
如此,我们就只要在循环中迭代这个 zip() 函数返回的迭代器,就可以依次拿到所有的数据了。下面是一个使用 zip 函数的示例,它遍历两个列表:一个包含学生姓名 ,另一个包含他们的成绩。zip 将这两个列表的元素一一对应起来,使得我们可以在一个 for 循环中同时访问每个学生的姓名和成绩。
students = ['张三', '李四', '王五', '赵六', '小明']
scores = [90, 85, 88, 92, 95]
for student, score in zip(students, scores):
print(f'{student} 的成绩是: {score}')
# 这段代码的输出会是:
# 张三 的成绩是: 90
# 李四 的成绩是: 85
# 王五 的成绩是: 88
# 赵六 的成绩是: 92
# 小明 的成绩是: 95