Python Control Flow 控制流
控制流的本质:决定代码在什么条件下执行、执行多少次、是否中断、如何优雅表达逻辑。
if / elif / else
基本结构和执行规则
python
if condition1:
...
elif condition2:
...
else:
...
# 从上至下,条件成立则执行。
条件表达式(Truth Value Testing)
Python 中,if 后面不要求是 bool。
python
if obj:
...
判断规则:
- 若对象定义了
__bool__(),使用其返回值。 - 否则若定义了
__len__(),len(obj) > 0为True。 - 否则
True
常见的 False 值:
NoneFalse0""[]、{}、set()
python
if data: # 判断是否为空
if result is None: # 判断是否为 None (推荐)
工程级原则:
- 判断"有没有",
if x: - 判断"是不是 None",
is None:
短路逻辑(and / or)
执行规则:
python
a and b
# a 为 False 返回 a
# a 为 True 返回 b
a or b
# a 为 True 返回 a
# a 为 False 返回 b
返回的不是 True 或 False ,而是原始对象。
常见用法:
设置默认值
python
name = user_input or "anonymous"
防御式编程
python
obj and obj.method()
优点:
- 少写 if
- 表达力强
- 性能好
python
x = a or b # 当 a = 0 / "" / [] 时,不可能是我们想要的
if-else 表达式(三元表达式)
python
x = a if condition else b
for / while
1. for 循环的本质
python
for x in iterable:
...
for 不是计数循环,而是:
遍历实现了 迭代器协议 的对象
底层等价逻辑(简化):
python
it = iter(iterable)
while True:
try:
x = next(it)
except StopIteration:
break
📌 这解释了:
- 为什么 for 能遍历 list / dict / file / generator
- 为什么 for 不需要索引
2. range() 的本质
python
range(start, stop, step)
特点:
- 返回的是 range 对象
- 惰性计算
- 不存储所有整数
python
range(1_000_000) # 几乎不占内存
📌 工程意义:
- for + range 是高效组合
- 比自己维护计数器安全
3. while 循环
python
while condition:
...
适合场景:
- 循环次数不确定
- 状态驱动逻辑
⚠️ 风险:
- 忘记修改 condition → 死循环
📌 实践建议:
- while 一定要有 清晰的退出路径
4. for / while + else
python
for x in data:
if x == target:
break
else:
print("not found")
语义:
循环正常结束(没有 break)才执行 else
❌ 不是"兜底"
❌ 不是"最后一次执行"
常见用途:
- 查找
- 校验
- 验证完整性
📌 这是 Python 非常独特、但非常优雅的控制流设计。
break / continue / pass
1. break
- 立即终止最近一层循环
- 会跳过 else
python
for x in data:
if x < 0:
break
2. continue
- 跳过当前迭代
- 进入下一轮
python
for x in data:
if x < 0:
continue
process(x)
📌 使用 continue 的核心目标:
减少嵌套,提高可读性
3. pass
if condition:
pass
本质:
- 语法占位符
- 什么都不做
常见用途:
- 代码结构占位
- 空类 / 空函数
- 尚未实现的分支
⚠️ 陷阱:
- 不要用 pass 掩盖未处理逻辑
match-case (Python 3.10+)
match-case ≠ switch
它是 结构化模式匹配
1. 常量匹配
python
match x:
case 1:
...
case 2 | 3 | 5: # 3 或 4 或 5
...
case _:
...
_:
- 通配符
- 不绑定变量
- 永远匹配
2. 解构匹配
python
match point:
case (0, 0):
...
case (x, y):
...
match data:
case {"id": id_, "name": name}:
...
📌 本质:
- 结构 + 模式
- 不只是值相等
python
point = (3, 4)
match point:
case (0, 0):
print("origin")
case (x, 0):
print(f"x-axis, x={x}")
case (0, y):
print(f"y-axis, y={y}")
case (x, y): # 走这个分支
print(f"point ({x}, {y})")
长度敏感 list 解构:
python
data = [1, 2, 3]
match data:
case [a, b]:
print("two elements")
case [a, b, c]:
print(a, b, c)
case _:
print("other")
*rest 变长结构匹配
python
data = [1, 2, 3, 4, 5]
match data:
case [first, *middle, last]:
print(first, middle, last)
输出:
python
1 [2, 3, 4] 5
dict 解构匹配
python
data = {
"id": 1001,
"name": "Alice",
"age": 18
}
match data:
case {"id": id_, "name": name}:
print(id_, name)
📌 关键认知(非常重要):
dict 解构是"子集匹配"
- 不要求 key 完全一致
- 只关心写出来的 key
- 其余 key 会被忽略
⚠️ 容易踩坑的点
case {"id": 1001, "name": name}:
这里:
1001是 常量匹配name是 变量绑定
解构 + 守卫 (Guard)
python
user = {"id": 3, "role": "admin"}
match user:
case {"id": id_, "role": "admin"} if id_ > 0:
print("valid admin")
case _:
print("other")
📌 流程是:
1️⃣ 结构匹配
2️⃣ 变量绑定
3️⃣ 执行 if 守卫判断
不是反过来。
类型解构(类实例匹配)
python
class Point:
__match_args__ = ("x", "y")
def __init__(self, x, y):
self.x = x
self.y = y
p = Point(1, 2)
match p:
case Point(0, 0):
print("origin")
case Point(x, y):
print(x, y)
📌 这是 match-case 最容易被低估的一点:
它能把「对象 + 属性访问 + if 判断」
变成 结构化语义
match-case vs if-elif 的思维差异(核心)
老 Python 思维
if isinstance(x, tuple) and len(x) == 2:
a, b = x
...
match-case 思维
match x:
case (a, b):
...
📌 不是"判断 + 解包"
📌 而是:"我期望你的结构是这样"
综合例子
python
def handle(msg):
match msg:
case {"type": "ping"}:
return "pong"
case {"type": "move", "pos": (x, y)}:
return f"move to {x}, {y}"
case {"type": "chat", "text": text} if text:
return f"say: {text}"
case _:
return "unknown"
"结构化的协议解析"
这在:
- Web API
- 消息队列
- 游戏逻辑
- 状态机
里非常强。
推导式 (Conprehensions)
四种推导式
列表推导式
python
[x * 2 for x in range(5)]
等价于
python
result = []
for x in range(5):
result.append(x * 2)
特点:
- 立即计算
- 返回 list
- 占用完整内存
字典推导式
python
{k: v * 2 for k, v in data.items()}
本质:
python
result = {}
for k, v in data.items():
result[k] = v * 2
常见高级用途:
python
{v: k for k, v in data.items()}
集合推导式
python
{x % 3 for x in range(10)}
自动去重。
生成器推导式
python
(x * 2 for x in range(5))
区别:
- 不立即计算
- 返回 generator
- 惰性求值
- 节省内存
常见工程写法:
python
sum(x * 2 for x in range(10_000_000)
如果用 list,会炸内存。
条件推导式
过滤条件
python
[x for x in range(10) if x % 2 == 0]
等价于:
python
for x in range(10):
if x % 2 == 0:
result.append(x)
表达式中的 if-else
python
[x if x % 2 == 0 else -1 for x in range(10)]
等价于:
python
for x in range(10):
result.append(x if x % 2 == 0 else -1)
[表达式 if 条件 else 表达式 for ...] 和 [表达式 for ... if 条件] 含义完全不同。
多层 for 的执行顺序
python
[(i, j) for i in range(2) for j in range(3)]
执行顺序:
python
for i in range(2):
for j in range(3):
result.append((i,j))
记忆口诀:
从左到右读
执行顺序是嵌套展开
带条件的复杂例子:
python
[(i, j) for i in range(3) if i != 1 for j in range(3)]
等价于:
python
for i in range(3):
if i != 1:
for j in range(3):
result.append((i, j))
推导式的作用域
Python 2 的坑
python
x = 10
lst = [x for x in range(5)]
print(x) # Python2 会变成 4
因为变量泄露。
Python 3 的行为
python
x = 10
lst = [x for x in range(5)]
print(x) # 10
推导式有自己的局部作用域。
本质:
Python 3 会在内部创建一个隐式函数作用域。
隐藏陷阱:
闭包 + 推导式:
python
funcs = [(lambda: i) for i in range(3)]
for f in funcs:
print(f())
输出:
tex
2
2
2
原因:
- lambda 捕获的是变量 i
- 而不是当时的值
- 循环结束时 i = 2
正确写法:
python
funcs = [(lambda i=i: i) for i in range(3)]
这是经典 late binding 问题。
lambda 匿名函数,在需要函数的地方快速写一个临时小函数:
python
lambda 参数: 表达式
- 只能写一个表达式
- 不能写多条语句
- 自动返回表达式结果(隐式 return)
性能&底层原理
为什么列表推导式比普通循环快?
因为:
- append 在 C 层完成
- 减少 Python 字节码跳转
- 局部变量访问更快
CPython 会专门优化推导式
生成器 VS 列表推导式
| 类型 | 是否立即计算 | 内存 | 适合 |
|---|---|---|---|
| 列表 | 是 | 高 | 小数据 |
| 生成器 | 否 | 低 | 大数据流 |
高级工程技巧
与 any / all 结合
python
any(x < 0 for x in nums)
优点:
- 不生成列表
- 遇到 True 立即停止
- 隐式 break
python
any(iterable)
- 参数必须是可迭代对象
- 返回布尔值
python
all(iterable) # 只有全部为 True 才返回 True
all([True, True, False]) # 返回 False
all(x > 0 for x in nums)
常见用法:如数据合法性校验。
python
if not all(isinstance(x, int) for x in nums):
raise TypeError
与 next 结合
python
next((x for x in nums if x > 5), None)
优雅查找第一个符合条件的元素。等价于:
python
for x in nums:
if x > 5:
return x
return None
next() 从迭代器中获取"下一个元素"。
语法:
python
next(iterator)
或
python
next(iterator, default_value)
没有默认值 next(it) 当没有元素时,StopIteration 异常。有默认值 next(it, None) 就不会报错。
| 函数 | 作用 | 何时停止 |
|---|---|---|
| any | 有一个 True 即 True | 遇 True |
| all | 全 True 才 True | 遇 False |
| next | 取第一个元素 | 取到即停 |
本质上,它们都是在做控制循环的提前终止,而不用写 for, break, return 。
性能优势:
错误写法:
python
if True in [x < 0 for x in nums]:
这会:
- 生成完整列表
- 占用额外内存
正确写法:
any(x < 0 for x in nums)
- 不生成列表
- 惰性计算
- 短路退出
嵌套推导式的可读性边界:
python
[(i, j) for i in range(3) for j in range(3) if (i + j) % 2 == 0]
可读。但是如果超过两层嵌套:
- 改成普通循环
- 或拆成函数
工程优先级:
可读性 > 短代码
可迭代对象 & 迭代器
列表是可迭代对象 iterable,但不是迭代器 iterator。
- iterable 可以被 for 遍历
- iterator 可以被
next()不断取下一个值
什么是可迭代对象?
是否实现了 __iter__() 方法。
python
nums = [1, 2, 3]
print(hasattr(nums, "__iter__()")) # True
什么是迭代器?
必须同时有:
__iter__()__next__()
python
it = iter([1, 2, 3])
print(hasattr(it, "__next__")) # True
for 循环底层原理:
python
for x in nums:
print(x)
底层其实是:
python
it = iter(nums)
while True:
try:
x = next(it)
print(x)
except StopIteration:
break