本附录梳理了面试中最常出现的 Python 考点,并给出每一道题的回答要点和思路。适合用于查漏补缺和面试前快速回顾。
一、语言基础
1. Python 中有哪些可变和不可变数据类型?这在实际中有什么影响?
- 不可变:
int,float,str,tuple,frozenset,bool - 可变:
list,dict,set, 自定义对象(多数情况) - 影响① 作为函数默认参数时,可变默认参数会导致“记忆”效果(Trap);② 作为字典的键,要求可哈希(即不可变类型);③ 安全性和内存共享,不可变对象可以在多个地方安全引用。
2. is 和 == 的区别?
==比较值是否相等(调用eq)。is比较是否为同一个对象(内存地址相同)。- 常见坑:小整数(-5~256)和短字符串会驻留,可能让
is的结果看起来相等,但不能依赖。
3. 深拷贝和浅拷贝的区别?如何实现?
- 浅拷贝:只复制最外层对象,内部元素仍引用原对象。
copy.copy()或list[:]。 - 深拷贝:递归复制所有层级,内部元素也是独立副本。
copy.deepcopy()。 - 坑点:相互引用的复杂对象可能造成递归爆栈或死循环,
deepcopy内部有解决。
4. 简述 Python 的列表和元组区别,各自适用场景?
- 列表可变,元组不可变。
- 元组占用内存更小,可以作为字典键。列表适合频繁增删改的数据集合。
- 实际用法:函数返回多个值时常用元组;需要保护数据不被意外修改时用元组。
5. 字典的底层实现原理?为什么键必须是可哈希的?
- 底层基于哈希表,通过键的 hash 值定位存储位置,解决冲突用开放寻址法。
- 键必须可哈希(
hash()可用且不可变)才能快速查找,并且能保证键的唯一性和稳定性。 - Python 3.7+ 字典保持插入顺序。
二、函数与装饰器
6. 装饰器的本质是什么?如何编写一个保留原函数信息的装饰器?
- 装饰器本质上是一个接受函数作为参数并返回新函数的高阶函数,实现不修改原函数代码却增加功能。
- 标准写法使用
functools.wraps来保留原函数的name和doc等元信息。 - 面试常要求手写一个计时装饰器或登录校验装饰器。
7. 闭包是什么?闭包里变量为什么不会消失?
- 闭包:函数内部定义了一个子函数,子函数使用了外部函数的变量,当外部函数返回子函数后,这些变量依然存在。
- 原因是闭包会将引用的外部变量保存在
closure属性中。 - 经典例子:创建多个 lambda 时的共享变量陷阱(for 循环变量的延迟绑定)。
*8. args 和 kwargs 的作用?
*args将多余位置参数打包为元组。**kwargs将多余关键字参数打包为字典。- 常用于函数参数传递的通用接口和装饰器中。
三、生成器与迭代器
9. 迭代器协议是什么?如何自定义一个迭代器?
- 实现了
iter()和next()方法的对象就是迭代器。 iter()返回自身,next()返回下一个元素,没有元素则抛出StopIteration。- 列表、字典等是可迭代对象但不是迭代器,它们具有
iter()方法,但每次返回一个新迭代器。
10. yield 的原理和生成器的优点?
- 包含
yield的函数是生成器函数,调用返回生成器对象。 - 执行到
yield会暂停并保留状态,下次next()或迭代时从断点继续。 - 优点:惰性计算,节省内存;适合处理流式数据和管道式编程。
四、面向对象
11. new 和 init 的区别?
new是类方法,负责创建对象并返回实例;init是对已存在的实例进行初始化。- 单例模式常通过重写
new实现。
12. MRO 是什么?super() 的工作机制?
- MRO(Method Resolution Order)决定多继承时方法和属性的查找顺序,使用 C3 线性化算法。
super()并不是直接调用父类,而是按 MRO 顺序找到当前调用者的下一个类,避免硬编码父类名,也解决了钻石继承问题。
13. @staticmethod、@classmethod、普通实例方法的区别?
- 实例方法:第一个参数
self,绑定实例,可以访问实例属性和类属性。 - 类方法:第一个参数
cls,绑定类,可以访问和修改类属性,常用于工厂方法。 - 静态方法:不需要类或实例的隐式参数,只是一个普通函数挂在该类的命名空间中,用于逻辑分组。
14. 如何用 slots 优化内存?有什么限制?
- 在类中定义
slots元组,明确声明实例属性,会让 Python 不再为每个实例维护dict,节省内存并提高属性访问速度。 - 限制:不能动态添加新属性;子类需重新定义
slots,否则又会出现dict;部分特性(如弱引用)需要额外声明weakref插槽。
五、异常与文件操作
15. try/except/else/finally 的执行顺序?
try块若无异常,执行完后进入else,最后必定执行finally。- 有异常时,跳到匹配的
except,执行完后再执行finally。 finally里的代码即使遇到return或break也会执行,常用于释放资源。
16. with 语句的原理是什么?自定义一个上下文管理器。
with后跟的对象必须实现enter和exit方法。enter返回资源对象(如文件句柄),exit在代码块结束时被调用,进行清理。- 也可用
contextlib.contextmanager装饰生成器函数简化实现。
六、内存管理与解释器
17. Python 的垃圾回收机制是怎样的?
- 主要手段是引用计数,对象引用计数降为 0 立即回收。
- 对循环引用采用标记-清除算法,找到不可达的对象并回收。
- 分代回收:将对象分为三代,新对象在新生代,存活得越久越可能被移动到老年代,减少全量扫描频率。
18. 什么是 GIL?它对多线程有什么影响?如何绕过?
- GIL(Global Interpreter Lock)是 CPython 解释器中的全局锁,保证同一时刻只有一个线程执行 Python 字节码。
- 它导致 CPU 密集型多线程程序无法利用多核,甚至会因锁争用变慢。
- 绕过方案:使用多进程(
multiprocessing);用 C 扩展执行耗时计算时主动释放 GIL;使用异步编程处理 IO 密集型任务;或换用 PyPy/JPython。
七、并发编程
19. 多线程、多进程、协程的区别和适用场景?
- 多线程:共享内存,受 GIL 限制,适合 IO 密集型任务(网络请求、文件读写)。
- 多进程:每个进程有独立内存空间,可绕过 GIL,适合 CPU 密集型任务,但开销大。
- 协程:用户态轻量线程,通过
async/await和事件循环切换,高并发 IO 场景性能极佳。 - 技术选型:一般 IO 密集用
asyncio或线程池,CPU 密集用进程池。
20. asyncio 的事件循环是什么?await 做了什么?
- 事件循环是一个循环不断取出并执行已就绪的协程任务,处理 IO 就绪、延时等事件。
await后跟一个可等待对象(协程/Task/Future),会将控制权交还给事件循环,直到该可等待对象完成,才继续执行后续代码。- 整个流程是非阻塞的,同一线程内可调度数千协程。
八、常用库与工具
21. 列举几个你常用的标准库模块,并说明用途。
os/sys:系统交互、命令行参数datetime:日期时间处理re:正则表达式匹配与替换json:JSON 序列化/反序列化collections:高级数据结构(Counter, defaultdict, deque 等)itertools:高效迭代器组合logging:日志记录unittest/pytest:测试框架
22. 如何管理 Python 项目的依赖和虚拟环境?
- 虚拟环境:
venv(内置) 或conda,隔离项目依赖,防止版本冲突。 - 依赖管理:
pip freeze > requirements.txt记录版本,或使用poetry/pipenv生成锁文件实现更可靠的重建。 - 最佳实践:每个项目一个独立虚拟环境,依赖锁定精确版本。
九、代码质量与工程化
23. PEP 8 是什么?列举几条你记忆深刻的规范。
- Python 代码风格指南。
- 常见规范:缩进4个空格;行宽不超过79字符;导入每个独占一行;空行分割函数和类;函数名小写下划线分隔;类名首字母大写驼峰;常量全大写等。
24. 类型提示(Type Hints)有什么用?你是否用过?
- 增强代码可读性,IDE 可提供更好的补全和静态检查(用 mypy)。
- 示例:
def greet(name: str) -> str:。 - 运行时不会强制类型校验,仅作为注解,但可以配合
pydantic、dataclasses等实现数据校验。
25. 怎么在 Python 中实现单例模式?
- 利用
new方法:
class Singleton:
_instance = None
def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = super().__new__(cls)
return cls._instance
- 也可用模块级别的变量(模块天然单例)、装饰器或元类实现。
26. Python 中如何调试程序?
- 最基本:
print()和logging。 - 专业:用
pdb设置断点,或在代码中插入breakpoint()(Python 3.7+)。 - IDE 集成调试:PyCharm/VS Code 可视化断点、步进、查看变量。
十、开放性问题
27. 请解释一下 Python 中的“胶水语言”特性,并结合实际举例。
- Python 可以很方便地调用 C/C++ 编写的扩展,也可以被嵌入到其他语言中作为脚本引擎。
- 实例:用 Cython 优化性能瓶颈;用
ctypes直接调用操作系统的 DLL;使用subprocess调用外部程序组合成管道;把 Python 脚本作为大型 Java 系统的胶水组件(Jython)。
28. 你认为 Python 有哪些缺点?在实际项目中如何规避?
- 运行速度较慢 —— 用 PyPy/Cython/多进程,或核心算法用 Rust/C 重写。
- GIL 限制多核并行 —— 转为多进程或协程处理。
- 动态类型导致大型项目维护成本上升 —— 使用类型提示 + mypy 做静态检查。
- 移动端和桌面开发生态偏弱 —— 如果有这类需求尽量选择其他技术栈,或只用 Python 做后端服务。
29. 在以往项目中,你遇到过的最大 Python 坑是什么?如何解决的?
- 示例答案:循环中使用
lambda或闭包捕获变量导致意料外的结果;解决办法是使用默认参数立即求值或functools.partial。 - 考察解决问题的能力,回答真实、具体、有反思即可。