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

第三章:流程控制与 match-case

第三章:流程控制与 match-case

本章学习目标

  • 掌握条件语句 if-elif-else 的使用
  • 理解 Python 3.10+ 的 match-case 结构化模式匹配
  • 学会使用短路求值和逻辑运算符
  • 理解真值和假值值的概念

3.1 条件语句

3.1.1 if 语句基础

条件语句用于根据条件的真假来决定是否执行特定的代码块。

# 基本的 if 语句
age: int = 18

if age >= 18:
    print("成年人")  # 条件为 True 时执行

# if-else 语句
score: int = 85

if score >= 60:
    print("及格")
else:
    print("不及格")

# if-elif-else 语句
grade: int = 85

if grade >= 90:
    result: str = "A"
elif grade >= 80:
    result = "B"
elif grade >= 70:
    result = "C"
elif grade >= 60:
    result = "D"
else:
    result = "F"

print(f"成绩等级: {result}")

3.1.2 条件表达式(三元运算符)

Python 支持简洁的条件表达式,类似于其他语言的三元运算符。

# 基本语法:value_if_true if condition else value_if_false

age: int = 20
status: str = "成年" if age >= 18 else "未成年"
print(status)  # "成年"

# 等价于
if age >= 18:
    status = "成年"
else:
    status = "未成年"

# 嵌套使用(不推荐,复杂)
max_num: int = a if a > b else b

# 复杂场景
message: str = (
    "优秀" if score >= 90
    else "良好" if score >= 80
    else "及格" if score >= 60
    else "不及格"
)

3.1.3 逻辑运算符

Python 提供三种逻辑运算符:andornot

# and:所有条件都为 True 时结果为 True
is_student: bool = True
has_id: bool = True
can_enter: bool = is_student and has_id  # True

# 真值表
# a and b:
# a=True, b=True  -> True
# a=True, b=False -> False
# a=False, b=True -> False
# a=False, b=False -> False

# or:任一条件为 True 时结果为 True
is_weekend: bool = True
is_holiday: bool = False
can_rest: bool = is_weekend or is_holiday  # True

# 真值表
# a or b:
# a=True, b=True  -> True
# a=True, b=False -> True
# a=False, b=True -> True
# a=False, b=False -> False

# not:取反
is_closed: bool = False
is_open: bool = not is_closed  # True

# 真值表
# not a:
# not True  -> False
# not False -> True

3.1.4 短路求值

短路求值是指:布尔表达式从左到右求值时,如果能确定结果,就不再继续求值。

# and 短路:如果第一个值为 False,后续不再计算
result: bool = False and (1 / 0)  # False,不执行除零操作
# 如果第一个值是 False,结果一定是 False

result: bool = True and (1 / 0)  # 抛出 ZeroDivisionError

# or 短路:如果第一个值为 True,后续不再计算
result: bool = True or (1 / 0)  # True,不执行除零操作
# 如果第一个值是 True,结果一定是 True

result: bool = False or (1 / 0)  # 抛出 ZeroDivisionError

# 实际应用:默认值
name: str | None = None
display_name: str = name or "匿名用户"  # "匿名用户"

# 实际应用:避免除零
divisor: int = 0
if divisor != 0 and 10 / divisor > 1:
    print("结果大于1")

# 使用 and-or 模拟三元运算符(需谨慎)
status: str = (age >= 18 and "成年") or "未成年"

3.1.5 比较运算符

# 相等比较
x: int = 5
y: int = 5
z: int = 10

print(x == y)   # True
print(x == z)   # False

# 不相等
print(x != z)   # True

# 大小比较
print(x > z)    # False
print(x < z)    # True
print(x >= y)   # True
print(x <= y)   # True

# 身份比较(is)
a: list[int] = [1, 2, 3]
b: list[int] = [1, 2, 3]
c = a

print(a is b)   # False (不同对象)
print(a is c)   # True (同一对象)

