条件判断与循环
条件判断让程序可以「分岔」,循环让程序可以「重复」。几乎所有的业务逻辑都由这两类语句组合而成:校验参数、按状态分支、遍历数据、累积结果。本节将系统讲解 if 分支、for / while 循环、break / continue / else,以及循环与条件配合的常见模式。
前置知识
阅读本节前,建议先了解:列表快速上手,熟悉序列的遍历方式。
基础概念
Python 用缩进(Indentation)表示代码块,而不是花括号。真值判断(Truthiness) 是本节的核心:if 后面跟的不是布尔值,而是任意对象——Python 会按真值规则隐式转换。
| 值 | 真值 |
|---|---|
0、0.0、0j | 假 |
""、[]、()、{}、set() | 假 |
None、False | 假 |
| 其他非零、非空对象 | 真 |
可迭代对象(Iterable) 是 for 循环的基础,字符串、列表、元组、字典、文件、生成器都支持迭代。
语法与用法
if / elif / else 与 for 循环的最基本写法:
python
# 最简可运行示例:条件判断
score = 78
if score >= 90:
grade = "A"
elif score >= 80:
grade = "B"
elif score >= 60:
grade = "C"
else:
grade = "D"
print(f"分数 {score} 对应等级 {grade}")
# 最简可运行示例:for 循环
total = 0
for n in range(1, 6):
total += n
print(f"1 到 5 的和:{total}")text
分数 78 对应等级 C
1 到 5 的和:15详细说明
条件判断与比较
if 支持链式比较与逻辑运算符,注意 and / or 的短路求值(Short-circuit Evaluation):
python
age = 25
has_ticket = True
# 链式比较:等价于 18 <= age and age < 60
if 18 <= age < 60:
print("成人票")
# 逻辑运算符
if age >= 18 and has_ticket:
print("可以入场")
# 短路求值:or 返回第一个真值,and 返回第一个假值
username = ""
display_name = username or "匿名用户" # 常用默认值技巧
print(display_name)
config = {"timeout": 0}
timeout = config.get("timeout") or 30 # 注意:0 会被判为假!
print(f"实际超时:{timeout}") # 结果不是 0,而是 30text
成人票
可以入场
匿名用户
实际超时:30用 or 设默认值的陷阱
当合法值可能是 0、""、False 时,x or default 会错误地替换成默认值。此时应显式判断 if x is None:。
真值判断与身份判断
python
items: list[str] = []
# 推荐:直接判断容器是否为空(利用真值规则)
if not items:
print("列表为空")
# 不推荐:多余地与空容器比较
if items == []: # 不推荐
print("列表为空")
# 判断 None 用 is,而不是 ==
value = None
if value is None:
print("value 尚未赋值")
# 判断成员关系
roles = ["admin", "editor"]
if "admin" in roles:
print("拥有管理员权限")text
列表为空
value 尚未赋值
拥有管理员权限for 循环与 range
range 生成整数序列,是「计数循环」的首选。它惰性生成,不占内存:
python
# 三种形式
print(list(range(5))) # 0, 1, 2, 3, 4
print(list(range(2, 6))) # 2, 3, 4, 5
print(list(range(0, 10, 3))) # 0, 3, 6, 9
# 倒序
for i in range(5, 0, -1):
print(i, end=" ")
print()
# 遍历字典
user = {"name": "alice", "age": 30}
for key in user: # 默认遍历键
print(key)
for key, value in user.items(): # 同时遍历键值
print(f"{key} = {value}")
# 并行遍历多个序列(推荐用 zip,而不是索引)
names = ["alice", "bob", "cathy"]
scores = [90, 85, 92]
for name, score in zip(names, scores):
print(f"{name:<8}{score}")text
[0, 1, 2, 3, 4]
[2, 3, 4, 5]
[0, 3, 6, 9]
5 4 3 2 1
name
age
name = alice
age = 30
alice 90
bob 85
cathy 92while 循环
while 适用于「循环次数不确定」的场景,如等待条件满足、读取输入直到退出:
python
# 用 while 实现重试逻辑
import random
random.seed(42)
attempts = 0
max_attempts = 5
while attempts < max_attempts:
attempts += 1
if random.random() < 0.5: # 模拟一次成功率为 50% 的操作
print(f"第 {attempts} 次尝试成功")
break
else:
# while 的 else 分支:循环未被 break 时执行
print(f"重试 {max_attempts} 次仍失败,终止")
# 无限循环 + 手动退出条件
countdown = 3
while True:
print(f"倒计时 {countdown}")
countdown -= 1
if countdown == 0:
print("发射!")
breaktext
第 1 次尝试成功
倒计时 3
倒计时 2
倒计时 1
发射!for / while 的 else 子句
else 在循环正常结束(未被 break)时执行,常用于「查找未命中」的场景。它与 if 的 else 语义完全不同,容易被误解。
break、continue 与循环控制
python
# continue:跳过本次迭代
for n in range(1, 8):
if n % 2 == 0:
continue # 偶数跳过
print(n, end=" ")
print()
# break:提前结束整个循环
for n in range(1, 100):
if n * n > 50:
print(f"第一个平方大于 50 的数是 {n}")
break
# for-else:查找未命中时提示
target = "python"
languages = ["go", "java", "rust"]
for lang in languages:
if lang == target:
print("找到目标语言")
break
else:
print(f"列表中不存在 {target}")text
1 3 5 7
第一个平方大于 50 的数是 8
列表中不存在 python嵌套循环与推导式选择
python
# 嵌套循环:打印乘法表(只打印下三角)
for i in range(1, 5):
for j in range(1, i + 1):
print(f"{j}x{i}={i * j}", end="\t")
print()
# 多层循环用 break 只能跳出最内层,需要标志位或封装为函数
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
found = False
for row in matrix:
for value in row:
if value == 5:
found = True
break
if found:
break
print(f"找到 5:{found}")
# 用 any/all + 生成器表达式替代标志位(更 Pythonic)
print("找到 5:", any(5 in row for row in matrix))text
1x1=1
1x2=2 2x2=4
1x3=3 2x3=6 3x3=9
1x4=4 2x4=8 3x4=12 4x4=16
找到 5:True
找到 5: Truematch 语句
Python 3.10 引入的结构化模式匹配(Structural Pattern Matching),适合替代冗长的 if/elif:
python
# Python 3.10+
def describe_command(command: str) -> str:
"""根据命令字符串返回处理说明"""
match command.split():
case ["quit"]:
return "退出程序"
case ["go", direction]:
return f"向 {direction} 前进"
case ["pick", *items]:
return f"拾取 {len(items)} 件物品:{', '.join(items)}"
case ["look"] | ["l"]:
return "环顾四周"
case _: # 默认分支
return "无法识别的命令"
for cmd in ["quit", "go north", "pick sword shield", "l", "dance"]:
print(f"{cmd!r:24} -> {describe_command(cmd)}")text
'quit' -> 退出程序
'go north' -> 向 north 前进
'pick sword shield' -> 拾取 2 件物品:sword, shield
'l' -> 环顾四周
'dance' -> 无法识别的命令实战示例
示例一:带校验的多分支业务规则
python
from dataclasses import dataclass
@dataclass
class Order:
"""订单"""
amount: float
is_vip: bool
coupon: float = 0.0
def calc_discount(order: Order) -> tuple[float, str]:
"""计算折扣金额与说明,返回 (折扣额, 说明)"""
if order.amount <= 0:
return 0.0, "金额异常,不打折"
if order.amount >= 1000:
rate, desc = 0.15, "满 1000 打 85 折"
elif order.amount >= 500:
rate, desc = 0.10, "满 500 打 9 折"
elif order.amount >= 100:
rate, desc = 0.05, "满 100 打 95 折"
else:
rate, desc = 0.0, "未达折扣门槛"
discount = order.amount * rate
# 会员额外叠加 5%,但折扣总额不超过订单金额
if order.is_vip:
extra = order.amount * 0.05
discount += extra
desc += ",会员额外 5%"
discount = min(discount, order.amount)
return round(discount, 2), desc
orders = [
Order(amount=1200.0, is_vip=True),
Order(amount=300.0, is_vip=False),
Order(amount=50.0, is_vip=True),
]
for order in orders:
discount, desc = calc_discount(order)
final = order.amount - discount
print(f"原价 {order.amount:>8.2f} 折扣 {discount:>7.2f} 实付 {final:>8.2f} ({desc})")text
原价 1200.00 折扣 240.00 实付 960.00 (满 1000 打 85 折,会员额外 5%)
原价 300.00 折扣 15.00 实付 285.00 (满 100 打 95 折)
原价 50.00 折扣 2.50 实付 47.50 (未达折扣门槛,会员额外 5%)示例二:循环聚合与数据清洗
python
# 原始日志行:时间 级别 消息
raw_logs = [
"2026-10-08 10:00:01 INFO 服务启动",
"2026-10-08 10:00:05 ERROR 数据库连接失败",
"2026-10-08 10:00:06 INFO 重试中",
"2026-10-08 10:00:07 ERROR 数据库连接失败",
"2026-10-08 10:00:08 WARN 连接池接近上限",
"非法日志行",
]
level_count: dict[str, int] = {}
error_lines: list[str] = []
for line in raw_logs:
parts = line.split(maxsplit=3)
# 跳过格式不符的行(防御式编程)
if len(parts) != 4:
continue
_, level, message = parts[0], parts[1], parts[3]
level_count[level] = level_count.get(level, 0) + 1
if level == "ERROR":
error_lines.append(message)
print("各级别数量:")
for level, count in sorted(level_count.items()):
print(f" {level:<6}{count}")
print(f"\n错误共 {len(error_lines)} 条:")
for msg in error_lines:
print(f" - {msg}")
# 用计数器找出出现次数最多的错误
if error_lines:
top = max(set(error_lines), key=error_lines.count)
print(f"\n最常见错误:{top}({error_lines.count(top)} 次)")text
各级别数量:
ERROR 2
INFO 2
WARN 1
错误共 2 条:
- 数据库连接失败
- 数据库连接失败
最常见错误:数据库连接失败(2 次)示例三:循环控制与生成器管道
python
def read_numbers(lines: list[str]) -> list[float]:
"""从文本行中解析数字,跳过无效行"""
numbers: list[float] = []
for line in lines:
line = line.strip()
if not line or line.startswith("#"): # 跳过空行与注释
continue
try:
numbers.append(float(line))
except ValueError:
print(f"跳过无效行:{line!r}")
return numbers
raw = ["12.5", "# 这是注释", "", "abc", "7.5", "-3.0", "0"]
values = read_numbers(raw)
print(f"有效数据:{values}")
# 累加正数,遇到连续两个负数时停止
total = 0.0
negative_streak = 0
for value in values:
if value < 0:
negative_streak += 1
if negative_streak >= 2:
print("连续出现负数,提前结束统计")
break
continue
negative_streak = 0
total += value
print(f"累计正数和:{total}")text
跳过无效行:'abc'
有效数据:[12.5, 7.5, -3.0, 0.0]
累计正数和:20.0注意事项
- 缩进必须一致:同一代码块混用 Tab 与空格会抛出
TabError,统一用 4 个空格。 =与==混淆:if x = 1:是语法错误;条件判断要用==,身份判断用is。range右边界不包含:range(5)生成0~4,这是最常见的差一错误来源。- 不要在遍历列表时增删元素:会跳过元素,应遍历副本或使用推导式重建。
or设默认值会吞掉假值:合法值可能为0/""时应显式is None判断。while必须有退出条件:忘记更新循环变量会导致死循环。- 浮点数不要用
==比较:0.1 + 0.2 == 0.3为False,应使用容差或Decimal。 - 循环
else易被误读:它不是「循环出错时执行」,而是「未被break时执行」。
python
# 错误!遍历时修改列表会跳过元素
nums = [1, 2, 3, 4, 5, 6]
for n in nums:
if n % 2 == 0:
nums.remove(n)
print(f"错误结果:{nums}")
# 正确:用推导式重建
nums2 = [1, 2, 3, 4, 5, 6]
nums2 = [n for n in nums2 if n % 2 != 0]
print(f"正确结果:{nums2}")text
错误结果:[1, 3, 5, 6]
正确结果:[1, 3, 5]最佳实践
- 用
in判断成员,用is判断None:if x is None:比if x == None:更准确。 - 避免三层以上嵌套:用「卫语句(Guard Clause)」提前
return/continue降低缩进。 - 计数循环优先
range:不要手动维护i = 0; i += 1。 - 并行遍历用
zip:避免用下标同时索引多个列表。 - 提前计算循环外的不变量:把循环内不变的计算和属性查找提到循环外。
- 用
enumerate同时取索引与元素:比range(len(x))更清晰。 - 善用
any/all/sum:把「循环 + 标志位」写成内置函数表达式。 - 复杂分支用
match:Python 3.10+ 中,结构化匹配比多层if/elif更易读。
python
# 卫语句代替深层嵌套
def process(records: list[dict], min_age: int) -> list[str]:
"""筛选并返回符合条件的人员姓名"""
result: list[str] = []
for record in records:
if not record: # 卫语句:提前跳过
continue
age = record.get("age")
if age is None or age < min_age:
continue
result.append(record.get("name", "未知"))
return result
data = [
{"name": "alice", "age": 30},
{},
{"name": "bob", "age": 17},
{"name": "cathy", "age": 25},
]
print(process(data, min_age=18))text
['alice', 'cathy']下一节
继续学习:定义函数