Administrator
发布于 2026-03-19 / 0 阅读
0
0

第四章:循环结构

第四章:循环结构

本章学习目标

  • 掌握 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

最佳实践

  1. 优先使用内置函数:enumerate、zip、map、filter 等更简洁高效
  2. 列表推导式代替 for 循环:更 Pythonic
  3. 生成器处理大数据:节省内存
  4. 避免无限循环:确保循环有终止条件
  5. 使用 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 等内置函数
  • 列表推导式、字典推导式、集合推导式
  • 生成器表达式

在下一章中,我们将学习函数和类型提示。

相关资源


评论