← 返回知识库
后端工程PythonGILasyncio装饰器生成器手写题

Python / AI 速成背诵笔记

GIL 与并发三选一、async/await、装饰器、生成器、Pydantic/FastAPI、5 道必默写手写题,以及 AI/LLM 原理与后端速记。

设计原则:只背面试概率高 + 能扛追问的内容。每节 = 一句话结论 + 可展开要点。 对照你的前端背景做类比,理解快一倍。


Part 1 · Python 高频(D3-D5)

1.1 GIL 与并发三选一

  • 结论:CPython 有 GIL,同一时刻只有一个线程执行字节码;CPU 密集多线程无加速,IO 密集有用。
  • 选型:CPU 密集 → 多进程 / C 扩展;IO 密集 → asyncio / 线程池。
  • 类比前端(讲清楚版)
    • JS 无锁的串行:主线程只有一个执行流 → 无竞态 → 不需要锁;异步靠事件循环交错(假并发)
    • Python 多线程 有锁的受限并行:threading 是真 OS 线程(多执行流真实存在),但 GIL 强制同一时刻只跑一个 → CPU 密集被串行化;IO 等待时 GIL 释放 → 多线程对 IO 有用
    • asyncio ≈ 浏览器事件循环:单线程 + 事件循环(await 挂起/恢复)——asyncio 是 Python 里最像 JS 的模型
    • 一句话:JS 无锁因为没多执行流;Python 有锁因为多线程真存在但被 GIL 限速;CPU 并行要换多进程

1.2 async/await 原理

  • 单线程事件循环:取协程执行 → 遇 await 挂起 → IO 完成恢复。
  • await = 把控制权交回事件循环,等结果。
  • FastAPI 同步端点跑线程池,async 端点跑事件循环;LangGraph 图执行走 ainvoke(异步)。

1.3 装饰器

  • @decoratorf = decorator(f);函数接收函数返回函数。
  • 带参装饰器 = 三层嵌套(外函数收参 → 中函数收 fn → 内函数包装)。
  • 必须写 functools.wraps(fn) 保留元信息。
  • 用途:重试、日志、鉴权、缓存。

1.4 生成器 / 迭代器

  • 迭代器:__iter__ + __next__;生成器:含 yield 的函数,惰性求值。
  • yield 暂停保存状态,next() 恢复;省内存处理大数据流。
  • 你项目的文档分块器就是生成器思想。

1.5 常用区别速记

对比结论
== vs is比值 vs 比身份;小整数/短字符串有缓存池,勿依赖 is
深拷贝 vs 浅拷贝浅拷贝只复制外层;copy.deepcopy 递归
*args / **kwargs位置参数→元组 / 关键字参数→字典
list vs tuple可变 vs 不可变;tuple 可作 dict key
dict vs defaultdict缺 key 自动给默认值
@staticmethod vs @classmethod不传 self / 传 cls

1.6 Pydantic / FastAPI

  • Pydantic:基于类型注解的校验+序列化;FastAPI 用它做请求体验证、响应模型、自动 OpenAPI 文档。
  • 类比前端:zod + joi 合体,但和类型系统原生集成。

1.7 手写题模板(默写 5 题)

# 1. 线程安全单例
import threading
class Singleton:
    _instance = None
    _lock = threading.Lock()
    def __new__(cls):
        if cls._instance is None:
            with cls._lock:
                if cls._instance is None:
                    cls._instance = super().__new__(cls)
        return cls._instance

# 2. 装饰器(带参)——必背版本,注意 raise 与退避
import functools, time
def retry(times=3, base_delay=0.5):
    def deco(fn):
        @functools.wraps(fn)
        def wrapper(*a, **kw):
            last = None
            for i in range(times):
                try: return fn(*a, **kw)
                except Exception as e:
                    last = e
                    if i < times - 1: time.sleep(base_delay * 2 ** i)   # 指数退避
            raise last          # 耗尽必须抛异常,不能静默返回 None(面试常考的坑)
        return wrapper
    return deco

