第四章:循环结构
本章学习目标
- 掌握 for 循环的使用
- 理解 while 循环的适用场景
- 学会使用循环控制语句(break、continue、else)
- 掌握 enumerate、zip、range 等内置函数
- 理解列表推导式的使用
- 学会使用生成器表达式
4.1 for 循环
4.1.1 for 循环基础
for 循环用于遍历序列(列表、元组、字符串等)中的元素。
# 基本 for 循环
fruits: list[str] = ["apple", "banana", "cherry"]
for fruit in fruits:
print(fruit)
# 输出:
# apple
# banana
# cherry
4.1.2 range 函数
range() 生成一个数字序列,常用于指定循环次数。
# range(stop)
for i in range(5):
print(i, end=" ") # 0 1 2 3 4
# range(start, stop)
for i in range(1, 6):
print(i, end=" ") # 1 2 3 4 5
# range(start, stop, step)
for i in range(0, 10, 2):
print(i, end=" ") # 0 2 4 6 8
# 倒序遍历
for i in range(5, 0, -1):
print(i, end=" ") # 5 4 3 2 1
# 负数步长
for i in range(10, 0, -2):
print(i, end=" ") # 10 8 6 4 2
4.1.3 遍历字符串
# 遍历字符串的每个字符
text: str = "Python"
for char in text:
print(char, end=" ")
# P y t h o n
# 遍历字符串的索引和字符
text = "Hello"
for i, char in enumerate(text):
print(f"{i}: {char}")
# 0: H
# 1: e
# 2: l
# 3: l
# 4: o
4.1.4 遍历列表和元组
# 遍历列表
numbers: list[int] = [1, 2, 3, 4, 5]
for num in numbers:
print(num, end=" ") # 1 2 3 4 5
# 遍历元组
coordinates: tuple[int, int, int] = (10, 20, 30)
for coord in coordinates:
print(coord, end=" ") # 10 20 30
# 使用索引遍历
for i in range(len(numbers)):
print(f"{i}: {numbers[i]}")
4.1.5 遍历字典
# 遍历字典的键
person: dict[str, int | str] = {"name": "Alice", "age": 25, "city": "Beijing"}
for key in person:
print(key)
# 输出: name age city
# 遍历值
for value in person.values():
print(value)
# 输出: Alice 25 Beijing
# 遍历键值对
for key, value in person.items():
print(f"{key}: {value}")
# 输出:
# name: Alice
# age: 25
# city: Beijing
4.1.6 嵌套循环
# 打印九九乘法表
for i in range(1, 10):
for j in range(1, i + 1):
print(f"{j}×{i}={i*j:2d}", end=" ")
print()
# 输出:
# 1×1= 1
# 1×2= 2 2×2= 4
# ...
# 遍历嵌套结构
matrix: list[list[int]] = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]
for row in matrix:
for element in row:
print(element, end=" ")
print()
4.2 while 循环
4.2.1 while 循环基础
while 循环在条件为真时重复执行代码块。
# 基本 while 循环
count: int = 0
while count < 5:
print(count, end=" ") # 0 1 2 3 4
count += 1
# 计算阶乘
def factorial(n: int) -> int:
result: int = 1
while n > 1:
result *= n
n -= 1
return result
print(factorial(5)) # 120
4.2.2 无限循环
无限循环需要配合 break 语句退出。
# 猜数字游戏
import random
def guess_number():
target: int = random.randint(1, 100)
attempts: int = 0
while True:
guess: int = int(input("请输入1-100之间的数字: "))
attempts += 1
if guess < target:
print("太小了!")
elif guess > target:
print("太大了!")
else:
print(f"恭喜你!猜对了!用了{attempts}次")
break
# 退出条件明确的循环
def find_in_list(items: list[int], target: int) -> int | None:
"""在列表中查找元素,返回索引"""
index: int = 0
while index < len(items):
if items[index] == target:
return index
index += 1
return None
print(find_in_list([1, 2, 3, 4, 5], 3)) # 2
4.2.3 while-else 结构
else 块在循环正常结束时执行(没有被 break 终止)。
# 查找质因数
def find_prime_factor(n: int) -> list[int]:
"""找出 n 的所有质因数"""
factors: list[int] = []
divisor: int = 2
while divisor * divisor <= n:
if n % divisor == 0:
factors.append(divisor)
n //= divisor
else:
divisor += 1
else:
# 如果循环没有被 break 终止
if n > 1:
factors.append(n)
return factors
print(find_prime_factor(12)) # [2, 2, 3]
print(find_prime_factor(17)) # [17] (质数)
4.3 循环控制语句
4.3.1 break 语句
break 语句立即终止循环。
# 找到第一个满足条件的元素
numbers: list[int] = [1, 5, 8, 3, 9]
for n in numbers:
if n > 5:
print(f"第一个大于5的数: {n}")
break
# 在 while 中使用
search: int = 7
i: int = 0
while i < len(numbers):
if numbers[i] == search:
print(f"找到 {search} 在索引 {i}")
break
i += 1
else:
print(f"未找到 {search}")
4.3.2 continue 语句
continue 语句跳过当前迭代,进入下一次迭代。
# 打印 1-10 中的偶数
for i in range(1, 11):
if i % 2 == 1:
continue # 跳过奇数
print(i, end=" ") # 2 4 6 8 10
# 过滤列表
names: list[str] = ["Alice", "Bob", None, "Charlie", None, "David"]
valid_names: list[str] = []
for name in names:
if name is None:
continue # 跳过 None
valid_names.append(name)
print(valid_names) # ['Alice', 'Bob', 'Charlie', 'David']
# 统计正数之和,跳过负数和零
numbers: list[int] = [1, -2, 3, -4, 5, -6]
total: int = 0
for n in numbers:
if n <= 0:
continue
total += n
print(total) # 9
4.3.3 pass 语句
pass 是空操作占位符。
# 占位符
for i in range(5):
pass # TODO: 实现逻辑
# 在条件中占位
if condition:
pass # 暂时不执行任何操作
else:
do_something()
4.3.4 for-else 和 while-else
else 块在循环正常结束时执行(没有被 break 终止)。
# for-else 示例
def find_prime(n: int) -> bool:
"""判断 n 是否是质数"""
if n < 2:
return False
for i in range(2, int(n ** 0.5) + 1):
if n % i == 0:
print(f"{n} 有因数 {i}")
return False # 找到因数,不是质数
else:
# 循环正常结束,没有找到因数
print(f"{n} 是质数")
return True
find_prime(17) # 17 是质数
find_prime(18) # 18 有因数 2
4.4 迭代工具函数
4.4.1 enumerate
同时获取索引和值。
fruits: list[str] = ["apple", "banana", "cherry"]
# 基本使用
for index, fruit in enumerate(fruits):
print(f"{index}: {fruit}")
# 输出:
# 0: apple
# 1: banana
# 2: cherry
# 指定起始索引
for index, fruit in enumerate(fruits, start=1):
print(f"{index}: {fruit}")
# 输出:
# 1: apple
# 2: banana
# 3: cherry
# 转换为列表
enum_list: list[tuple[int, str]] = list(enumerate(fruits))
# [(0, 'apple'), (1, 'banana'), (2, 'cherry')]
4.4.2 zip
并行遍历多个序列。
names: list[str] = ["Alice", "Bob", "Charlie"]
ages: list[int] = [25, 30, 35]
# 组合遍历
for name, age in zip(names, ages):
print(f"{name} is {age} years old")
# 输出:
# Alice is 25 years old
# Bob is 30 years old
# Charlie is 35 years old
# 转换为字典
data: dict[str, int] = dict(zip(names, ages))
# {'Alice': 25, 'Bob': 30, 'Charlie': 35}
# 长度不同时,以最短为准
short_names: list[str] = ["Alice", "Bob"]
for name, age in zip(short_names, ages):
print(f"{name}: {age}")
# Alice: 25
# Bob: 30
4.4.3 zip_strict (Python 3.10+)
长度不同时抛出异常。
from itertools import zip_strict
# zip_strict:长度不一致会抛出异常
names: list[str] = ["Alice", "Bob", "Charlie"]
ages: list[int] = [25, 30]
try:
for name, age in zip_strict(names, ages):
print(f"{name}: {age}")
except ValueError as e:
print(f"错误: {e}") # 长度不一致
# 输出: 错误: zip_strict() argument 2 is shorter
4.4.4 map
对序列中每个元素应用函数。
# 基本用法
numbers: list[int] = [1, 2, 3, 4, 5]
# 使用 lambda
squared: list[int] = list(map(lambda x: x ** 2, numbers))
print(squared) # [1, 4, 9, 16, 25]
# 使用函数
def capitalize(word: str) -> str:
return word.capitalize()
words: list[str] = ["hello", "world", "python"]
capitalized: list[str] = list(map(capitalize, words))
print(capitalized) # ['Hello', 'World', 'Python']
4.4.5 filter
过滤序列中满足条件的元素。
numbers: list[int] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# 过滤偶数
evens: list[int] = list(filter(lambda x: x % 2 == 0, numbers))
print(evens) # [2, 4, 6, 8, 10]
# 过滤质数
def is_prime(n: int) -> bool:
if n < 2:
return False
for i in range(2, int(n ** 0.5) + 1):
if n % i == 0:
return False
return True
primes: list[int] = list(filter(is_prime, numbers))
print(primes) # [2, 3, 5, 7]
4.5 列表推导式
4.5.1 基本列表推导式
# 基本语法: [expression for item in iterable]
# 生成 0-9 的平方
squares: list[int] = [x ** 2 for x in range(10)]
print(squares) # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
# 生成列表的副本
numbers: list[int] = [1, 2, 3, 4, 5]
copy: list[int] = [x for x in numbers]
print(copy) # [1, 2, 3, 4, 5]
4.5.2 带条件的列表推导式
# 语法: [expression for item in iterable if condition]
# 过滤:提取偶数
numbers: list[int] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
evens: list[int] = [x for x in numbers if x % 2 == 0]
print(evens) # [2, 4, 6, 8, 10]
# 转换:提取字符串长度
words: list[str] = ["hello", "world", "python"]
lengths: list[int] = [len(w) for w in words]
print(lengths) # [5, 5, 6]
# 同时使用 if-else(三元表达式)
# 语法: [expression_if_true if condition else expression_false for item in iterable]
numbers: list[int] = [1, 2, 3, 4, 5]
labels: list[str] = ["偶数" if x % 2 == 0 else "奇数" for x in numbers]
print(labels) # ['奇数', '偶数', '奇数', '偶数', '奇数']
4.5.3 嵌套列表推导式
# 二维矩阵转置
matrix: list[list[int]] = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]
# 转置
transposed: list[list[int]] = [[row[i] for row in matrix] for i in range(3)]
print(transposed)
# [[1, 4, 7], [2, 5, 8], [3, 6, 9]]
# 扁平化嵌套列表
nested: list[list[int]] = [[1, 2], [3, 4], [5, 6]]
flat: list[int] = [num for row in nested for num in row]
print(flat) # [1, 2, 3, 4, 5, 6]
# 生成九九乘法表
multiplication: list[str] = [f"{i}×{j}={i*j}" for i in range(1, 10) for j in range(1, i+1)]
for item in multiplication[:10]: # 只显示前10个
print(item)
4.6 字典推导式
# 交换键值对
original: dict[str, int] = {"a": 1, "b": 2, "c": 3}
swapped: dict[int, str] = {v: k for k, v in original.items()}
print(swapped) # {1: 'a', 2: 'b', 3: 'c'}
# 过滤字典
scores: dict[str, int] = {"Alice": 85, "Bob": 92, "Charlie": 78, "Diana": 90}
passing: dict[str, int] = {k: v for k, v in scores.items() if v >= 80}
print(passing) # {'Bob': 92, 'Diana': 90}
# 转换值
data: dict[str, int] = {"a": 1, "b": 2, "c": 3}
doubled: dict[str, int] = {k: v * 2 for k, v in data.items()}
print(doubled) # {'a': 2, 'b': 4, 'c': 6}
# 根据条件分组
names: list[str] = ["Alice", "Bob", "Anna", "Bill", "Ben"]
grouped: dict[str, list[str]] = {}
for name in names:
key = name[0]
if key not in grouped:
grouped[key] = []
grouped[key].append(name)
# 使用字典推导式
grouped_v2: dict[str, list[str]] = {name[0]: [n for n in names if n[0] == name[0]] for name in names}
print(grouped) # {'A': ['Alice', 'Anna'], 'B': ['Bob', 'Bill', 'Ben']}
4.7 集合推导式
# 生成集合
unique_chars: set[str] = {c for c in "hello world" if c != " "}
print(unique_chars) # {'h', 'e', 'l', 'o', 'w', 'r', 'd'}
# 集合运算推导式
set_a: set[int] = {1, 2, 3, 4}
set_b: set[int] = {3, 4, 5, 6}
# 交集
intersection: set[int] = {x for x in set_a if x in set_b}
print(intersection) # {3, 4}
# 并集
union: set[int] = set_a | set_b
union2: set[int] = {x for x in set_a} | {x for x in set_b}
# 差集
diff: set[int] = {x for x in set_a if x not in set_b}
print(diff) # {1, 2}
4.8 生成器表达式
4.8.1 生成器 vs 列表推导式
列表推导式一次性生成所有数据,生成器表达式惰性求值,节省内存。
# 列表推导式:一次性生成
squares_list: list[int] = [x ** 2 for x in range(10)]
print(squares_list) # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
# 生成器表达式:惰性求值
squares_gen = (x ** 2 for x in range(10))
print(squares_gen) # <generator object <genexpr> at 0x...>
# 迭代生成器
for square in squares_gen:
print(square, end=" ") # 0 1 4 9 16 25 36 49 64 81
4.8.2 使用场景
# 处理大数据集
def process_large_file(filepath: str) -> int:
"""统计文件行数(内存友好)"""
# 使用生成器逐行读取
line_count = sum(1 for _ in open(filepath, 'r'))
return line_count
# 与函数结合
numbers: list[int] = [1, 2, 3, 4, 5]
# sum 使用生成器
total: int = sum(x ** 2 for x in numbers)
print(total) # 55
# max 使用生成器
maximum: int = max(x for x in numbers if x % 2 == 0)
print(maximum) # 4
4.8.3 列表推导式 vs 生成器 vs 集合推导式
# 列表推导式:返回列表
result = [x ** 2 for x in range(1000)]
print(type(result)) # <class 'list'>
print(len(result)) # 1000
# 集合推导式:返回集合
result = {x ** 2 for x in range(1000)}
print(type(result)) # <class 'set'>
print(len(result)) # 1000
# 生成器表达式:返回生成器
result = (x ** 2 for x in range(1000))
print(type(result)) # <class 'generator'>
# 需要迭代才能获取值
4.9 综合示例
示例 1:找最大公约数
def gcd(a: int, b: int) -> int:
"""欧几里得算法求最大公约数"""
while b:
a, b = b, a % b
return a
print(gcd(48, 18)) # 6
示例 2:斐波那契数列
# 斐波那契数列
def fibonacci(n: int) -> list[int]:
"""生成前 n 个斐波那契数"""
fibs: list[int] = []
a, b = 0, 1
for _ in range(n):
fibs.append(a)
a, b = b, a + b
return fibs
print(fibonacci(10)) # [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
# 使用列表推导式
def fibonacci_list_comp(n: int) -> list[int]:
fibs: list[int] = [0, 1]
[fibs.append(fibs[-1] + fibs[-2]) for _ in range(n - 2)]
return fibs[:n]
print(fibonacci_list_comp(10)) # [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
示例 3:质数筛选
def is_prime(n: int) -> bool:
if n < 2:
return False
if n == 2:
return True
if n % 2 == 0:
return False
for i in range(3, int(n ** 0.5) + 1, 2):
if n % i == 0:
return False
return True
# 筛选质数
def primes_up_to(n: int) -> list[int]:
return [x for x in range(2, n + 1) if is_prime(x)]
print(primes_up_to(50)) # [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]
示例 4:实现 zip
def my_zip(*iterables):
"""模拟 zip 函数"""
iterators = [iter(it) for it in iterables]
while True:
result = []
for it in iterators:
try:
result.append(next(it))
except StopIteration:
return
yield tuple(result)
# 测试
names = ["Alice", "Bob", "Charlie"]
ages = [25, 30, 35]
for name, age in my_zip(names, ages):
print(f"{name}: {age}")
# 输出:
# Alice: 25
# Bob: 30
# Charlie: 35
最佳实践
- 优先使用内置函数:enumerate、zip、map、filter 等更简洁高效
- 列表推导式代替 for 循环:更 Pythonic
- 生成器处理大数据:节省内存
- 避免无限循环:确保循环有终止条件
- 使用 break 提前退出:避免不必要的迭代
# ✗ 不推荐
result: list[int] = []
for i in range(1000):
result.append(i ** 2)
# ✓ 推荐
result: list[int] = [i ** 2 for i in range(1000)]
# ✓ 更推荐(大数据)
result_gen: Generator[int, None, None] = (i ** 2 for i in range(1000))
课后练习
练习 4.1:求和
使用 for 循环计算 1-100 的所有整数之和。
练习 4.2:猜数字游戏
使用 while 循环实现猜数字游戏,程序生成一个随机数,用户输入猜测,直到猜对为止。
练习 4.3:斐波那契数列
使用循环生成 Fibonacci 数列前 20 项。
练习 4.4:质数判断
实现一个函数,找出列表中的最大值(不使用 max)。
练习 4.5:列表操作
使用 enumerate 和列表推导式,生成一个包含索引和值的元组列表。
练习 4.6:字符统计
使用字典推导式,统计字符串中每个字符出现的次数。
练习 4.7:列表去重
实现一个函数,去除列表中的重复元素,保持原有顺序。
练习 4.8:矩阵转置
使用列表推导式实现矩阵转置。
练习 4.9:杨辉三角
使用列表推导式生成杨辉三角前 n 行。
练习 4.10:生成器应用
使用生成器表达式实现一个惰性求值的质数生成器。
本章小结
本章我们学习了:
- for 循环的使用和 range 函数
- while 循环和无限循环
- 循环控制语句 break、continue、else
- enumerate、zip、map、filter 等内置函数
- 列表推导式、字典推导式、集合推导式
- 生成器表达式
在下一章中,我们将学习函数和类型提示。