# 成员比较(in)
fruits: list[str] = ["apple", "banana", "cherry"]
print("banana" in fruits)    # True
print("orange" in fruits)   # False

# 字符串包含
text: str = "Hello, World!"
print("World" in text)      # True
print("world" in text)     # False (大小写敏感)

# 字典成员检查
person: dict[str, int] = {"name": "Alice", "age": 25}
print("name" in person)    # True (检查键)
print(25 in person)        # False (不检查值)

3.1.6 pass 语句

pass 是空操作占位符,用于保持代码结构完整性。

# 条件为真时什么都不做
if age >= 18:
    pass  # TODO: 实现成年人的逻辑
else:
    print("未成年")

# 空函数
def placeholder() -> None:
    pass  # 暂时不实现

# 空类
class EmptyClass:
    pass

3.2 真值与假值

3.2.1 什么是真值和假值

在 Python 中,布尔上下文中,以下值被视为假值(False):

说明
False 布尔 False
None 空值
0 整数零
0.0 浮点数零
"" 空字符串
[] 空列表
() 空元组
{} 空字典
set() 空集合

所有其他值都被视为真值。

# 假值示例
print(bool(False))       # False
print(bool(None))       # False
print(bool(0))          # False
print(bool(0.0))        # False
print(bool(""))         # False
print(bool([]))         # False
print(bool(()))         # False
print(bool({}))         # False

# 真值示例
print(bool(1))          # True
print(bool(-1))         # True
print(bool("hello"))    # True
print(bool([1, 2]))     # True
print(bool({"a": 1}))   # True

3.2.2 真值的实际应用

# 判断列表是否为空
data: list[int] = []

if data:  # 等价于 len(data) > 0
    print("有数据")
else:
    print("空列表")

# 设置默认值
name: str = ""
result: str = name or "匿名用户"

# 短路求值
items: list[int] = []
count: int = len(items) or 0  # 0

# 条件判断
value: int = -5
if value:  # 等价于 value != 0
    print(f"非零值: {value}")
else:
    print("值为零或空")

3.3 match-case 结构化模式匹配 (Python 3.10+)

3.3.1 match-case 简介

match-case 是 Python 3.10 引入的结构化模式匹配功能,类似于其他语言中的 switch-case,但更加强大。

# 基本语法
def http_status(status_code: int) -> str:
    match status_code:
        case 200:
            return "OK"
        case 201:
            return "Created"
        case 404:
            return "Not Found"
        case 500:
            return "Internal Server Error"
        case _:
            return "Unknown"  # 默认匹配

# 使用
print(http_status(200))  # OK
print(http_status(404))  # Not Found
print(http_status(999))  # Unknown

3.3.2 字面量模式

匹配特定的值或多个值。

# 单个值
def grade(score: int) -> str:
    match score:
        case 100:
            return "满分"
        case 90:
            return "优秀"
        case 80:
            return "良好"
        case _:
            return "继续努力"

# 多个值(使用 |)
def grade_multiple(score: int) -> str:
    match score:
        case 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100:
            return "A"
        case 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89:
            return "B"
        case 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79:
            return "C"
        case 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69:
            return "D"
        case _:
            return "F"

# 使用范围(带 guard 条件)
def grade_range(score: int) -> str:
    match score:
        case n if n >= 90:
            return "A"
        case n if n >= 80:
            return "B"
        case n if n >= 70:
            return "C"
        case n if n >= 60:
            return "D"
        case _:
            return "F"

3.3.3 通配符模式

使用 _ 匹配任意值。

# 简单通配符
def respond(message: str) -> None:
    match message:
        case "hello":
            print("你好!")
        case "bye":
            print("再见!")
        case _:
            print("我不知道你在说什么")

