python面试重在语言特性理解与问题解决思路:一、可变/不可变对象差异及陷阱;二、装饰器嵌套结构与闭包要点;三、yield from的委托迭代作用;四、gil对i/o与cpu任务并发的影响。

Python面试最常考的不是语法细节,而是对语言特性的理解深度和实际问题的解决思路。 面试官真正想看的,是你能否用Python“自然地”表达逻辑,是否清楚哪些写法高效、安全、可维护,以及遇到典型场景时有没有踩过坑、有没有反思过。
一、可变对象 vs 不可变对象——为什么 list.append() 不用赋值,而 s += 'a' 却可能新建字符串?
这是理解Python内存模型和性能的关键切入点。不可变对象(str、int、tuple)一旦创建就不能修改,所有“修改”操作(如 +=、s.upper())都会返回新对象;可变对象(list、dict、set)则允许原地修改(如 list.append()、dict.update()),不改变对象身份(id()不变)。
常见陷阱:
- 函数参数传入可变对象作为默认值(如 def func(items=[]) ),会导致多次调用间状态残留;应改用 None 做占位,内部初始化。
- 用 == 比较两个 list/dict 是深比较,但 is 只判断是否同一对象——别混淆语义。
- [[]] * 3 创建的是三个指向同一子列表的引用,修改任一子项会影响全部;需用 [[] for _ in range(3)] 独立构造。
二、装饰器与闭包——如何写出带参数的 @retry(max_times=3)?
装饰器本质是“接收函数、返回函数”的高阶函数。带参数的装饰器需要多一层嵌套:外层接收参数,中间层接收被装饰函数,内层是实际执行逻辑。
立即学习“Python免费学习笔记(深入)”;
关键点:
- 闭包变量(如 max_times)必须在中间层函数中被捕获,不能直接在最内层定义——否则每次调用都找不到。
- 用 functools.wraps(func) 包装内层函数,保留原函数的 __name__、__doc__ 等元信息,否则调试和文档工具会失效。
- 类装饰器更易管理状态(如累计重试次数),适合复杂逻辑;函数装饰器更轻量,适合通用横切关注点(日志、计时)。
三、生成器与迭代器——yield from 是为了解决什么问题?
yield 让函数变成生成器,支持惰性求值和协程;yield from 则是“委托迭代”,用于把子生成器的产出直接透传给调用方,避免手动循环 yield。
典型用途:
- 扁平化嵌套结构(如树的遍历、多层列表展开);
- 组合多个生成器(如分页读取多个文件,统一 yield 每行);
- 协程中转发子协程结果(Python 3.5+ async/await 底层也依赖此机制)。
注意:yield from gen 不仅转发值,还会处理 .send()、.throw()、.close() 等协议调用,比手动循环健壮得多。
四、GIL 与并发——为什么多线程爬虫快,但多线程计算慢?
GIL(全局解释器锁)确保同一时刻只有一个线程执行 Python 字节码。它不阻止系统调用(如网络 I/O、文件读写),但会阻塞纯 CPU 运算。
所以:
- I/O 密集型任务(爬虫、API 调用、数据库查询)用 threading 或 asyncio 效果好——线程等待时 GIL 会被释放;
- CPU 密集型任务(图像处理、数值计算)应选 multiprocessing,绕过 GIL 使用多进程;或用 C 扩展(NumPy 底层已优化);
- concurrent.futures 提供统一接口:ThreadPoolExecutor 处理 I/O,ProcessPoolExecutor 处理 CPU,代码结构几乎一致。










