Python 语言 · 面试清单
面试权重:★★★★☆ Python 岗位基础盘;「GIL」「可变默认参数」「深浅拷贝」几乎必问。
一、对象模型与变量
二、序列、字典与常用容器
三、函数与装饰器
四、GIL 与并发模型
五、内存管理与 GC
六、类、异常与工程实践
七、高频陷阱速记
参考资料
- Python 官方文档:https://docs.python.org/3/
- 《流畅的 Python》(Fluent Python)
- 站点内《Python 基础》课程笔记
面试权重:★★★★☆ Python 岗位基础盘;「GIL」「可变默认参数」「深浅拷贝」几乎必问。
| 术语 | 一句话释义 |
|---|---|
| 一切皆对象 | 数字、函数、类、模块都是对象;变量只是指向对象的名字,不是容器 |
| 引用语义 | 赋值是绑定引用,不是拷贝值;a = b 后两者指向同一对象 |
| 可变 vs 不可变 | list/dict/set/bytearray 可变;int/str/tuple/frozenset 不可变 |
is vs == | is 比身份(id()),== 比值(可重写 __eq__);判断 None 必须用 is |
| 小整数池与字符串驻留 | 小整数(-5~256)与部分字符串会被复用,所以 a is b 有时为 True 属实现细节,不可依赖 |
id() | 对象在 CPython 中的内存地址 |
| 可变默认参数的坑 | def f(x=[]) 默认值只在定义时求值一次,会跨调用累积;要用 None 占位 |
| 闭包的晚绑定 | 循环里创建的 lambda 捕获的是变量而非值,需用默认参数或 functools.partial 固定 |
| LEGB 作用域 | Local → Enclosing → Global → Builtin 的查找顺序 |
global / nonlocal | 声明要修改的是模块级 / 外层函数级变量 |
__slots__ | 用固定槽位替代 __dict__,省内存并禁止动态加属性 |
| 术语 | 一句话释义 |
|---|---|
| list | 动态数组,按下标 O(1),头部插入 O(n) |
| tuple | 不可变序列,可作 dict 键、更省内存、可哈希 |
| dict | 哈希表,3.7+ 保证插入有序;查找均摊 O(1) |
| dict 的有序实现 | 紧凑布局:索引数组 + 条目数组,同时省内存且保序 |
| set | 哈希集合,O(1) 判重,元素必须可哈希 |
切片 a[1:3] | 返回新对象(浅拷贝);a[::-1] 反转 |
| 深浅拷贝 | 浅拷贝只复制第一层(copy.copy/list(x)),深拷贝递归复制(copy.deepcopy,靠 __deepcopy__/memo 处理循环引用) |
sorted vs list.sort | 前者返回新列表、可用于任意可迭代对象;后者原地排序返回 None |
key 与稳定排序 | Timsort 稳定,多级排序可用连续多次 sorted |
| 推导式 | 列表/字典/集合/生成器推导式,比显式循环更短更快(局部作用域) |
collections 常用件 | Counter、defaultdict、deque(双端队列)、namedtuple、OrderedDict |
deque vs list | deque 两端 O(1),适合队列;list 头部弹出 O(n) |
| heapq | 小顶堆操作,取最大的 K 个要存负值或自定义元组 |
| 术语 | 一句话释义 |
|---|---|
| 一等函数 | 函数可赋值、传参、返回,是所有高阶特性的基础 |
*args / **kwargs | 收集位置参数为 tuple、关键字参数为 dict |
| 仅位置 / 仅关键字参数 | / 之前只能位置传参,* 之后只能关键字传参 |
| 装饰器 Decorator | 接收函数返回新函数的可调用对象,用于横切逻辑(日志、缓存、鉴权、重试) |
functools.wraps | 保留原函数的 __name__/__doc__,否则调试与框架反射会失效 |
| 带参数的装饰器 | 三层嵌套:factory(args) → decorator(func) → wrapper |
类装饰器 / __call__ | 用类实现装饰器可携带状态 |
functools.lru_cache | 基于参数的记忆化缓存,参数必须可哈希 |
functools.partial | 固定部分参数生成新函数,替代 lambda 的可读写法 |
上下文管理器 with | 靠 __enter__/__exit__,确保异常时也能释放资源 |
contextlib.contextmanager | 用生成器写上下文管理器,yield 前是进入、后是退出 |
| 生成器 Generator | 含 yield 的函数,惰性产出、一次一个值、省内存 |
| 迭代器 vs 可迭代对象 | 可迭代对象实现 __iter__;迭代器同时实现 __iter__ 与 __next__ 且可耗尽 |
yield from | 委托给子生成器,简化嵌套迭代与协程转发 |
| 协程 Coroutine | async def 定义,靠事件循环调度,await 处让出控制权 |
asyncio 事件循环 | 单线程内通过 IO 多路复用并发调度大量任务 |
| async 的适用边界 | 只对 IO 密集有效;CPU 密集阻塞事件循环,必须丢给线程/进程池 |
run_in_executor | 把阻塞函数放到线程/进程池,避免卡住事件循环 |
| 术语 | 一句话释义 |
|---|---|
| GIL | 全局解释器锁,CPython 同一时刻只允许一个线程执行字节码 |
| GIL 存在的原因 | 简化引用计数与内存管理、保证 C 扩展的线程安全 |
| GIL 的影响 | 多线程无法并行跑满多核 CPU;但 IO 等待时 GIL 会释放,所以 IO 密集型多线程仍有效 |
| GIL 的释放时机 | 每 sys.getswitchinterval()(默认 5ms)或发生阻塞 IO 时 |
| 绕开 GIL 的手段 | 多进程(multiprocessing)、C 扩展(NumPy/Cython)、其他解释器(Jython 无 GIL)、3.13+ 自由线程构建 |
| 多线程 vs 多进程 vs 协程 | IO 密集且量多用协程;IO 密集但库是阻塞式用多线程;CPU 密集用多进程 |
multiprocessing | 独立解释器与内存空间,靠 pickle 序列化通信,注意 Windows 下的 spawn 与 __main__ 保护 |
| 进程池 / 线程池 | concurrent.futures 的 ProcessPoolExecutor/ThreadPoolExecutor 统一接口 |
| 线程安全的数据结构 | queue.Queue 是线程安全的;list/dict 的单次操作因 GIL 原子,但「先查后改」不是原子操作 |
threading.Lock / RLock | 互斥锁 / 可重入锁(同一线程可重复获取) |
| 条件变量与事件 | Condition 生产者消费者;Event 线程间信号 |
| 术语 | 一句话释义 |
|---|---|
| 引用计数 | 每个对象记录被引用次数,归零即释放——这是主要回收手段 |
| 循环引用 | 引用计数无法回收环,需要 GC 兜底 |
| 标记清除 | 从根对象出发标记可达对象,回收不可达的容器对象 |
| 分代回收 | 分 0/1/2 三代,新对象放 0 代,存活越久越少被扫描——因为大多数对象很快死亡 |
gc 模块 | gc.collect() 手动触发、gc.freeze() 冻结对象、gc.get_objects() 排查 |
| 内存池 pymalloc | 小对象(≤512 字节)走独立内存池,减少 malloc 调用与碎片 |
| arena / pool / block | pymalloc 的三级结构,按 8 字节对齐分档 |
| 内存泄漏的表现 | RSS 持续增长、对象数只增不减;用 tracemalloc/objgraph/gc.get_referrers 定位 |
__del__ 与 weakref | 析构函数的执行时机不确定,循环引用时可能永不执行;weakref 用于避免强引用成环 |
sys.getrefcount | 查看引用计数(传参本身会 +1,所以看到的是实际值 +1) |
| 术语 | 一句话释义 |
|---|---|
| MRO 与 C3 线性化 | 多继承的方法解析顺序,Class.__mro__ 可查看 |
super() | 按 MRO 调用下一个父类,是协作式多继承的关键,不要写死父类名 |
@property | 把方法变成属性访问,实现受控读写与惰性计算 |
@staticmethod / @classmethod | 前者无 self 只是逻辑归属,后者第一个参数是 cls,常作备选构造器 |
| 魔术方法 | __init__/__repr__/__eq__/__hash__/__len__/__getitem__ 决定对象行为 |
__new__ vs __init__ | __new__ 创建实例(单例/不可变类型子类化时用),__init__ 初始化 |
| 元类 Metaclass | 类的类,可控制类的创建;框架(ORM)常用,业务代码应尽量避免 |
| 异常处理 | try/except/else/finally;finally 一定会执行(return 也拦不住) |
异常链 raise from | 保留原始异常上下文,排障时非常关键 |
| EAFP vs LBYL | 「先做再看异常」优于「先判断再做」,但要避免用异常控制正常流程 |
if __name__ == '__main__' | 模块被直接执行时才运行的入口保护,多进程必需 |
| 类型注解 | typing 与 PEP 604 的联合类型写法;运行时不做强校验,但可被 mypy/pyright 静态检查 |
| 虚拟环境与依赖锁定 | venv/uv/pip-tools;锁文件保证环境可复现 |
| 性能优化的正确顺序 | 先 profile(cProfile/py-spy)再优化,别凭直觉;热点优先换成向量化或 C 扩展 |
| 术语 | 一句话释义 |
|---|---|
== 与 is 混用 | 比较值用 ==,比较 None/单例用 is |
| 浮点精度 | 0.1 + 0.2 != 0.3,比较要用 math.isclose,金额用 Decimal |
| 字符串拼接 | 循环里 += 是 O(n²),应 ''.join(list) |
| 成员判断 | x in list 是 O(n),频繁判断用 set/dict |
| 浅拷贝的连锁反应 | 二维列表浅拷贝后内层仍共享,改动会互相影响 |
is 判断整数 | 大整数不驻留,1000 is 1000 在不同场景结果可能不同 |
dict 迭代时修改 | 直接增删会抛 RuntimeError,应先构造副本或收集待删键 |
| 可变对象作字典键 | 不可哈希会报错;tuple 里含 list 也不能当键 |
copy.deepcopy 的性能陷阱 | 大对象深拷贝极慢,考虑不可变设计或显式重建 |
| 异常捕获过宽 | except Exception: pass 会吞掉真实错误,应精确捕获并记录 |