人人都会AI编程

附录 C Python 高频面试题与核心考点

更新时间:2026-07-12

本附录梳理了面试中最常出现的 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 来保留原函数的 namedoc 等元信息。
  • 面试常要求手写一个计时装饰器或登录校验装饰器。

7. 闭包是什么?闭包里变量为什么不会消失?

  • 闭包:函数内部定义了一个子函数,子函数使用了外部函数的变量,当外部函数返回子函数后,这些变量依然存在。
  • 原因是闭包会将引用的外部变量保存在 closure 属性中。
  • 经典例子:创建多个 lambda 时的共享变量陷阱(for 循环变量的延迟绑定)。

*8. argskwargs 的作用?

  • *args 将多余位置参数打包为元组。
  • **kwargs 将多余关键字参数打包为字典。
  • 常用于函数参数传递的通用接口和装饰器中。

三、生成器与迭代器

9. 迭代器协议是什么?如何自定义一个迭代器?

  • 实现了 iter()next() 方法的对象就是迭代器。
  • iter() 返回自身,next() 返回下一个元素,没有元素则抛出 StopIteration
  • 列表、字典等是可迭代对象但不是迭代器,它们具有 iter() 方法,但每次返回一个新迭代器。

10. yield 的原理和生成器的优点?

  • 包含 yield 的函数是生成器函数,调用返回生成器对象。
  • 执行到 yield 会暂停并保留状态,下次 next() 或迭代时从断点继续。
  • 优点:惰性计算,节省内存;适合处理流式数据和管道式编程。

四、面向对象

11. newinit 的区别?

  • 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 里的代码即使遇到 returnbreak 也会执行,常用于释放资源。

16. with 语句的原理是什么?自定义一个上下文管理器。

  • with 后跟的对象必须实现 enterexit 方法。
  • 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:
  • 运行时不会强制类型校验,仅作为注解,但可以配合 pydanticdataclasses 等实现数据校验。

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
  • 考察解决问题的能力,回答真实、具体、有反思即可。