第三章:流程控制与 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 提供三种逻辑运算符:and、or、not。
# 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"))
最佳实践
- match-case 优先用于复杂匹配:当需要匹配多个模式时,match-case 更清晰
- 保持条件简洁:避免复杂的嵌套条件
- 使用有意义的变量名:提高代码可读性
- 避免过度使用三元表达式:复杂逻辑使用 if-else
- 短路求值提高性能:将快速判断的条件放在前面
# ✓ 推荐:先判断快速的条件
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 循环。