1:is 和 == 的本质区别?什么是小整数缓存机制?

回答要点:

  • 本质区别:
    • == 比较的是值 (Value),即两个对象的内容是否相等(调用 __eq__ 方法)。
    • is 比较的是身份 (Identity),即两个对象在内存中的地址是否相同(相当于 id(a) == id(b))。
  • 小整数缓存 (Small Integer Caching):
    • Python 为了性能优化,在启动时会预先创建范围在 [-5, 256] 之间的整数对象。
    • 现象:a = 10; b = 10; a is b 返回 True。但 a = 257; b = 257; a is b 在交互式命令行中通常返回 False(注意:在 PyCharm 或脚本文件中执行可能会因为编译器优化而返回 True,面试时要区分场景)。
  • 字符串驻留 (Interning):
    • 类似的,简短的、只包含字母数字下划线的字符串也会被缓存,指向同一内存地址。

2:讲讲 async / await 的原理?asyncio 是怎么工作的?

回答要点:

  • 核心概念:asyncio 是基于事件循环 (Event Loop) 的单线程并发模型。
  • async/await:
    • async def 定义一个协程 (Coroutine),调用它不会立即执行,而是返回一个协程对象。
    • await 用于挂起当前协程,将控制权交还给事件循环,等待 await 的对象(Awaitable,如 IO 操作)完成。
  • 工作流程:
    1. 创建一个 Event Loop。
    2. 将协程注册到 Loop 中(Task)。
    3. Loop 不断循环,检测 IO 事件(如 Socket 可读写)。
    4. 当事件就绪,Loop 唤醒对应的协程继续执行,直到遇到下一个 await。
  • 优势:相比多线程,无锁竞争,上下文切换开销极小,适合高并发 IO 密集型任务。

3:functools.lru_cache 是什么?它是怎么实现缓存的?

回答要点:

  • 作用:这是一个装饰器,为函数提供 LRU (Least Recently Used,最近最少使用) 缓存策略。
  • 原理:
    • 它用一个字典缓存函数的输入参数(key)和返回值(value)。
    • 当函数被调用时,先查字典:
      • 命中:直接返回缓存值,并将该记录移到链表头部(表示最近使用)。
      • 未命中:执行函数,存入结果,若缓存已满,丢弃链表尾部(最久未使用)的记录。
  • 注意点:
    • 被装饰函数的参数必须是可哈希的 (Hashable),否则无法作为字典键。
    • maxsize 参数控制缓存大小,默认为 128。

4:什么是元类 (Metaclass)?它有什么应用场景?

回答要点:

  • 定义:元类是创建类的类。在 Python 中,一切皆对象,类本身也是对象,而元类就是用来创建这些类对象的“模版”。默认的元类是 type。
  • 关系:Instance (实例) -> Class (类) -> Metaclass (元类)。
  • 应用场景:
    • ORM 框架:如 Django 的 Model,通过元类读取类属性(字段定义),自动生成数据库映射逻辑。
    • 接口校验:在类创建时检查是否定义了特定方法。
    • 自动注册:插件系统中,自动将子类注册到全局注册表中。
  • 实现:通过继承 type 并重写 __new__ 或 __init__ 方法,然后在类定义中指定 metaclass=MyMeta。

5:collections 模块里你常用哪些数据结构?

回答要点:
面试官通常希望听到 dict/list/tuple 之外的答案:

  1. defaultdict:访问不存在的 key 时,自动调用工厂函数生成默认值(避免 KeyError,常用于计数或分组)。
  2. Counter:专门用于计数的字典,提供 most_common(n) 等方便的方法。
  3. deque:双端队列,支持线程安全的、O(1) 复杂度的两端 append/pop(list 在头部插入是 O(n))。
  4. namedtuple:给元组的元素命名,代码可读性更强(比 dict 省内存)。

在这里插入图片描述

6:什么是 Monkey Patching (猴子补丁)?有什么风险?

回答要点:

  • 定义:在运行时动态修改类或模块的行为(如替换方法、属性),而无需修改源代码。
  • 常见场景:
    • 测试 (Mocking):在单元测试中替换网络请求、数据库连接等外部依赖。
    • 热修复 (Hotfix):在不重启服务或无法修改源码库的情况下修复 Bug。
    • Gevent:通过猴子补丁将标准库的阻塞 IO 替换为非阻塞 IO。
  • 风险:
    • 隐式行为:代码逻辑变得难以追踪,排查问题困难。
    • 版本兼容性:第三方库升级后,内部实现变了,补丁可能失效或报错。
    • 冲突:多个补丁可能互相覆盖。

7:Python 的类型注解 (Type Hints) 有什么用?运行时会生效吗?

回答要点:

  • 作用:
    • 静态分析:配合 MyPy 等工具在代码运行前发现类型错误。
    • IDE 提示:让 IDE 能提供更好的代码补全和跳转。
    • 文档化:明确函数入参和返回类型,提高代码可读性。
  • 运行时:
    • 默认不生效:Python 解释器在运行时会忽略类型注解,不会进行强制类型检查(Python 依然是动态类型语言)。
    • 特例:Pydantic 或 FastAPI 等库会在运行时利用类型注解进行数据校验和序列化。

8:什么是海象运算符 (:=)?它有什么应用场景?

回答要点:

  • 引入版本:Python 3.8+。
  • 作用:赋值表达式。允许在表达式内部进行赋值,并返回该值。
  • 场景:
    • while 循环:读取文件块或正则匹配。
      # 旧写法
      chunk = file.read(1024)
      while chunk:
          process(chunk)
          chunk = file.read(1024)
      
      # 海象运算符写法
      while (chunk := file.read(1024)):
          process(chunk)
      
    • 列表推导式:避免重复计算。
      [y for x in data if (y := func(x)) > 0]
      

9:只要实现了 __call__ 方法的对象就是函数吗?

回答要点:

  • 可调用对象 (Callable):不一定是函数。任何实现了 __call__ 特殊方法的类实例,都可以像函数一样被调用(instance())。
  • 区别:
    • 普通函数是 function 类型。
    • 实现了 __call__ 的对象可以维护状态(属性),比闭包更灵活。
  • 应用:装饰器类、需要保持状态的回调函数。

10:BaseException 和 Exception 的区别?为什么不能直接 except BaseException?

回答要点:

  • 继承关系:BaseException 是所有异常的基类;Exception 是所有常规异常(非系统退出类)的基类,继承自 BaseException。
  • 关键区别:
    • SystemExit(sys.exit() 触发)、KeyboardInterrupt(Ctrl+C 触发)等是直接继承自 BaseException 的。
  • 最佳实践:
    • 业务代码应该捕获 Exception。
    • 如果捕获了 BaseException(或裸写 except:),会拦截 Ctrl+C 和系统退出信号,导致程序无法正常停止或被运维工具杀掉。

更多推荐