# 使用 * 解包捕获剩余元素
def process_command(command: str) -> None:
    match command.split():
        case ["quit"]:
            print("退出程序")
        case ["help"]:
            print("显示帮助")
        case ["run", *args]:
            print(f"运行命令,参数: {args}")
        case ["echo", *args]:
            print(f"回显: {args}")
        case _:
            print("未知命令")

# 测试
process_command("run test.py arg1 arg2")
# 输出: 运行命令,参数: ('test.py', 'arg1', 'arg2')

3.3.4 捕获模式 (as)

使用 as 为匹配的值指定变量名。

# 基本捕获
def describe(point: tuple[int, int]) -> str:
    match point:
        case (0, 0):
            return "原点"
        case (x, 0):
            return f"X轴上的点: {x}"
        case (0, y):
            return f"Y轴上的点: {y}"
        case (x, y) as p:
            return f"平面点: {p}"

# 测试
print(describe((0, 0)))     # 原点
print(describe((5, 0)))     # X轴上的点: 5
print(describe((0, 3)))     # Y轴上的点: 3
print(describe((2, 4)))     # 平面点: (2, 4)

3.3.5 类型模式

匹配对象的类型。

# 类型匹配
def get_info(value: int | str | float | None) -> str:
    match value:
        case int():
            return f"整数: {value}"
        case str():
            return f"字符串: {value}"
        case float():
            return f"浮点数: {value}"
        case None:
            return "空值"
        case _:
            return "未知类型"

# 测试
print(get_info(42))       # 整数: 42
print(get_info("hello"))  # 字符串: hello
print(get_info(3.14))    # 浮点数: 3.14
print(get_info(None))     # 空值

3.3.6 映射模式

匹配字典或其他映射对象。

# 字典模式
def parse_user(user: dict[str, str | int]) -> str:
    match user:
        case {"name": name, "age": int(age)} if age >= 18:
            return f"{name} 是成年人"
        case {"name": name, "age": int(age)}:
            return f"{name} 是未成年人"
        case {"name": name}:
            return f"{name},年龄未知"
        case _:
            return "无效用户信息"

# 测试
print(parse_user({"name": "Alice", "age": 20}))  # Alice 是成年人
print(parse_user({"name": "Bob", "age": 16}))    # Bob 是未成年人
print(parse_user({"name": "Charlie"}))           # Charlie,年龄未知
print(parse_user({"id": 1}))                     # 无效用户信息

# 使用 **rest 捕获额外字段
def parse_config(config: dict[str, int | str]) -> str:
    match config:
        case {"host": str(host), "port": int(port)}:
            return f"配置: {host}:{port}"
        case {"port": int(port), **rest}:
            return f"端口: {port}, 其他: {rest}"
        case _:
            return "无效配置"

print(parse_config({"host": "localhost", "port": 8080}))
# 输出: 配置: localhost:8080

print(parse_config({"port": 3000, "debug": True}))
# 输出: 端口: 3000, 其他: {'debug': True}

3.3.7 序列模式

匹配列表或元组等序列对象。

# 列表模式
def process_coordinate(coord: list[int] | tuple[int, ...]) -> str:
    match coord:
        case [x, y]:
            return f"2D坐标: ({x}, {y})"
        case [x, y, z]:
            return f"3D坐标: ({x}, {y}, {z})"
        case [x, y, z, w]:
            return f"4D坐标: ({x}, {y}, {z}, {w})"
        case _:
            return "无效坐标"

# 测试
print(process_coordinate([10, 20]))        # 2D坐标: (10, 20)
print(process_coordinate([1, 2, 3]))       # 3D坐标: (1, 2, 3)
print(process_coordinate((1, 2, 3, 4)))   # 4D坐标: (1, 2, 3, 4)

# 使用 *rest 捕获剩余元素
def first_two(items: list[int]) -> str:
    match items:
        case [first, second, *rest]:
            return f"前两个: {first}, {second}, 剩余: {rest}"
        case [first]:
            return f"只有一个元素: {first}"
        case []:
            return "空列表"

