强曰为道

与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

05 - 控制流

第 05 章:控制流

掌握 Python 的条件判断、循环、上下文管理器、模式匹配以及推导式。


5.1 条件判断

5.1.1 if 语句

temperature = 35

if temperature > 30:
    print("天气炎热")
elif temperature > 20:
    print("天气适宜")
elif temperature > 10:
    print("天气凉爽")
else:
    print("天气寒冷")

5.1.2 三元表达式

# 语法:value_if_true if condition else value_if_false
age = 20
status = "成年" if age >= 18 else "未成年"

# 嵌套三元(不推荐过度使用)
level = "高" if score >= 90 else ("中" if score >= 60 else "低")

5.1.3 match/case(Python 3.10+)

# 基本匹配
def http_status(code: int) -> str:
    match code:
        case 200:
            return "OK"
        case 301:
            return "Moved Permanently"
        case 404:
            return "Not Found"
        case 500:
            return "Internal Server Error"
        case _:
            return f"Unknown: {code}"

# 模式匹配(解构)
def process_command(command: str) -> str:
    match command.split():
        case ["quit"]:
            return "退出程序"
        case ["load", filename]:
            return f"加载文件: {filename}"
        case ["copy", source, dest]:
            return f"复制 {source}{dest}"
        case ["copy", *files]:
            return f"复制多个文件: {files}"
        case _:
            return f"未知命令: {command}"

print(process_command("load data.csv"))     # 加载文件: data.csv
print(process_command("copy a.txt b.txt"))  # 复制 a.txt 到 b.txt

# 守卫条件
def check_number(n: int) -> str:
    match n:
        case x if x > 0:
            return "正数"
        case x if x < 0:
            return "负数"
        case _:
            return "零"

5.1.4 match 与类模式

from dataclasses import dataclass

@dataclass
class Point:
    x: float
    y: float

@dataclass
class Circle:
    center: Point
    radius: float

def describe_shape(shape):
    match shape:
        case Point(x=0, y=0):
            return "原点"
        case Point(x=x, y=y):
            return f"点 ({x}, {y})"
        case Circle(center=Point(x=cx, y=cy), radius=r):
            return f"圆心 ({cx}, {cy}), 半径 {r}"

print(describe_shape(Point(0, 0)))    # 原点
print(describe_shape(Point(1, 2)))    # 点 (1, 2)
print(describe_shape(Circle(Point(0, 0), 5)))  # 圆心 (0, 0), 半径 5

5.2 for 循环

5.2.1 基本迭代

# 遍历列表
fruits = ["apple", "banana", "cherry"]
for fruit in fruits:
    print(fruit)

# 遍历字符串
for char in "Python":
    print(char, end=" ")  # P y t h o n

# 遍历字典
person = {"name": "Alice", "age": 30}
for key, value in person.items():
    print(f"{key}: {value}")

5.2.2 range()

# range(stop)
for i in range(5):
    print(i)  # 0, 1, 2, 3, 4

# range(start, stop)
for i in range(1, 6):
    print(i)  # 1, 2, 3, 4, 5

# range(start, stop, step)
for i in range(0, 10, 2):
    print(i)  # 0, 2, 4, 6, 8

# 倒序
for i in range(5, 0, -1):
    print(i)  # 5, 4, 3, 2, 1

5.2.3 enumerate()

# 同时获取索引和值
fruits = ["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}")

5.2.4 zip()

# 并行遍历多个序列
names = ["Alice", "Bob", "Charlie"]
ages = [25, 30, 35]
cities = ["北京", "上海", "广州"]

for name, age, city in zip(names, ages, cities):
    print(f"{name}, {age}岁, {city}")

# zip 以最短的为准
# itertools.zip_longest 以最长的为准
from itertools import zip_longest
for name, age in zip_longest(names, [25], fillvalue="未知"):
    print(f"{name}: {age}")

5.2.5 for-else

# else 在循环正常结束时执行(未被 break 中断)
def find_factor(n: int) -> int | None:
    for i in range(2, int(n**0.5) + 1):
        if n % i == 0:
            print(f"{n} 的因子: {i}")
            return i
    else:
        print(f"{n} 是质数")
        return None

find_factor(15)  # 15 的因子: 3
find_factor(17)  # 17 是质数

5.3 while 循环

5.3.1 基本用法

# 基本 while
count = 0
while count < 5:
    print(count)
    count += 1

# 用户输入
while True:
    user_input = input("输入 'quit' 退出: ")
    if user_input == "quit":
        break
    print(f"你输入了: {user_input}")

5.3.2 while-else

# else 在循环条件为 False 时执行
n = 5
while n > 0:
    print(n)
    n -= 1
else:
    print("倒计时结束!")

5.3.3 break 和 continue

# break:跳出整个循环
for i in range(10):
    if i == 5:
        break
    print(i)  # 0, 1, 2, 3, 4

# continue:跳过当前迭代
for i in range(10):
    if i % 2 == 0:
        continue
    print(i)  # 1, 3, 5, 7, 9

5.4 上下文管理器(with 语句)

# 文件操作
with open("data.txt", "w") as f:
    f.write("Hello, World!")

# 多个上下文管理器
with open("input.txt") as fin, open("output.txt", "w") as fout:
    fout.write(fin.read())

# 自定义上下文管理器
from contextlib import contextmanager
import time

@contextmanager
def timer(label: str):
    start = time.perf_counter()
    try:
        yield
    finally:
        elapsed = time.perf_counter() - start
        print(f"{label}: {elapsed:.4f} 秒")

with timer("数据处理"):
    time.sleep(1)
    # 执行某些操作

