一文详解Python的流程控制if/for/while
本文覆盖:条件语句(if/elif/else + 三元)、循环语句(for/while + else 子句)、range/enumerate/zip 并行迭代、break/continue/pass 循环控制、match-case 模式匹配(3.10+)、常见陷阱与最佳实践,最后用两个实操 Demo 收尾。
1. 介绍:为什么需要流程控制
1.1 一句话理解
流程控制 = 程序的「大脑决策系统」。没有流程控制的程序就是一条直线——从第一行执行到最后一行,中间不做任何判断、不重复任何步骤。现实中哪有这么简单的活儿?
1.2 三大基本结构
所有编程语言的流程控制都围绕这三个结构展开:
| 结构 | 作用 | Python 关键字 |
|---|---|---|
| 顺序结构 | 代码从上到下依次执行 | (默认行为) |
| 选择结构 | 根据条件决定走哪条路 | if / elif / else / match-case |
| 循环结构 | 重复执行某段代码 | for / while |
1.3 Python 的特殊之处
跟 C/Java 比,Python 的流程控制有几个显著区别:
# 1. 没有大括号 {},靠缩进划分代码块
if True:
print("缩进就是命")
print("缩进错了就 SyntaxError")
# 2. 循环可以带 else 子句(是的你没看错,循环也有 else)
for i in range(3):
print(i)
else:
print("循环正常结束(没被 break)")
# 3. 条件表达式不用括号包裹(当然加了也不报错)
if x > 0: # ✓
if (x > 0): # ✓ 但多余
2. 条件语句:if / elif / else 与三元表达式
2.1 原理:Python 的真值测试
Python 的条件判断不是「判断 True/False」,而是真值测试(truthiness testing)。任何对象都能被 if 测试,Python 会调用内置的 bool() 函数来决定走哪条路。
# bool() 的底层逻辑(伪代码)
def bool(obj):
# 1. 先找 __bool__ 方法
if hasattr(obj, '__bool__'):
return obj.__bool__()
# 2. 再找 __len__ 方法
if hasattr(obj, '__len__'):
return obj.__len__() > 0
# 3. 都没有?默认 True
return True
假值(Falsy)清单
以下对象在 if 中等价于 False:
| 类型 | 假值 | 说明 |
|---|---|---|
NoneType | None | 空对象 |
bool | False | 布尔假 |
int | 0, 0 | 整数零 |
float | 0.0 | 浮点零 |
complex | 0j | 复数零 |
str | "" | 空字符串 |
list / tuple | [], () | 空序列 |
dict / set | {}, set() | 空映射/集合 |
range | range(0) | 空范围 |
# 验证假值
for val in [None, False, 0, 0.0, "", [], {}, set(), range(0)]:
print(f"{repr(val):>10} -> {'假值' if not val else '真值'}")
# 输出:
# None -> 假值
# False -> 假值
# 0 -> 假值
# 0.0 -> 假值
# '' -> 假值
# [] -> 假值
# {} -> 假值
# set() -> 假值
# range(0, 0) -> 假值
踩坑点: 0.0 是假值,但 0.0000001 不是。别用浮点数做条件判断,精度问题会咬人。
2.2 if / elif / else 完整语法
# 完整结构
if condition_1:
# 条件1为真时执行
pass
elif condition_2:
# 条件1为假、条件2为真时执行
pass
elif condition_3:
# 前面都假、条件3为真时执行
pass
else:
# 以上条件全假时执行(可选)
pass
执行流程
condition_1 ──True──▶ 执行代码块1 ──▶ 跳出整个if
│
False
▼
condition_2 ──True──▶ 执行代码块2 ──▶ 跳出整个if
│
False
▼
condition_3 ──True──▶ 执行代码块3 ──▶ 跳出整个if
│
False
▼
else(可选)──▶ 执行else代码块
实际示例:成绩等级判定
def grade_level(score: float) -> str:
"""根据分数返回等级"""
if score >= 90:
return "A - 优秀"
elif score >= 80:
return "B - 良好"
elif score >= 70:
return "C - 中等"
elif score >= 60:
return "D - 及格"
else:
return "F - 不及格"
# 测试
for score in [95, 85, 75, 65, 55]:
print(f"{score}分 -> {grade_level(score)}")
# 输出:
# 95分 -> A - 优秀
# 85分 -> B - 良好
# 75分 -> C - 中等
# 65分 -> D - 及格
# 55分 -> F - 不及格
C++ 对比: Python 没有 switch 语句(3.10 前没有,3.10 后用 match-case 替代)。多个 elif 就是 Python 式的 switch。在 C++ 里你可以 switch(score / 10),Python 里通常用 elif 链或者查表法(bisect 模块)。
2.3 三元表达式(条件表达式)
当 if-else 只有一个表达式时,Python 提供了简洁的三元写法:
# 语法:value_if_true if condition else value_if_false
result = "成年" if age >= 18 else "未成年"
# 等价于
if age >= 18:
result = "成年"
else:
result = "未成年"
嵌套三元(谨慎使用)
# 三层嵌套三元 —— 能用但别滥用
status = "优秀" if score >= 90 else ("及格" if score >= 60 else "不及格")
# 更推荐拆开写
if score >= 90:
status = "优秀"
elif score >= 60:
status = "及格"
else:
status = "不及格"
踩坑点: 三元表达式的求值顺序是先判断条件,再求值对应分支,不是两个分支都求值。
# 安全:只有条件为真时才调用 expensive_func() result = cheap_value if condition else expensive_func()
2.4 条件组合:and / or / not
# and:两边都真才真(短路:左边假就不看右边了)
if age >= 18 and has_id:
print("可以进入")
# or:一边真就真(短路:左边真就不看右边了)
if is_vip or is_admin:
print("有特权")
# not:取反
if not is_banned:
print("账号正常")
短路求值的妙用
# 模式1:默认值赋值(Python 3.8+ 用海象运算符更优雅)
name = user_input or "匿名"
# 模式2:安全调用(避免 None 上的属性访问)
# 如果 obj 不为 None,才调用 obj.method()
obj is not None and obj.method()
# 模式3:链式比较(Python 独有,C++ 不行)
if 0 <= score <= 100:
print("合法分数")
# 等价于
if 0 <= score and score <= 100:
print("合法分数")
C++ 对比: C++ 里 0 <= score <= 100 会被解析成 (0 <= score) <= 100,即先算 0 <= score 得到 true/false(1/0),再拿这个值跟 100 比——永远为真!Python 的链式比较是语法糖,会正确展开成 0 <= score and score <= 100。
3. 循环语句:for / while 与 else 子句
3.1 for 循环:遍历的艺术
Python 的 for 不是 C 的 for(i=0; i<n; i++),它是基于迭代器协议的——每次从可迭代对象里取一个元素,直到取完。
# 基本语法
for element in iterable:
# 对每个 element 执行
pass
# 遍历列表
fruits = ["苹果", "香蕉", "橙子"]
for fruit in fruits:
print(fruit)
# 遍历字符串
for char in "Hello":
print(char, end=" ") # H e l l o
# 遍历字典
person = {"name": "yance", "age": 30, "role": "blogger"}
for key in person: # 默认遍历 key
print(key, person[key])
for key, value in person.items(): # 同时取 key 和 value
print(f"{key}: {value}")
迭代器协议底层原理
# for 循环的等价伪代码
iterator = iter(iterable) # 调用 iterable.__iter__()
while True:
try:
element = next(iterator) # 调用 iterator.__next__()
# 执行循环体
...
except StopIteration: # 迭代器耗尽
break
# 手动模拟 for 循环 my_list = [10, 20, 30] it = iter(my_list) print(type(it)) # <class 'list_iterator'> print(next(it)) # 10 print(next(it)) # 20 print(next(it)) # 30 # print(next(it)) # StopIteration!
3.2 while 循环:条件驱动
# 基本语法
while condition:
# condition 为真时反复执行
pass
# 倒计时
count = 5
while count > 0:
print(f"倒计时:{count}")
count -= 1
print("发射!🚀")
# 输出:
# 倒计时:5
# 倒计时:4
# 倒计时:3
# 倒计时:2
# 倒计时:1
# 发射!🚀
while True + break 模式
# 经典模式:无限循环 + 条件退出
while True:
user_input = input("输入 quit 退出:")
if user_input == "quit":
break
print(f"你输入了:{user_input}")
踩坑点: while 循环最容易犯的错就是忘记更新条件变量,导致死循环。
# ❌ 死循环!count 永远是 5
count = 5
while count > 0:
print(count)
# 忘了 count -= 1
# ✓ 正确
count = 5
while count > 0:
print(count)
count -= 1
3.3 循环的 else 子句(Python 独有特性)
这是 Python 最反直觉的特性之一——循环也有 else。
语义
for...else:循环正常结束(没被break打断)时,执行else块while...else:条件变假导致循环结束时(没被break打断),执行else块
# for...else:查找质数
for n in range(2, 10):
for x in range(2, n):
if n % x == 0:
print(f"{n} = {x} * {n // x}")
break
else:
# 如果内层 for 没被 break(即没找到因子),说明 n 是质数
print(f"{n} 是质数")
# 输出:
# 2 是质数
# 3 是质数
# 4 = 2 * 2
# 5 是质数
# 6 = 2 * 3
# 7 是质数
# 8 = 2 * 4
# 9 = 3 * 3
理解 else 的触发条件
# 情况1:正常结束 -> else 执行
for i in range(3):
print(i)
else:
print("else 执行了") # ✓ 会执行
# 情况2:被 break -> else 不执行
for i in range(3):
print(i)
if i == 1:
break
else:
print("else 执行了") # ✗ 不会执行
循环执行流程:
for/while ──▶ 循环体
│
┌───────┴───────┐
│ │
遇到 break 正常结束
│ │
▼ ▼
跳出循环 执行 else 块
(跳过 else) 然后继续后续代码
踩坑点: 很多人把 for...else 理解成「循环没执行时才走 else」,这是错的。空循环也会走 else:
for i in range(0): # 循环体一次都不执行
pass
else:
print("else 照样执行") # ✓ 会执行,因为没有 break
最佳实践: 如果你觉得 for...else 难读,用标志变量替代完全没问题:
found = False
for x in range(2, n):
if n % x == 0:
found = True
break
if not found:
print(f"{n} 是质数")
4. range / enumerate / zip 并行迭代
4.1 range:生成整数序列
range 是 Python 中最常用的迭代工具之一,它不实际生成列表,而是一个惰性序列对象。
# 三种形式
range(stop) # 0, 1, 2, ..., stop-1
range(start, stop) # start, start+1, ..., stop-1
range(start, stop, step) # start, start+step, start+2*step, ...
# 基本用法
for i in range(5):
print(i, end=" ") # 0 1 2 3 4
for i in range(2, 6):
print(i, end=" ") # 2 3 4 5
for i in range(0, 10, 2):
print(i, end=" ") # 0 2 4 6 8
# 倒序
for i in range(10, 0, -1):
print(i, end=" ") # 10 9 8 7 6 5 4 3 2 1
range 的惰性特性
r = range(1000000) # 不会占内存! print(type(r)) # <class 'range'> print(len(r)) # 1000000 print(r[0]) # 0 —— 支持索引 print(r[-1]) # 999999 print(500 in r) # True —— 支持成员测试 # range 对象只存储 start, stop, step 三个值 # 不管范围多大,内存占用恒定 import sys print(sys.getsizeof(range(10))) # 48 字节 print(sys.getsizeof(range(1000000))) # 48 字节 —— 一样大!
踩坑点: range 对象不支持 + 拼接和 * 重复,这些是 list 的操作。
# ❌ TypeError range(3) + range(3) # ✓ 转成 list 或用 itertools.chain list(range(3)) + list(range(3)) # [0, 1, 2, 0, 1, 2]
4.2 enumerate:带索引遍历
当你需要「同时拿索引和元素」时,enumerate 是 Pythonic 的做法。
# ❌ 不推荐:手动维护索引
i = 0
for fruit in fruits:
print(i, fruit)
i += 1
# ❌ 不推荐:用 range(len())
for i in range(len(fruits)):
print(i, fruits[i])
# ✓ 推荐:enumerate
for index, fruit in enumerate(fruits):
print(index, fruit)
# enumerate 还能指定起始索引
for index, fruit in enumerate(fruits, start=1):
print(f"第{index}个:{fruit}")
enumerate 底层实现
# enumerate 的等价实现
def my_enumerate(iterable, start=0):
n = start
for elem in iterable:
yield n, elem
n += 1
# 它返回的是 enumerate 对象,不是列表
e = enumerate(["a", "b", "c"])
print(type(e)) # <class 'enumerate'>
print(list(e)) # [(0, 'a'), (1, 'b'), (2, 'c')]
4.3 zip:并行迭代多个序列
zip 把多个可迭代对象「拉链式」合并,每次取各序列的一个元素组成元组。
names = ["Alice", "Bob", "Charlie"]
ages = [25, 30, 35]
cities = ["北京", "上海", "深圳"]
# 基本用法:两两配对
for name, age in zip(names, ages):
print(f"{name} 今年 {age} 岁")
# 多个序列并行
for name, age, city in zip(names, ages, cities):
print(f"{name}, {age}岁, 住在{city}")
# 输出:
# Alice, 25岁, 住在北京
# Bob, 30岁, 住在上海
# Charlie, 35岁, 住在深圳
zip 的「木桶效应」
# zip 以最短的那个序列为准 a = [1, 2, 3, 4, 5] b = ["a", "b", "c"] print(list(zip(a, b))) # [(1, 'a'), (2, 'b'), (3, 'c')] —— 4和5丢了! # 如果要保留所有元素,用 itertools.zip_longest from itertools import zip_longest print(list(zip_longest(a, b, fillvalue="?"))) # [(1, 'a'), (2, 'b'), (3, 'c'), (4, '?'), (5, '?')]
zip 的妙用:矩阵转置
# 经典技巧:二维矩阵转置
matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]
transposed = list(zip(*matrix))
print(transposed) # [(1, 4, 7), (2, 5, 8), (3, 6, 9)]
# *matrix 展开 = zip([1,2,3], [4,5,6], [7,8,9])
# zip 从每个子列表取第1个元素组成第1个元组,以此类推
zip 创建字典
keys = ["name", "age", "city"]
values = ["yance", 30, "深圳"]
# Pythonic 的字典构造法
person = dict(zip(keys, values))
print(person) # {'name': 'yance', 'age': 30, 'city': '深圳'}
Python 3 的坑: Python 3 的 zip 返回的是迭代器(惰性的),只能遍历一次。Python 2 返回的是列表。
z = zip([1, 2], ["a", "b"]) print(list(z)) # [(1, 'a'), (2, 'b')] print(list(z)) # [] —— 迭代器已经耗尽了!
5. break / continue / pass 与循环控制
5.1 三者对比
| 关键字 | 作用 | 影响范围 |
|---|---|---|
break | 跳出当前循环(彻底结束循环) | 最近的 for / while |
continue | 跳过本次迭代剩余代码,进入下一次 | 最近的 for / while |
pass | 什么都不做(占位符) | 无影响 |
5.2 break:跳出循环
# 找到第一个能被7整除的数
for n in range(1, 100):
if n % 7 == 0:
print(f"找到了:{n}")
break # 找到就停,不继续了
# 输出:找到了:7
break 只跳一层
# 嵌套循环中,break 只跳出最内层
for i in range(3):
for j in range(3):
if j == 1:
break # 只跳出内层 for
print(f"i={i}, j={j}")
# 输出:
# i=0, j=0
# i=1, j=0
# i=2, j=0
跳出多层循环的技巧
# 方法1:标志变量
found = False
for i in range(5):
for j in range(5):
if i * j == 6:
found = True
break # 跳出内层
if found:
break # 跳出外层
# 方法2:for...else + break(更 Pythonic)
for i in range(5):
for j in range(5):
if i * j == 6:
print(f"找到:i={i}, j={j}")
break
else:
continue # 内层没 break,继续外层
break # 内层 break 了,也跳出外层
# 方法3:封装成函数 + return(最推荐)
def find_pair():
for i in range(5):
for j in range(5):
if i * j == 6:
return i, j
return None
result = find_pair()
5.3 continue:跳过本次
# 打印所有奇数
for n in range(1, 10):
if n % 2 == 0:
continue # 偶数跳过,不执行后面的 print
print(n, end=" ") # 1 3 5 7 9
# 过滤列表中的空字符串
data = ["hello", "", "world", None, "python", ""]
for item in data:
if not item: # 空字符串和 None 都是假值
continue
print(item)
# 输出:
# hello
# world
# python
踩坑点: continue 在 while 循环中要特别小心——确保它在条件变量更新之前不会跳过更新。
# ❌ 死循环!continue 跳过了 count += 1
count = 0
while count < 5:
if count == 2:
continue # count 永远停在 2...
print(count)
count += 1
# ✓ 正确:把更新放在 continue 之前
count = 0
while count < 5:
if count != 2:
print(count)
count += 1 # 更新一定执行
5.4 pass:占位符
pass 是一个空操作——什么都不做。它存在的唯一意义是语法占位,因为 Python 的代码块不能为空。
# 1. 空函数/类的占位
def not_implemented_yet():
pass # 先占个位,以后再写
class EmptyClass:
pass
# 2. 条件分支的占位
if debug_mode:
pass # TODO: 加调试逻辑
else:
do_something()
# 3. 在 try-except 中忽略异常
try:
risky_operation()
except SomeError:
pass # 故意忽略这个异常
pass vs … (Ellipsis)
# Python 3 中,... 也能做占位符
def stub():
...
class Empty:
...
# ... 和 pass 效果一样,但 ... 是一个真正的对象(Ellipsis)
# pass 是语句,... 是表达式
# 类型标注中 ... 有特殊含义
def callback() -> ...:
...
最佳实践: except: pass 是 Python 中最被诟病的模式之一(被称为「吞掉异常」)。如果你真的要忽略异常,至少记录一下日志:
# ❌ 吞异常,出了问题无从排查
try:
do_something()
except Exception:
pass
# ✓ 至少记录一下
import logging
try:
do_something()
except Exception as e:
logging.warning(f"操作失败: {e}")
6. match-case 模式匹配(Python 3.10+)
6.1 介绍
Python 3.10 引入了 match-case,这不是简单的 switch-case,而是结构化模式匹配(Structural Pattern Matching)——源自函数式语言(如 Haskell、Scala、Rust)的强大特性。
# 基本语法
match subject:
case pattern_1:
# 匹配 pattern_1 时执行
pass
case pattern_2:
# 匹配 pattern_2 时执行
pass
case _:
# 都不匹配时执行(_ 是通配符)
pass
6.2 各种模式
字面量模式
def http_status(code: int) -> str:
match code:
case 200:
return "OK"
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
变量绑定模式
# 匹配的值会绑定到变量上
point = (3, 4)
match point:
case (0, 0):
print("原点")
case (0, y):
print(f"在Y轴上,y={y}") # y 绑定为 4
case (x, 0):
print(f"在X轴上,x={x}")
case (x, y):
print(f"坐标:({x}, {y})") # x=3, y=4
类模式
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def describe(point: Point) -> str:
match point:
case Point(x=0, y=0):
return "原点"
case Point(x=0, y=y):
return f"Y轴上,y={y}"
case Point(x=x, y=0):
return f"X轴上,x={x}"
case Point(x=x, y=y):
return f"普通点({x}, {y})"
print(describe(Point(3, 4))) # 普通点(3, 4)
序列模式
def process_command(cmd: list) -> str:
match cmd:
case ["quit"]:
return "退出程序"
case ["help"]:
return "显示帮助"
case ["go", direction]:
return f"向{direction}移动"
case ["go", direction, distance]:
return f"向{direction}移动{distance}步"
case ["look", *rest]:
return f"查看:{rest}" # *rest 捕获剩余元素
case _:
return "未知命令"
print(process_command(["go", "north"])) # 向north移动
print(process_command(["go", "north", 5])) # 向north移动5步
print(process_command(["look", "around"])) # 查看:['around']
映射(字典)模式
def handle_event(event: dict) -> str:
match event:
case {"type": "click", "x": x, "y": y}:
return f"点击坐标({x}, {y})"
case {"type": "keypress", "key": key}:
return f"按键:{key}"
case {"type": "scroll", "delta": delta}:
return f"滚动:{delta}"
case _:
return "未知事件"
print(handle_event({"type": "click", "x": 100, "y": 200}))
# 点击坐标(100, 200)
守卫(Guard)模式
def classify_number(n: int) -> str:
match n:
case n if n < 0:
return "负数"
case n if n == 0:
return "零"
case n if n < 10:
return "小正数"
case n if n < 100:
return "中等正数"
case _:
return "大正数"
print(classify_number(-5)) # 负数
print(classify_number(0)) # 零
print(classify_number(7)) # 小正数
print(classify_number(50)) # 中等正数
print(classify_number(999)) # 大正数
or 模式
def day_type(day: str) -> str:
match day.lower():
case "saturday" | "sunday":
return "周末"
case "monday" | "tuesday" | "wednesday" | "thursday" | "friday":
return "工作日"
case _:
return "无效输入"
6.3 match-case vs if-elif-else 对比
# 传统 if-elif 写法
def handle_http(method, path):
if method == "GET" and path == "/users":
return "获取用户列表"
elif method == "GET" and path.startswith("/users/"):
user_id = path.split("/")[-1]
return f"获取用户{user_id}"
elif method == "POST" and path == "/users":
return "创建用户"
else:
return "未知路由"
# match-case 写法(更清晰)
def handle_http_v2(method, path):
parts = path.strip("/").split("/")
match (method, parts):
case ("GET", ["users"]):
return "获取用户列表"
case ("GET", ["users", user_id]):
return f"获取用户{user_id}"
case ("POST", ["users"]):
return "创建用户"
case _:
return "未知路由"
踩坑点: match-case 中的变量名如果和已有变量重名,会被当作新的绑定模式,而不是值比较!
status = 200
# ❌ 这不是比较 status == 200
match response:
case status: # 这是变量绑定!永远匹配
print(f"绑定了: {status}") # status 被重新赋值为 response 的值
# ✓ 要做值比较,用字面量或加了点号的变量
match response:
case 200:
print("OK")
case _: # 通配符
print("其他")
7. 常见陷阱与最佳实践
7.1 陷阱清单
陷阱1:在循环中修改正在遍历的列表
# ❌ 边遍历边删除,会跳过元素
nums = [1, 2, 2, 3, 4, 4, 5]
for n in nums:
if n % 2 == 0:
nums.remove(n)
print(nums) # [1, 2, 3, 4, 5] —— 没删干净!
# ✓ 方法1:遍历副本
for n in nums[:]: # nums[:] 创建副本
if n % 2 == 0:
nums.remove(n)
# ✓ 方法2:列表推导式(更 Pythonic)
nums = [n for n in nums if n % 2 != 0]
# ✓ 方法3:filter
nums = list(filter(lambda n: n % 2 != 0, nums))
陷阱2:可变默认参数与循环
# ❌ 默认参数在函数定义时只创建一次
def add_item(item, lst=[]): # lst 在多次调用间共享!
lst.append(item)
return lst
print(add_item(1)) # [1]
print(add_item(2)) # [1, 2] —— 不是 [2]!
# ✓ 用 None 做默认值
def add_item(item, lst=None):
if lst is None:
lst = []
lst.append(item)
return lst
陷阱3:浮点数做循环条件
# ❌ 浮点精度问题可能导致意外行为
i = 0.0
while i != 1.0: # 可能永远不等!
i += 0.1
print(i)
if i > 2: # 加个安全阀
break
# ✓ 用整数范围 + 缩放
for i in range(11):
val = i / 10.0
print(val)
陷阱4:无限循环耗尽资源
# ❌ 忘记更新条件
while True:
response = fetch_data()
if response is None:
break
process(response)
# 如果 fetch_data 永远不返回 None...死循环
# ✓ 加超时/最大重试限制
max_retries = 100
retry_count = 0
while retry_count < max_retries:
response = fetch_data()
if response is None:
break
process(response)
retry_count += 1
7.2 最佳实践
实践1:用列表推导式替代简单循环
# ❌ 传统写法
squares = []
for i in range(10):
squares.append(i ** 2)
# ✓ 列表推导式
squares = [i ** 2 for i in range(10)]
# 带条件过滤
even_squares = [i ** 2 for i in range(10) if i % 2 == 0]
实践2:用 any() / all() 替代显式循环
# ❌ 显式循环找满足条件的元素
def has_negative(nums):
for n in nums:
if n < 0:
return True
return False
# ✓ any() 一行搞定
def has_negative(nums):
return any(n < 0 for n in nums)
# all():所有元素都满足条件
all_positive = all(n > 0 for n in nums)
实践3:循环中避免重复计算
# ❌ 每次循环都调用 len()
for i in range(len(data)):
process(data[i], len(data)) # len(data) 每次都算
# ✓ 缓存长度
n = len(data)
for i in range(n):
process(data[i], n)
实践4:用字典替代冗长的 if-elif 链
# ❌ 冗长的 if-elif
def calculate(a, b, op):
if op == "+":
return a + b
elif op == "-":
return a - b
elif op == "*":
return a * b
elif op == "/":
return a / b
else:
raise ValueError(f"未知运算符: {op}")
# ✓ 字典分发(策略模式)
import operator
ops = {
"+": operator.add,
"-": operator.sub,
"*": operator.mul,
"/": operator.truediv,
}
def calculate(a, b, op):
if op not in ops:
raise ValueError(f"未知运算符: {op}")
return ops[op](a, b)
8. 实操 Demo
8.1 猜数字游戏
"""
猜数字游戏
- 电脑随机生成 1-100 的数字
- 玩家输入猜测,电脑提示「大了」或「小了」
- 限制最多 7 次机会(二分法理论最优)
- 支持「放弃」提前退出
"""
import random
def guess_number_game() -> None:
target = random.randint(1, 100)
max_attempts = 7
print("=" * 40)
print("🎮 猜数字游戏(1-100)")
print(f"你有 {max_attempts} 次机会,输入 'quit' 放弃")
print("=" * 40)
for attempt in range(1, max_attempts + 1):
user_input = input(f"第 {attempt}/{max_attempts} 次猜测:").strip()
# 处理退出
if user_input.lower() in ("quit", "exit", "q"):
print(f"你放弃了!答案是 {target}")
return
# 处理非数字输入
if not user_input.isdigit():
print("⚠️ 请输入数字!")
continue # 不消耗次数(视规则而定)
guess = int(user_input)
# 范围检查
if not (1 <= guess <= 100):
print("⚠️ 请输入 1-100 之间的数字!")
continue
# 比较
if guess == target:
print(f"🎉 恭喜!{attempt} 次猜中!")
return
elif guess < target:
print("📈 小了!")
else:
print("📉 大了!")
# 循环正常结束(次数用完没猜中)
else:
print(f"💀 机会用完!答案是 {target}")
# 运行游戏
if __name__ == "__main__":
guess_number_game()
运行效果:
======================================== 🎮 猜数字游戏(1-100) 你有 7 次机会,输入 'quit' 放弃 ======================================== 第 1/7 次猜测:50 📈 小了! 第 2/7 次猜测:75 📉 大了! 第 3/7 次猜测:62 📈 小了! 第 4/7 次猜测:68 📉 大了! 第 5/7 次猜测:65 🎉 恭喜!5 次猜中!
8.2 简易计算器
"""
简易计算器
- 支持加减乘除、取余、幂运算
- 支持连续计算
- 输入 'quit' 退出
- 完整的错误处理
"""
import operator
import sys
# 运算符映射表
OPERATIONS = {
"+": (operator.add, "加法"),
"-": (operator.sub, "减法"),
"*": (operator.mul, "乘法"),
"/": (operator.truediv, "除法"),
"//": (operator.floordiv, "整除"),
"%": (operator.mod, "取余"),
"**": (operator.pow, "幂运算"),
}
def get_number(prompt: str) -> float | None:
"""安全地获取数字输入,返回 None 表示退出"""
while True:
raw = input(prompt).strip()
if raw.lower() in ("quit", "exit", "q"):
return None
try:
return float(raw)
except ValueError:
print("⚠️ 输入无效,请重新输入数字!")
def get_operator() -> str | None:
"""获取运算符输入"""
ops_display = " / ".join(OPERATIONS.keys())
while True:
raw = input(f"选择运算符 ({ops_display}):").strip()
if raw.lower() in ("quit", "exit", "q"):
return None
if raw in OPERATIONS:
return raw
print("⚠️ 无效运算符,请重新输入!")
def calculate(a: float, op: str, b: float) -> float:
"""执行计算"""
func, _ = OPERATIONS[op]
return func(a, b)
def calculator() -> None:
print("=" * 50)
print("🧮 简易计算器")
print("输入 'quit' 随时退出")
print("=" * 50)
while True:
# 获取第一个操作数
a = get_number("请输入第一个数:")
if a is None:
break
# 获取运算符
op = get_operator()
if op is None:
break
# 获取第二个操作数
b = get_number("请输入第二个数:")
if b is None:
break
# 执行计算(带异常处理)
try:
result = calculate(a, op, b)
op_name = OPERATIONS[op][1]
print(f"\n✅ 结果:{a} {op} {b} = {result}\n")
# 特殊情况提示
if op == "/" and b == 0:
print("(除以零会产生 inf)")
elif op == "//" and b == 0:
print("(整除零会抛异常,已被捕获)")
except ZeroDivisionError:
print("\n❌ 错误:除数不能为零!\n")
except OverflowError:
print("\n❌ 错误:结果溢出!\n")
except Exception as e:
print(f"\n❌ 未知错误:{e}\n")
print("感谢使用,再见!👋")
# 运行计算器
if __name__ == "__main__":
calculator()
运行效果:
================================================== 🧮 简易计算器 输入 'quit' 随时退出 ================================================== 请输入第一个数:10 选择运算符 (+ / - / * / / / // / % / **):** 请输入第二个数:3 ✅ 结果:10.0 ** 3.0 = 1000.0 请输入第一个数:100 选择运算符 (+ / - / * / / / // / % / **):/ 请输入第二个数:0 ❌ 错误:除数不能为零! 请输入第一个数:quit 感谢使用,再见!👋
9. 总结
9.1 知识图谱
流程控制
├── 条件分支
│ ├── if / elif / else # 基本条件判断
│ ├── 三元表达式 # value if cond else value
│ ├── and / or / not # 逻辑组合 + 短路求值
│ └── match-case (3.10+) # 结构化模式匹配
├── 循环
│ ├── for # 遍历可迭代对象
│ ├── while # 条件驱动循环
│ └── else 子句 # 循环正常结束时执行
├── 迭代工具
│ ├── range # 惰性整数序列
│ ├── enumerate # 带索引遍历
│ └── zip # 并行迭代
├── 循环控制
│ ├── break # 跳出循环
│ ├── continue # 跳过本次
│ └── pass # 空操作占位
└── 最佳实践
├── 列表推导式替代简单循环
├── any() / all() 替代显式查找
├── 字典分发替代 if-elif 链
└── 避免在循环中修改遍历对象
9.2 速查表
| 场景 | 推荐 | 示例 |
|---|---|---|
| 简单二选一 | 三元表达式 | x if cond else y |
| 多分支 | if-elif-else | 成绩等级判定 |
| 遍历序列 | for | for item in list |
| 不知道次数 | while | 读取直到 EOF |
| 带索引遍历 | enumerate | for i, v in enumerate(lst) |
| 多序列并行 | zip | for a, b in zip(l1, l2) |
| 复杂结构匹配 | match-case | 解析命令/事件 |
| 查找元素 | any() | any(x > 0 for x in lst) |
| 过滤+变换 | 列表推导式 | [f(x) for x in lst if cond] |
9.3 C++ 程序员的 Python 流程控制速记
| C++ | Python | 备注 |
|---|---|---|
if (x) {} | if x: | 不用括号,靠缩进 |
switch-case | match-case (3.10+) 或 elif 链 | 3.10 前没有 switch |
for (int i=0; i<n; i++) | for i in range(n): | 基于迭代器 |
for (auto& x : container) | for x in container: | 天然支持 |
while (x) | while x: | 一样 |
break / continue | break / continue | 一样 |
{} 空代码块 | pass | Python 代码块不能为空 |
#define MAX 100 | MAX = 100 | 不用预处理器 |
goto | ❌ 不存在 | 用函数 + return 替代 |
以上就是一文详解Python的流程控制if/for/while的详细内容,更多关于Python流程控制if/for/while的资料请关注脚本之家其它相关文章!
相关文章
Python接口自动化之浅析requests模块post请求
这篇文章Python接口自动化之浅析requests模块post请求,以下主要介绍requests模块中的post请求的使用,post源码,data、json参数应用场景及实战2021-08-08
VSCode设置类似Pycharm控制台运行Python显示中间变量的步骤
这篇文章主要介绍了如何在VSCode中设置调试功能,以实现类似于Pycharm在控制台输出中间变量的功能,文中通过图文介绍的非常详细,需要的朋友可以参考下2025-03-03


最新评论