# 3. 生成器斐波那契
def fib():
    a, b = 0, 1
    while True:
        yield a
        a, b = b, a + b

# 4. LRU Cache(OrderedDict)
from collections import OrderedDict
class LRU:
    def __init__(self, cap):
        self.cap = cap; self.d = OrderedDict()
    def get(self, k):
        if k not in self.d: return -1
        self.d.move_to_end(k); return self.d[k]
    def put(self, k, v):
        if k in self.d: self.d.move_to_end(k)
        self.d[k] = v
        if len(self.d) > self.cap: self.d.popitem(last=False)

Part 2 · AI/LLM 原理(D6-D8)

2.1 Tokenizer

  • 最小文本单位;BPE 子词切分;中文单字信息密度高 → 同样内容 token 更多 → 更贵更慢。

2.2 Embedding

  • 文本 → 高维向量;语义近 → 向量近;相似度用余弦(cosine)。
  • RAG:query 和文档块都向量化,检索取 top-K。

2.3 Transformer 注意力(Q/K/V)

  • 生成每个词时关注输入其他词:Q 与 K 算相似度(点积)→ softmax 权重 → 加权求和 V。
  • 多头 = 多组并行捕捉不同关系。
  • 不需要推公式,能讲清"Q查K,权重加权V"即可。

2.4 temperature / top_p

  • temperature:高→更随机更有创造性;低→更确定(写代码用低)。
  • top_p:只从累计概率 p 的 token 采样(核采样)。

2.5 幻觉与防御(你的强项,必讲)

  • 幻觉 = 看似合理但错误/编造。
  • 四层防御(项目里都有):① RAG 引用溯源 来源N;② 审核循环校验引用;③ 提示约束(未覆盖标"存疑");④ HITL 人工兜底。
  • 加分:评测集 + LLM-as-judge 做持续回归。

2.6 RAG 链路 + chunk(完整工程版 → 详见题库 C6)

  • 链路八步:解析 → 清洗 → 分块 → 向量化/存储 → 检索 → 重排 → 注入 prompt → 生成+引用溯源。
  • chunk 大小:200-500 token + overlap 10-20% 是常见起点;按内容类型定(FAQ 一条一块,长文按语义段)。
  • 项目落地记忆卡(每步一句话):
    1. 解析:Seam 三层(spec+impl),MinerU 可选增强、PlainText 兜底,工厂模式
    2. 清洗:统一换行符 → 合并空格 → 压缩空行(保留 \n\n 段落边界)→ 去 HTML 标签/URL → 去页码行(正则)→ Counter 统计高频短行(出现≥3次且≤30字符 = 页眉页脚/水印,整行删) → 去空行得干净段落;清洗在解析层做(一次性确定性、好测试),chunker 只认干净段落
    3. 分块:段落边界优先聚合,超长段强制切 + overlap 回退;别字符硬切(切断因果)
    4. 存储:Phase 1 零依赖关键词检索(中文 2-gram + 英文单词命中计数)→ Phase 2 再接向量库;MySQL 三表(base/doc/chunk,chunk 带 seq 还原顺序)
    5. 检索:返回结构化素材 {title/snippet/source/score},对齐 research 节点输入
    6. 重排:小数据量用分数排序够用;数据量大了再上专门 rerank("召回别错过,重排别误伤")
    7. 注入:编号 + 强制 来源N 标记,按分数降序、截断低分保 source
    8. 生成+校验:审核节点反向核对引用是否越界/一致——检索→引用→校验形成证据链,才是 RAG 防幻觉的工程闭环
  • 加分一句话:"RAG 落地效果由三段决定:检索质量(查得到)、注入格式(读得懂)、引用校验(敢信它)。"

2.7 Agent vs RAG

  • RAG = 检索增强生成,单轮能力,减少幻觉。
  • Agent = 能规划 + 调工具 + 多步决策的闭环;RAG 是 Agent 的一个组件。

2.8 Function Calling(完整机制版)

