# Python条件判断和循环有哪些常见写法与易错点?
面试官问:"Python的for循环和C语言的for有什么不同?for后面的else什么时候执行?"
很多录友会把Python的for理解成"带下标的计数循环",于是写出for i in range(len(items)),或者在遍历列表时直接删除元素,结果漏删。for-else也常被理解成"循环没执行时走else"。这道题要讲清楚三件事:条件判断依赖真值测试,for是在遍历可迭代对象,以及**else绑定的是"循环没有被break打断"**。
# 简要回答
Python的条件判断用if、elif、else,从上到下匹配,命中第一个为真的分支后就不再检查后面的条件。条件不要求必须是布尔值,任何对象都会经过真值测试,None、0、空字符串、空容器为假。
Python的for不是计数循环,而是遍历可迭代对象:先通过iter()拿到迭代器,再反复调用next()取元素,直到迭代器结束。需要下标时用enumerate(),并行遍历用zip(),而不是手动维护索引。while用于"条件满足就继续"的场景。
for和while都可以带else:循环正常结束时执行else,被break跳出时不执行。最常见的坑是遍历列表时修改同一个列表,应改为遍历副本或用推导式生成新列表。
# 详细回答
# 条件判断:真值测试与顺序匹配
if后面可以放任何表达式,解释器会对结果做真值测试,规则和and、or使用的规则一致,详见《Python的运算符有哪些?》。所以判断列表非空,惯用写法是if items:,而不是if len(items) > 0:。
但要注意边界:if value:把0、""、[]都当成假。如果0是合法输入,只想排除None,就要写if value is not None:。
elif是按顺序匹配的,范围判断要从严格到宽松排列:
score = 95
if score >= 60:
level = "及格"
elif score >= 90:
level = "优秀" # 永远走不到
print(level) # 及格
2
3
4
5
6
95先满足了score >= 60,后面的elif根本不会再检查。把>= 90放在最前面才是正确顺序。
简单的二选一可以用条件表达式:level = "及格" if score >= 60 else "不及格"。条件表达式适合短小的取值,嵌套超过一层时,改回普通if更好读。
# for循环在遍历什么
for item in items:的执行过程,可以理解为下面几步:
- 调用
iter(items),从可迭代对象拿到一个迭代器。 - 反复调用
next(迭代器),把返回值绑定到循环变量item,执行循环体。 - 迭代器抛出
StopIteration时,循环正常结束,如果有else就执行else。 - 循环体中遇到
break,直接跳出循环,跳过else。
这也是为什么for能直接遍历列表、字符串、字典、文件和生成器:它们都实现了迭代协议。可迭代对象和迭代器的具体区别属于后面进阶语法的内容,这里先记住for是"取下一个元素",不是"下标加一"。

