后端工程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 装饰器
@decorator≡f = 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 一条一块,长文按语义段)。
- 项目落地记忆卡(每步一句话):
- 解析:Seam 三层(spec+impl),MinerU 可选增强、PlainText 兜底,工厂模式
- 清洗:统一换行符 → 合并空格 → 压缩空行(保留
\n\n段落边界)→ 去 HTML 标签/URL → 去页码行(正则)→ Counter 统计高频短行(出现≥3次且≤30字符 = 页眉页脚/水印,整行删) → 去空行得干净段落;清洗在解析层做(一次性确定性、好测试),chunker 只认干净段落 - 分块:段落边界优先聚合,超长段强制切 + overlap 回退;别字符硬切(切断因果)
- 存储:Phase 1 零依赖关键词检索(中文 2-gram + 英文单词命中计数)→ Phase 2 再接向量库;MySQL 三表(base/doc/chunk,chunk 带 seq 还原顺序)
- 检索:返回结构化素材
{title/snippet/source/score},对齐 research 节点输入 - 重排:小数据量用分数排序够用;数据量大了再上专门 rerank("召回别错过,重排别误伤")
- 注入:编号 + 强制 来源N 标记,按分数降序、截断低分保 source
- 生成+校验:审核节点反向核对引用是否越界/一致——检索→引用→校验形成证据链,才是 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 · 一周记忆节奏
| 天 | 内容 | 自测方式 |
|---|---|---|
| D3 | Python 1.1-1.5 + 手写题 1-3 | 默写手写题 |
| D4 | Python 1.6-1.7 + 手写题 4-5 | 白板写 LRU |
| D5 | Python 全量重背 | 给自己讲 GIL/异步 |
| D6 | AI 2.1-2.5 | 讲防幻觉四层 |
| D7 | AI 2.6-2.9 | 画 RAG 链路图 |
| D8 | AI 全量 + 追问 | 模拟被追问 |
| D9 | 后端 3.1-3.2 | 讲 Redis 三连 |
| D10 | 后端 3.3-3.5 + 前端 4 | 对比 FastAPI/Flask |
| D11+ | 全量滚动复习 | 每天扫一遍 ⚠️ 标记 |