print(first_two([1, 2, 3, 4, 5]))
# 输出: 前两个: 1, 2, 剩余: [3, 4, 5]

3.3.8 类模式

匹配类实例的属性。

from dataclasses import dataclass

@dataclass
class Point:
    x: int
    y: int

@dataclass
class Circle:
    center: Point
    radius: int

def describe_shape(shape: object) -> str:
    match shape:
        case Point(x=0, y=0):
            return "原点"
        case Point(x, y):
            return f"点({x}, {y})"
        case Circle(center=Point(x=0, y=0), radius=r):
            return f"圆心在原点,半径{r}"
        case Circle(center=c, radius=r):
            return f"圆心在{c},半径{r}"
        case _:
            return "未知图形"

# 测试
p = Point(10, 20)
circle = Circle(Point(0, 0), 5)
print(describe_shape(p))       # 点(10, 20)
print(describe_shape(circle))   # 圆心在 Point(x=0, y=0),半径5

# 使用位置匹配(如果类定义支持)
@dataclass
class Person:
    name: str
    age: int

def greet(person: object) -> str:
    match person:
        case Person("Alice", age=25):
            return "你好 Alice (25岁)!"
        case Person(name, age):
            return f"你好 {name} ({age}岁)!"
        case _:
            return "你好陌生人"

print(greet(Person("Bob", 30)))  # 你好 Bob (30岁)!

3.3.9 Guard 条件

使用 if 添加额外的条件。

# Guard 条件
def classify_number(n: int) -> str:
    match n:
        case n if n < 0:
            return "负数"
        case 0:
            return "零"
        case n if n % 2 == 0:
            return f"正偶数: {n}"
        case n:
            return f"正奇数: {n}"

# 测试
print(classify_number(-5))  # 负数
print(classify_number(0))   # 零
print(classify_number(4))   # 正偶数: 4
print(classify_number(7))   # 正奇数: 7

# 复杂 Guard
def describe_list(lst: list[int]) -> str:
    match lst:
        case []:
            return "空列表"
        case [x] if x > 10:
            return f"单个大于10的元素: {x}"
        case [x]:
            return f"单个元素: {x}"
        case [x, y] if x == y:
            return f"两个相等元素: {x}"
        case [x, y]:
            return f"两个不相等元素: {x}, {y}"
        case _ if len(lst) > 5:
            return f"超过5个元素的列表,共{len(lst)}个"
        case _:
            return f"列表: {lst}"

print(describe_list([]))           # 空列表
print(describe_list([15]))         # 单个大于10的元素: 15
print(describe_list([1, 2]))       # 两个不相等元素: 1, 2
print(describe_list([5, 5]))      # 两个相等元素: 5
print(describe_list([1, 2, 3, 4, 5, 6, 7]))  # 超过5个元素的列表,共7个

3.3.10 match-case vs 传统 if-elif

对比两种方式的使用场景:

# 使用 if-elif(适合简单条件)
def grade_if(score: int) -> str:
    if score >= 90:
        return "A"
    elif score >= 80:
        return "B"
    elif score >= 70:
        return "C"
    elif score >= 60:
        return "D"
    else:
        return "F"

# 使用 match-case(适合复杂模式匹配)
def grade_match(score: int) -> str:
    match score:
        case n if n >= 90:
            return "A"
        case n if n >= 80:
            return "B"
        case n if n >= 70:
            return "C"
        case n if n >= 60:
            return "D"
        case _:
            return "F"

# 适合使用 match-case 的场景
def process_http_response(response: dict[str, str | int]) -> str:
    match response:
        case {"status": 200, "data": data}:
            return f"成功: {data}"
        case {"status": 404, "message": msg}:
            return f"未找到: {msg}"
        case {"status": 500, "error": err}:
            return f"服务器错误: {err}"
        case _:
            return "未知响应"

