卡码笔记-最强八股文
首页
计算机基础
Python
C++
Java
Go
🔥大模型🔥
  • 大模型面经
  • Java面经
  • C++面经
大模型Github (opens new window)
代码随想录 (opens new window)
首页
计算机基础
Python
C++
Java
Go
🔥大模型🔥
  • 大模型面经
  • Java面经
  • C++面经
大模型Github (opens new window)
代码随想录 (opens new window)
  • 本栏必读

    • Python面试题专栏介绍
    • Python学习路线
    • Python零基础入门课
  • 基础语法

    • Python有哪些内置数据类型?
    • Python变量保存的是什么?
    • 可变对象和不可变对象有什么区别?
    • is和==有什么区别?
    • Python的运算符有哪些?
    • Python条件判断和循环的常见写法与易错点
      • 简要回答
      • 详细回答
      • 知识拓展
    • Python字符串为什么不可变?
    • Python列表和元组有什么区别?
    • Python字典和集合有什么区别?
    • Python切片和序列解包怎么用?
    • 列表、字典和集合推导式怎么写?
  • 函数、作用域与模块

  • 面向对象

  • 进阶语法与语言机制

# 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)  # 及格
1
2
3
4
5
6

95先满足了score >= 60,后面的elif根本不会再检查。把>= 90放在最前面才是正确顺序。

简单的二选一可以用条件表达式:level = "及格" if score >= 60 else "不及格"。条件表达式适合短小的取值,嵌套超过一层时,改回普通if更好读。

# for循环在遍历什么

for item in items:的执行过程,可以理解为下面几步:

  1. 调用iter(items),从可迭代对象拿到一个迭代器。
  2. 反复调用next(迭代器),把返回值绑定到循环变量item,执行循环体。
  3. 迭代器抛出StopIteration时,循环正常结束,如果有else就执行else。
  4. 循环体中遇到break,直接跳出循环,跳过else。

这也是为什么for能直接遍历列表、字符串、字典、文件和生成器:它们都实现了迭代协议。可迭代对象和迭代器的具体区别属于后面进阶语法的内容,这里先记住for是"取下一个元素",不是"下标加一"。

for-else循环执行流程

图中纵向主线是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
1
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("没有偶数")  # 没有偶数
1
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
1
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
1
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及以上版本。

Last Updated: 10/13/2026, 5:57:00 PM

← Python的运算符有哪些? Python字符串为什么不可变? →

评论

验证登录状态...

侧边栏 侧边栏
夜间模式 夜间
卡码简历 卡码简历
代码随想录 代码随想录
卡码投递表 卡码投递表🔥
2026实习校招群 2026群
添加客服微信 2026实习校招客服微信 PS:通过微信后,请发送姓名-学校-年级-2026实习/校招
支持卡码笔记 支持卡码笔记
鼓励/支持/赞赏Carl 卡码笔记赞赏码
1. 如果感觉本站对你很有帮助,也可以请Carl喝杯奶茶,金额大小不重要,心意已经收下
2. 希望大家都能梦想成真,有好的前程,加油💪