一句话:把工具的"说明书"(名称/描述/参数 JSON Schema)随 prompt 发给模型 → 模型不直接执行,而是输出结构化的"我要调 tool_x,参数是 {...}" → 程序解析并执行真实函数 → 把结果以 tool 消息回传 → 模型基于结果继续生成。

完整往返(OpenAI/DeepSeek 兼容格式,背熟)

① 请求messages 数组里追加一个 tools 参数(工具定义,JSON Schema):

// POST /v1/chat/completions
{
  "model": "deepseek-chat",
  "messages": [
    {"role": "system", "content": "你是门店助手,可调用工具查天气"},
    {"role": "user", "content": "明天广州会下雨吗?"}
  ],
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "weather_query",
        "description": "查询指定城市的天气",
        "parameters": {
          "type": "object",
          "properties": {
            "city": {"type": "string", "description": "城市名"},
            "days": {"type": "integer", "enum": [1, 3, 7]}
          },
          "required": ["city"]
        }
      }
    }
  ]
}

② 模型响应(它不会直接答,而是要求调工具):

{
  "choices": [{
    "message": {
      "role": "assistant",
      "content": null,
      "tool_calls": [
        {
          "id": "call_abc123",
          "type": "function",
          "function": {
            "name": "weather_query",
            "arguments": "{\"city\": \"广州\", \"days\": 1}"
          }
        }
      ]
    }
  }]
}

③ 程序执行真实工具,把结果以 role: "tool" 回传:

{
  "messages": [
    {"role": "system", "content": "你是门店助手,可调用工具查天气"},
    {"role": "user", "content": "明天广州会下雨吗?"},
    {"role": "assistant", "content": null,
     "tool_calls": [{"id": "call_abc123", "type": "function",
       "function": {"name": "weather_query", "arguments": "{\"city\": \"广州\", \"days\": 1}"}}]},
    {"role": "tool", "tool_call_id": "call_abc123", "content": "{\"city\":\"广州\",\"tomorrow\":\"小雨 20-24℃\"}"}
  ],
  "tools": ["...同①..."]
}

④ 模型拿到工具结果后,才组织最终回答:"明天广州有小雨,记得提醒门店做雨天促销、主推根茎类蔬菜。"

Python 侧循环(核心代码,能默写)

def run_with_tools(messages, tools):
    # 第一轮:可能返回 tool_call,也可能直接回答
    resp = client.chat.completions.create(model="deepseek-chat", messages=messages, tools=tools)
    msg = resp.choices[0].message

    # 有 tool_call → 执行 → 回传 → 再请求(循环直到无 tool_call/达到上限)
    while msg.tool_calls:
        messages.append(msg)                    # 把 assistant 的 tool_call 请求加进历史
        for tc in msg.tool_calls:               # 允许一次多个工具调用
            result = exec_tool(tc.function.name, json.loads(tc.function.arguments))
            messages.append({"role": "tool", "tool_call_id": tc.id, "content": json.dumps(result)})
        resp = client.chat.completions.create(model="deepseek-chat", messages=messages, tools=tools)
        msg = resp.choices[0].message
    return msg.content

关键工程点(面试加分)

  • 模型只"提议"不执行:安全边界——真正执行在程序侧(可加白名单/审批)
  • arguments 是 JSON 字符串:必须 json.loads 解析,解析失败/参数非法走降级(不崩流水线)
  • 历史必须完整回传:assistant 的 tool_call 和 tool 的结果都要进 messages,否则模型"失忆"
  • 循环上限:tool_calls 可能连环触发,要设最大轮数(防死循环烧 token)——项目里类似"审核回写 ≤3 轮"的同一思想
  • 多工具并行:一次响应可带多个 tool_calls,逐个执行后一起回传