# if-elif 不适合这种场景

3.4 综合示例

示例 1:简单计算器

def calculator(a: float, op: str, b: float) -> float | None:
    """简单计算器"""
    match op:
        case "+":
            return a + b
        case "-":
            return a - b
        case "*":
            return a * b
        case "/":
            return a / b if b != 0 else None
        case _:
            return None

# 测试
print(calculator(10, "+", 5))   # 15.0
print(calculator(10, "/", 3))   # 3.333...
print(calculator(10, "%", 3))  # None

示例 2:命令解析器

from dataclasses import dataclass

@dataclass
class Command:
    name: str
    args: list[str]

def parse_command(cmd: str) -> Command:
    parts: list[str] = cmd.strip().split()
    return Command(parts[0], parts[1:] if len(parts) > 1 else [])

def execute(command: Command) -> None:
    match command:
        case Command("ls", args | []):
            print(f"列出目录: {args or ['.']}")
        case Command("cd", [path]):
            print(f"切换目录: {path}")
        case Command("rm", [*files]) if files:
            print(f"删除文件: {files}")
        case Command("mkdir", [*dirs]) if dirs:
            print(f"创建目录: {dirs}")
        case Command("touch", [filename]):
            print(f"创建文件: {filename}")
        case Command():
            print(f"未知命令: {command.name}")

# 测试
execute(parse_command("ls -la"))
execute(parse_command("cd /home"))
execute(parse_command("rm file1.txt file2.txt"))
execute(parse_command("mkdir new_folder"))
execute(parse_command("touch readme.md"))
execute(parse_command("unknown"))

最佳实践

  1. match-case 优先用于复杂匹配:当需要匹配多个模式时,match-case 更清晰
  2. 保持条件简洁:避免复杂的嵌套条件
  3. 使用有意义的变量名:提高代码可读性
  4. 避免过度使用三元表达式:复杂逻辑使用 if-else
  5. 短路求值提高性能:将快速判断的条件放在前面
# ✓ 推荐:先判断快速的条件
if user and user.is_active:
    # 处理活跃用户

# ✗ 不推荐:将复杂计算放在前面
if expensive_calculation() == expected_value:
    pass

课后练习

练习 3.1:条件语句基础

编写一个程序,根据成绩输出对应的等级:

  • 90-100: A
  • 80-89: B
  • 70-79: C
  • 60-69: D
  • 0-59: F

练习 3.2:闰年判断

编写一个函数,判断一年是否是闰年:

  • 能被4整除但不能被100整除
  • 或能被400整除

练习 3.3:计算器

使用 match-case 实现一个计算器,支持 +、-、、/、//、%、* 运算。

练习 3.4:命令解析

使用 match-case 解析以下命令:

  • ls [路径] - 列出目录
  • cd <路径> - 切换目录
  • mkdir <目录> - 创建目录
  • touch <文件> - 创建文件
  • rm <文件> - 删除文件
  • 其他 - 未知命令

练习 3.5:坐标点描述

使用 match-case 判断一个点:

  • 是在原点
  • 在 X 轴上(y=0)
  • 在 Y 轴上(x=0)
  • 在第一象限
  • 在第二象限
  • 在第三象限
  • 在第四象限

练习 3.6:JSON 解析

使用 match-case 解析不同类型的 JSON 数据:

  • {"type": "user", "name": "Alice"}
  • {"type": "admin", "permissions": ["read", "write"]}
  • {"type": "guest"}
  • 其他

练习 3.7:真值应用

编写一个函数,接受任意值,返回"真值"或"假值"的字符串描述。

本章小结

本章我们学习了:

  • 条件语句 if-elif-else
  • 逻辑运算符 and、or、not
  • 短路求值机制
  • 真值和假值的概念
  • Python 3.10+ match-case 结构化模式匹配

在下一章中,我们将学习循环结构,包括 for 循环和 while 循环。

相关资源


评论