5.5 列表推导(List Comprehension)

5.5.1 基本语法

# 语法:[expression for item in iterable if condition]

# 平方
squares = [x**2 for x in range(10)]
# [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

# 带条件
evens = [x for x in range(20) if x % 2 == 0]
# [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

# 字符串处理
words = ["hello", "world", "python"]
upper_words = [w.upper() for w in words]
# ['HELLO', 'WORLD', 'PYTHON']

5.5.2 嵌套推导

# 展平二维列表
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flat = [num for row in matrix for num in row]
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

# 生成坐标对
points = [(x, y) for x in range(3) for y in range(3)]
# [(0,0), (0,1), (0,2), (1,0), (1,1), (1,2), (2,0), (2,1), (2,2)]

# 条件推导
result = [x if x > 0 else 0 for x in [-2, -1, 0, 1, 2]]
# [0, 0, 0, 1, 2]

5.5.3 对比 for 循环

# 使用 for 循环
result = []
for x in range(10):
    if x % 2 == 0:
        result.append(x ** 2)

# 使用列表推导(更简洁)
result = [x**2 for x in range(10) if x % 2 == 0]

5.6 字典推导和集合推导

# 字典推导
squares_dict = {x: x**2 for x in range(5)}
# {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}

# 反转字典
original = {"a": 1, "b": 2, "c": 3}
reversed_dict = {v: k for k, v in original.items()}
# {1: "a", 2: "b", 3: "c"}

# 集合推导
unique_lengths = {len(word) for word in ["hello", "world", "hi", "hey"]}
# {2, 3, 5}

5.7 生成器表达式(Generator Expression)

# 语法类似列表推导,但用圆括号
gen = (x**2 for x in range(10))
print(type(gen))  # <class 'generator'>

# 惰性求值,节省内存
print(next(gen))  # 0
print(next(gen))  # 1

# 作为函数参数(可以省略额外的括号)
total = sum(x**2 for x in range(1_000_000))
max_val = max(x**2 for x in range(100))

# 对比:列表推导会立即创建整个列表,内存开销大
big_list = [x**2 for x in range(1_000_000)]       # 内存大
big_gen  = (x**2 for x in range(1_000_000))       # 几乎不占内存
特性列表推导生成器表达式
语法[...](...)
返回类型listgenerator
内存立即分配惰性分配
速度首次快首次慢,后续快
重复使用✅ 可以❌ 不可以

5.8 常用迭代工具

5.8.1 itertools

import itertools

# chain:链接多个迭代器
combined = list(itertools.chain([1, 2], [3, 4], [5]))
# [1, 2, 3, 4, 5]

# product:笛卡尔积
colors = ["红", "蓝"]
sizes = ["S", "M", "L"]
for combo in itertools.product(colors, sizes):
    print(combo)
# ('红', 'S'), ('红', 'M'), ('红', 'L'), ('蓝', 'S'), ...

# combinations:组合
print(list(itertools.combinations([1, 2, 3, 4], 2)))
# [(1,2), (1,3), (1,4), (2,3), (2,4), (3,4)]

# permutations:排列
print(list(itertools.permutations([1, 2, 3], 2)))
# [(1,2), (1,3), (2,1), (2,3), (3,1), (3,2)]

# groupby:分组
data = sorted(["apple", "avocado", "banana", "blueberry", "cherry"], key=lambda x: x[0])
for key, group in itertools.groupby(data, key=lambda x: x[0]):
    print(f"{key}: {list(group)}")
# a: ['apple', 'avocado']
# b: ['banana', 'blueberry']
# c: ['cherry']

5.8.2 any() 和 all()

numbers = [2, 4, 6, 8, 10]

# any:至少一个为 True
print(any(x > 5 for x in numbers))  # True
print(any(x > 20 for x in numbers)) # False

# all:全部为 True
print(all(x % 2 == 0 for x in numbers))  # True
print(all(x > 5 for x in numbers))        # False

5.9 注意事项

🔴 注意

  • for 循环没有 C 风格的 for(i=0; i<n; i++) 语法,使用 range()
  • 列表推导不要太复杂,超过两层嵌套应改用普通循环
  • 生成器表达式只能迭代一次,重复使用需要重新创建
  • match/case 是 Python 3.10+ 特性

💡 提示

  • 使用 enumerate() 替代手动维护索引
  • 使用 zip() 并行遍历多个序列
  • 优先使用推导式而非 map() + filter()(更 Pythonic)
  • 大数据集使用生成器表达式节省内存

📌 业务场景

# 数据清洗:从 CSV 数据中提取有效记录
import csv
from datetime import datetime

def process_orders(orders: list[dict]) -> list[dict]:
    """处理订单数据。"""
    # 筛选有效订单
    valid = [
        {
            "id": order["id"],
            "amount": float(order["amount"]),
            "date": datetime.strptime(order["date"], "%Y-%m-%d"),
        }
        for order in orders
        if order.get("status") == "completed"
        and float(order.get("amount", 0)) > 0
    ]

    # 按日期排序
    valid.sort(key=lambda x: x["date"])

    return valid

# 模拟数据
orders = [
    {"id": 1, "amount": "100.50", "date": "2024-01-15", "status": "completed"},
    {"id": 2, "amount": "0", "date": "2024-01-16", "status": "completed"},
    {"id": 3, "amount": "75.00", "date": "2024-01-17", "status": "cancelled"},
]

result = process_orders(orders)
print(result)  # [{'id': 1, 'amount': 100.5, 'date': datetime(...)}]

5.10 扩展阅读