为什么用 Function Calling(好处,必答)

  • 突破知识截止:模型训练数据有截止日期,工具能取实时数据(天气/库存/竞品/用户画像)——AI 小灵 research 节点接 Tavily 联网搜索就是这个
  • 突破能力边界:模型只会生成文本,无法自己发请求/查库/操作业务系统——Function Calling 让 LLM 变成"指挥中枢",执行交给程序(发 API/SQL/发布)
  • 准确可信(关键):比"把工具描述写进 prompt 让模型自己编结果"可靠得多——参数由 JSON Schema 结构化约束,输出可解析、可校验,不会出现模型幻觉出一串假数据
  • 安全可管控:"模型只提议、程序才执行"——执行前可过白名单/权限/HITL 审批(项目里 MCP autoApprove 豁免 + 其余工具走人工确认),AI 不能越权乱动
  • 可观测可留痕:每次 tool_call 的参数和结果都能落事件日志——调了什么工具、传了什么参、返回了什么都可回放,防幻觉/追责都有据可查(呼应项目 append-only 事件日志)
  • 组合能力:Agent = LLM 决策 + Function Calling 执行 + 循环控制——没有 Function Calling 就没有真正的 Agent,只有"聊天机器人"

2.9 MCP

  • 标准化"模型 ↔ 工具/数据"通信;Server 暴露工具,Client 连接调用;传输 stdio / streamable HTTP。
  • 工具名 {server}__{tool};autoApprove 白名单免确认。

Part 3 · 后端速记(D9-D10)

3.1 MySQL

  • B+ 树索引:叶子存数据且有序链表,范围查询高效。
  • 最左前缀:联合索引 (a,b,c),条件必须从最左列开始。
  • 覆盖索引:查询列都在索引里,免回表。
  • ACID:原子/一致/隔离/持久。
  • 隔离级别:读未提交 → 读已提交 → 可重复读(MySQL 默认) → 串行化;对应脏读/不可重复读/幻读。(易错:MySQL 默认是可重复读,不是读已提交)
  • 慢查询:EXPLAIN 看 type/key/rows。

3.2 Redis(面试必问三连)

  • 穿透:查不存在的数据 → 布隆过滤器 / 缓存空值。
  • 击穿:热点 key 过期瞬间 → 互斥锁 / 逻辑过期。
  • 雪崩:大量 key 同时过期 → 过期时间加随机值 / 集群。
  • 分布式锁:SETNX + 过期时间 + 释放校验(防误删)。

3.3 FastAPI vs Flask vs Django

  • FastAPI:异步原生 + Pydantic 校验 + 自动文档;契合 LangGraph 异步生态。
  • Flask:轻量同步;Django:全家桶重型。

3.4 认证 / 安全

  • JWT:无状态、验签不查库;缺点无法主动失效 → 短过期 + refresh token;必须 HTTPS。
  • RBAC:用户→角色→权限点;你项目 admin 路由守卫 + 角色鉴权。
  • 密钥:Fernet 对称加密落库,opaque at rest / plaintext at use;主密钥走环境变量。

3.5 SSE vs WebSocket

  • SSE:单向(服务端→客户端)、HTTP 复用、自动重连;够用且简单。
  • WebSocket:双向全双工;需要双向/实时交互时用。
  • 你项目:服务端推任务事件 → SSE 足够。

Part 4 · 前端保底(G,最低优先级)

  • Promise.all / 防抖节流 / 深拷贝:手写要会。
  • React 优化:memo / useMemo / useCallback / 虚拟列表 / 懒加载。
  • HTTP 缓存:强缓存(Cache-Control/Expires) vs 协商缓存(ETag/Last-Modified)。
  • 微前端 qiankun:JS 沙箱、样式隔离、应用通信。
  • Vite vs Webpack:ESM 原生 vs 打包;Tree-shaking 原理。

Part 5 · 一周记忆节奏

内容自测方式
D3Python 1.1-1.5 + 手写题 1-3默写手写题
D4Python 1.6-1.7 + 手写题 4-5白板写 LRU
D5Python 全量重背给自己讲 GIL/异步
D6AI 2.1-2.5讲防幻觉四层
D7AI 2.6-2.9画 RAG 链路图
D8AI 全量 + 追问模拟被追问
D9后端 3.1-3.2讲 Redis 三连
D10后端 3.3-3.5 + 前端 4对比 FastAPI/Flask
D11+全量滚动复习每天扫一遍 ⚠️ 标记