简介这份《Python 八股文.pdf》面向准备技术面试的 Python 学习者与求职者系统梳理语言核心概念与高频考点帮助读者在有限时间内建立完整的知识框架。内容覆盖内置电池、生成器与迭代器、列表与元组、字典实现、GIL 全局解释器锁、Python 2 与 3 差异、可变与不可变对象、闭包、装饰器及元类等主题并配有代码示例与原理说明。资源包内含 1 个 PDF 文件大小约 304KB轻量便携适合在电脑或移动设备上随时翻阅复习。目前已有 13672 人学习下载热度较高说明其内容对面试突击与知识查漏补缺具有实际参考价值。读者可借此快速回顾生成器 yield 机制、dict 哈希查找 O(1) 原理、GIL 对多线程的影响以及装饰器在不修改源码前提下扩展函数功能等要点适合作为面试前的速查手册与日常巩固材料。1. 从一道“送分题”翻车说起Python 八股文到底考什么“生成器和迭代器有什么区别”——这道题在面试里出现频率高到离谱但能完整答对的人不到三成。多数人背了“生成器是迭代器迭代器不一定是生成器”再往下问“那 list 为什么不是迭代器”“__iter__和__next__分别解决什么问题”就开始卡壳。Python 八股文的考点分布其实很集中可变与不可变类型、迭代协议、GIL 与多线程、深浅拷贝、装饰器与闭包、元类、Py2/Py3 差异。分值上迭代协议和 GIL 几乎每场必问深浅拷贝和装饰器是区分候选人是“背过”还是“用过”的分水岭。这份笔记适合正在准备后端、数据、测试岗面试的人也适合想系统梳理 Python 底层机制的人。下面按考点模块拆开讲每道题都给答题思路和易错点。2. 高频考点迭代协议与生成器的底层逻辑2.1 可迭代对象、迭代器、生成器的三层关系面试官问“list 和迭代器的区别”真正想听的是你对迭代协议的理解。可迭代对象Iterable必须实现__iter__()调用后返回一个迭代器。迭代器Iterator必须同时实现__iter__()和__next__()__next__()每次返回一个值耗尽后抛StopIteration。生成器Generator是迭代器的一种但迭代器不一定是生成器——比如iter([1,2,3])返回的是 list_iterator不是生成器。from collections.abc import Iterable, Iterator lst [1, 2, 3] print(isinstance(lst, Iterable)) # Truelist 可迭代 print(isinstance(lst, Iterator)) # Falselist 本身不是迭代器 it iter(lst) # 通过 iter() 拿到迭代器 print(isinstance(it, Iterator)) # True print(next(it)) # 1 print(next(it)) # 2这段代码的关键在iter(lst)这一步它调用了 list 的__iter__()返回一个 list_iterator 对象。list 本身没有__next__()所以不能直接next(lst)。面试时如果只答“list 可迭代但不是迭代器”面试官会追问“那 for 循环怎么工作的”——for 循环先调iter()拿到迭代器再反复调next()直到捕获StopIteration。把这条链路说清楚这题才算过关。2.2 生成器的两种创建方式与内存优势生成器有两种产生方式列表生成式加括号g1 (x for x in range(10))或者在函数里用yield。yield的核心是“暂停并保留状态”函数执行到yield时返回值并挂起下次next()从挂起点继续。def fib(max): n, a, b 0, 0, 1 while n max: yield b # 暂停在这里返回 b a, b b, a b # 下次 next() 从这里继续 n n 1 return done g fib(8) for val in g: print(val) # 1 1 2 3 5 8 13 21面试官最想听的是“为什么生成器省内存”列表会一次性把所有元素算出来放进内存生成器只在每次next()时计算当前值用完即弃。处理大文件逐行读取、无限序列、流式数据时生成器是标准答案。注意return done的值不会通过next()返回而是包在StopIteration异常里这是很多人翻车的细节。2.3 真题演练手写一个生成器实现斐波那契面试题常要求“不用列表手写斐波那契生成器”。答题思路定义函数用yield返回当前值用变量交换更新状态。上面fib函数就是标准写法。如果面试官加一句“限制最大长度”就在while里加n max条件。如果问“生成器能不能反向遍历”答案是默认不能因为生成器是单向的需要先转成列表或自己实现缓存。提示面试时被问到“生成器和迭代器哪个更省内存”不要只答“生成器”要补一句“迭代器本身不承诺省内存比如 list_iterator 背后还是整个列表生成器才是惰性计算”。3. 高频考点可变类型、深浅拷贝与参数传递3.1 可变与不可变对象的判定标准可变对象list、dict、set。不可变对象bool、int、float、tuple、str。判定标准不是“能不能改”而是“改的时候是不是原地修改”。对不可变对象做“修改”Python 会创建新对象对可变对象做修改内存地址不变。a [1, 2, 3] print(id(a)) # 例如 140234... a.append(4) print(id(a)) # 地址不变原地修改 b hello print(id(b)) b b world # 创建了新字符串 print(id(b)) # 地址变了面试官问“tuple 里放 list 能不能改”答案是能改 list 里的元素但不能改 tuple 指向的 list 引用。这题考的是“不可变”的边界tuple 的不可变是指它持有的引用不可变不是引用指向的对象不可变。3.2 赋值、浅拷贝、深拷贝的差异与代码验证赋值只是创建新引用两个变量指向同一个对象。浅拷贝copy.copy创建新对象但内部元素还是原对象的引用。深拷贝copy.deepcopy递归复制所有层级完全独立。import copy origin [[1, 2], [3, 4]] assign origin shallow copy.copy(origin) deep copy.deepcopy(origin) origin[0][0] 99 print(assign) # [[99, 2], [3, 4]] 跟着变 print(shallow) # [[99, 2], [3, 4]] 内层还是同一个引用 print(deep) # [[1, 2], [3, 4]] 完全独立这段代码是面试白板题的高频版本。答题时先画引用关系assign和origin是同一个盒子shallow是新盒子但里面装的是原盒子的内层引用deep是新盒子加新内层。面试官追问“什么时候用深拷贝”答“需要完全隔离修改、或者对象里有嵌套可变结构且不想互相影响时”。3.3 真题演练函数默认参数的坑def add_item(item, lst[]): lst.append(item) return lst print(add_item(1)) # [1] print(add_item(2)) # [1, 2] 不是 [2]默认参数在函数定义时只求值一次所以lst[]是同一个列表。正确写法是lstNone函数体内判断if lst is None: lst []。这题考的是“可变默认参数”这个经典失分点面试官想看你有没有踩过这个坑。注意浅拷贝只复制最外层内层嵌套的可变对象仍然共享。深拷贝遇到循环引用会递归处理但性能开销大不是所有场景都该用。4. 高频考点GIL、多线程与多进程的选择4.1 GIL 是什么、为什么存在GILGlobal Interpreter Lock是 CPython 解释器层面的全局锁确保同一时刻只有一个线程执行 Python 字节码。它不是 Python 语言特性而是 CPython 实现多线程时的同步机制。设计初衷是单核时代高效利用 CPU并保护解释器内部数据结构如引用计数的线程安全。面试官问“GIL 加在哪一层”标准答案是“加在解释器层不是语言层也不是操作系统层”。因为 Python 调用的原生线程由 C 语言控制Python 虚拟机无法直接管理只能在解释器这一层加锁。4.2 多线程、多进程、IO 密集与 CPU 密集的对应关系GIL 导致多线程无法利用多核做并行计算但 IO 操作时会释放 GIL所以 IO 密集型任务用多线程仍然有效。CPU 密集型任务要用多进程multiprocessing绕过 GIL。任务类型推荐方案原因IO 密集网络、文件多线程 / asyncioIO 等待时释放 GIL线程切换开销小CPU 密集计算、图像多进程每个进程独立解释器各自持有 GIL混合型多进程 多线程进程间并行计算进程内并发 IOimport multiprocessing def cpu_task(n): return sum(i * i for i in range(n)) if __name__ __main__: with multiprocessing.Pool(4) as pool: results pool.map(cpu_task, [10**6] * 4) print(results)这段代码用进程池把四个计算任务分到四个进程每个进程有独立的 GIL真正并行。注意if __name__ __main__在 Windows 和 macOS 上是必须的否则子进程会重复导入主模块导致递归创建进程。4.3 真题演练Python 多线程能不能提速面试题“我有一个下载 100 个网页的任务用多线程能提速吗”答题思路能因为网络 IO 会释放 GIL线程在等待响应时其他线程可以继续发起请求。但如果任务是“对 100 个大数组做排序”多线程不能提速要用多进程。面试官最想听的是“区分 IO 密集和 CPU 密集”这个判断标准而不是背“GIL 让多线程变鸡肋”。提示Python 3.x 用计时器替代了 Python 2.x 的 ticks 计数器达到时间阈值后释放 GIL。但无论 2.x 还是 3.xGIL 都没有被移除多核并行仍然要靠多进程。5. 易错点五道高频翻车题与正确答法5.1 错把“可迭代”当“迭代器”错在哪回答“list 是迭代器”。为什么错list 只有__iter__()没有__next__()不满足迭代器协议。怎么答对先给定义——可迭代对象实现__iter__迭代器同时实现__iter__和__next__再举例子——list 是可迭代对象iter(list)返回的才是迭代器最后补一句“生成器是迭代器的一种”。5.2 混淆浅拷贝和深拷贝的适用场景错在哪认为copy.copy能完全隔离修改。为什么错浅拷贝只复制最外层容器内层嵌套对象仍是共享引用。怎么答对画两层结构——外层新对象内层旧引用深拷贝递归复制所有层级。面试时主动说“如果对象只有一层浅拷贝和深拷贝效果一样有嵌套时必须用 deepcopy”。5.3 默认参数用可变对象错在哪def f(lst[])然后往里 append。为什么错默认参数在函数定义时求值一次之后所有调用共享同一个列表。怎么答对默认值用None函数体内判断并初始化同时说明“这是 Python 的常见陷阱不是 bug是设计如此”。5.4 认为多线程一定能利用多核错在哪说“Python 多线程可以并行计算”。为什么错GIL 保证同一时刻只有一个线程执行字节码多核 CPU 上多线程计算任务反而因切换开销变慢。怎么答对先区分 IO 密集和 CPU 密集IO 密集用多线程CPU 密集用多进程再补一句“GIL 是 CPython 的实现细节不是 Python 语言规范”。5.5 装饰器忘记保留原函数元信息错在哪写装饰器直接return inner导致原函数的__name__、__doc__丢失。为什么错inner覆盖了原函数元信息没有传递。怎么答对用functools.wraps装饰inner保留原函数名称和文档字符串。import functools def log(func): functools.wraps(func) # 保留 func 的元信息 def inner(*args, **kwargs): print(fcall {func.__name__}) return func(*args, **kwargs) return inner log def greet(name): 打招呼 return fhello {name} print(greet.__name__) # greet不是 inner面试时如果被问到“装饰器怎么写”先写闭包结构再加functools.wraps最后说明“装饰器本质是接收函数返回函数的闭包”。6. 答题技巧从八股文到面试现场的转化方法面试官问八股文不是要你背定义是要看你有没有“用过”和“想过”。我自己的习惯是每道题先给一句话结论再给一个代码或场景例子最后补一个易错点。比如被问“深拷贝浅拷贝”先说“浅拷贝只复制外层深拷贝递归复制”再写三行代码演示最后补“默认参数用可变对象是同类坑”。这样答面试官会觉得你不仅背过还踩过坑。另一个技巧是“主动暴露边界”。被问 GIL不要只答“多线程不能并行”要补“IO 密集仍然有效CPU 密集用多进程GIL 是 CPython 实现不是语言规范”。被问生成器不要只答“省内存”要补“生成器是迭代器但迭代器不一定是生成器return值包在StopIteration里”。面试官最想听的是你知不知道“什么时候不适用”。押题方向上迭代协议、GIL、深浅拷贝、装饰器、可变默认参数这五个点覆盖了八成以上的 Python 八股文面试。每个点准备一个定义、一个代码例子、一个易错点就能应对大部分追问。如果面试官问“Py2 和 Py3 的区别”重点说print函数化、默认编码 UTF-8、range返回迭代器、除法返回 float、str是 unicode。这些差异背后是“Python 3 更一致、更少隐式行为”的设计取向。从那以后我每次准备面试都强制走一遍“定义 代码 易错点”的三段式把每个考点压缩成三句话。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?