图中纵向主线是for通过迭代器逐个取元素的过程,右侧回环是一轮循环体结束后继续取下一个元素。else只挂在"迭代器耗尽、正常结束"这条绿色路径上,break走左侧红色虚线直接离开循环,因此会跳过else。
# 用range、enumerate、zip代替手动下标
range(start, stop, step)生成一个左闭右开的整数序列,range(5, 0, -2)依次产生5、3、1。在Python 3中,range是惰性的序列对象,不会一次性创建完整列表。
需要同时拿到下标和元素时,用enumerate():
courses = ["Python", "Go", "Java"]
for index, name in enumerate(courses, start=1):
print(index, name) # 1 Python / 2 Go / 3 Java
for name, hours in zip(courses, [40, 30]):
print(name, hours) # Python 40 / Go 30
2
3
4
5
6
enumerate()每次返回(下标, 元素)元组,for再把它解包到两个变量。zip()把多个可迭代对象按位置配对,以最短的那个为准,所以Java没有被输出。Python 3.10起可以写zip(a, b, strict=True),长度不一致时直接报错。
# break、continue和循环的else
break立即结束整个循环,continue跳过本轮剩余代码进入下一轮。它们只作用于最内层的循环,Python没有带标签的break,跳出多层循环通常把逻辑封装成函数再用return。
循环的else最适合"查找"场景:找到了就break,一个都没找到才执行else。
numbers = [3, 5, 7]
for number in numbers:
if number % 2 == 0:
print("找到偶数", number)
break
else:
print("没有偶数") # 没有偶数
2
3
4
5
6
7
三个数都不是偶数,break一次都没执行,循环正常结束,于是执行else。如果列表里有4,打印"找到偶数 4"后break,else就被跳过。遍历空列表时循环也是正常结束的,else同样会执行,所以else的含义是"没有被break",不是"循环体没执行"。while ... else的规则完全一样。
# 遍历时修改同一个容器
这是控制流里最常见的线上Bug:
nums = [1, 2, 2, 3]
for num in nums:
if num == 2:
nums.remove(num)
print(nums) # [1, 2, 3],漏删了一个2
2
3
4
5
列表迭代器内部按下标前进。删除下标1的2后,后面的元素整体左移,第二个2移到了下标1,而迭代器下一步已经走到下标2,于是跳过了它。循环本身没有报错,结果却悄悄错了,这比直接抛异常更危险。
修正方式有两种:遍历副本for num in nums[:]:,或者用推导式生成新列表nums = [num for num in nums if num != 2],后者更清晰,详见《推导式怎么写?》。字典和集合更严格,遍历时增删元素会直接抛出RuntimeError: dictionary changed size during iteration。
还有一个细节:for的循环变量在循环结束后仍然存在,保留最后一次绑定的值。for i in range(3): pass之后,print(i)输出2。Python没有块级作用域,if和for不会创建新的变量作用域。
# match-case:结构化模式匹配
Python 3.10引入了match语句。它不只是switch,还能按结构解构数据,但基础用法和多分支判断类似:
def describe(status):
match status:
case 200:
return "OK"
case 404 | 410:
return "Not Found"
case _:
return "Other"
print(describe(410)) # Not Found
2
3
4
5
6
7
8
9
10
404 | 410表示匹配任意一个,_是通配符,相当于默认分支。它和C语言switch的一个重要区别是没有贯穿(fall through),匹配成功后执行完该分支就结束,不需要写break。
最大的坑是裸名字是捕获模式,不是常量比较。写case NOT_FOUND:时,Python不会拿status和变量NOT_FOUND比较,而是把任何值都绑定到名字NOT_FOUND上,等于无条件匹配。后面还有其他分支时,解释器会直接报SyntaxError: name capture 'NOT_FOUND' makes remaining patterns unreachable。要按常量匹配,需要使用带点的名字,比如case HTTPStatus.NOT_FOUND:或case config.NOT_FOUND:。
# 知识拓展
面试官可能追问:
Q1:Python为什么没有do-while和i++?
Python倾向于用少量通用语法覆盖常见场景。do-while可以用while True:加末尾的if ...: break实现;i++被i += 1替代,整数不可变,+=本质是创建新对象再重新绑定名字。
Q2:for i in range(len(items))有什么问题?
能运行,但不地道,也更容易出错:多了一次下标访问,换成不支持下标的可迭代对象(如生成器、集合)就不能用。只要元素用for item in items,要下标和元素用enumerate(items)。
Q3:while循环里可以用海象运算符吗?
可以,Python 3.8起支持。读取数据直到为空的场景很常见,例如while (chunk := file.read(1024)):,把"读取、赋值、判断"合并到一行,避免在循环前后各写一次读取。
Q4:pass、continue和...有什么区别?
pass是空语句,什么都不做,用来占位;continue会跳过本轮剩余代码进入下一轮。...是Ellipsis对象,写在函数体里也能占位,但它是一个表达式,不是控制流语句。
Q5:match-case能完全替代if-elif吗?
不能,两者擅长的事情不同。按值或数据结构分支时match更清晰,比如解析不同形状的字典和元组;范围判断、组合条件仍然用if-elif更直接。match也需要Python 3.10及以上版本。
评论
验证登录状态...