<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <author>
    <name>Furina</name>
  </author>
  <generator uri="https://hexo.io/">Hexo</generator>
  <id>https://www.furinaluna.top/</id>
  <link href="https://www.furinaluna.top/" rel="alternate"/>
  <link href="https://www.furinaluna.top/atom.xml" rel="self"/>
  <rights>All rights reserved 2026, Furina</rights>
  <subtitle>记录技术，分享生活</subtitle>
  <title>芙芙`s Home</title>
  <updated>2026-09-11T13:48:57.151Z</updated>
  <entry>
    <author>
      <name>Furina</name>
    </author>
    <category term="langchain" scheme="https://www.furinaluna.top/categories/langchain/"/>
    <category term="langchain" scheme="https://www.furinaluna.top/tags/langchain/"/>
    <category term="rag" scheme="https://www.furinaluna.top/tags/rag/"/>
    <content>
      <![CDATA[<h1>LangChain &amp; RAG 简单快速入门</h1><blockquote><p>可能会有问题，欢迎评论区留言</p></blockquote><hr><h2 id="目录">目录</h2><ol><li><a href="#1-%E6%A0%B8%E5%BF%83%E6%A6%82%E5%BF%B5%E9%80%9F%E8%A7%88">核心概念速览</a></li><li><a href="#2-langchain-%E5%9F%BA%E7%A1%80%E7%BB%84%E4%BB%B6">LangChain 基础组件</a></li><li><a href="#3-rag-%E5%8E%9F%E7%90%86%E8%AF%A6%E8%A7%A3">RAG 原理详解</a></li><li><a href="#4-%E5%AE%9E%E6%88%98%E6%9E%84%E5%BB%BA%E5%AE%8C%E6%95%B4%E7%9A%84-rag-%E5%BA%94%E7%94%A8">实战：构建完整的 RAG 应用</a></li><li><a href="#5-%E9%AB%98%E7%BA%A7%E6%8A%80%E5%B7%A7%E4%B8%8E%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5">高级技巧与最佳实践</a></li><li><a href="#6-%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98%E4%B8%8E%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88">常见问题与解决方案</a></li></ol><hr><h2 id="1-核心概念速览">1. 核心概念速览</h2><h3 id="什么是-LangChain？">什么是 LangChain？</h3><p><strong>LangChain</strong> 是一个用于开发大语言模型（LLM）应用的框架。它的核心思想是：<strong>把 LLM 和其他数据源连接起来，构建复杂的应用</strong>。</p><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs scss">┌─────────────────────────────────────────────────────────┐<br>│                    LangChain 应用                        │<br>├─────────────┬─────────────┬─────────────┬───────────────┤<br>│   LLM       │   Chains    │   Agents    │   Memory      │<br>│  (大脑)     │  (工作流)   │  (决策者)   │  (记忆)       │<br>└─────────────┴─────────────┴─────────────┴───────────────┘<br></code></pre></td></tr></table></figure><h3 id="什么是-RAG？">什么是 RAG？</h3><p><strong>RAG (Retrieval-Augmented Generation)</strong> = 检索 + 生成</p><figure class="highlight 1c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs 1c">用户提问 → 检索相关知识 → 把知识和问题一起给 LLM → LLM 生成答案<br><br>     ┌──────────┐    ┌──────────┐    ┌──────────┐<br>     │  提问    │ →  │  检索    │ →  │  生成    │<br>     │ <span class="hljs-string">&quot;公司法  │    │ 找到相关 │    │ LLM 结合 │</span><br>     │ 人代表？<span class="hljs-string">&quot;│    │ 法律条文 │    │ 上下文回答│</span><br>     └──────────┘    └──────────┘    └──────────┘<br></code></pre></td></tr></table></figure><p><strong>为什么需要 RAG？</strong></p><ul><li>LLM 的知识是有限的（训练数据截止时间）</li><li>LLM 会&quot;胡编乱造&quot;（幻觉问题）</li><li>你有私有数据需要利用（公司文档、数据库等）</li></ul><hr><h2 id="2-LangChain-基础组件">2. LangChain 基础组件</h2><h3 id="2-1-模型（Models）">2.1 模型（Models）</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> langchain_openai <span class="hljs-keyword">import</span> ChatOpenAI<br><span class="hljs-keyword">from</span> langchain_anthropic <span class="hljs-keyword">import</span> ChatAnthropic<br><br><span class="hljs-comment"># 初始化模型</span><br>llm = ChatOpenAI(<br>    model=<span class="hljs-string">&quot;gpt-4o-mini&quot;</span>,<br>    temperature=<span class="hljs-number">0</span>,  <span class="hljs-comment"># 0 = 确定性回答，1 = 创造性回答</span><br>    api_key=<span class="hljs-string">&quot;your-api-key&quot;</span><br>)<br><br><span class="hljs-comment"># 调用模型</span><br>response = llm.invoke(<span class="hljs-string">&quot;你好，请介绍一下自己&quot;</span>)<br><span class="hljs-built_in">print</span>(response.content)<br></code></pre></td></tr></table></figure><h3 id="2-2-提示词（Prompts）">2.2 提示词（Prompts）</h3><p><strong>Prompt Template</strong> 让你可以复用提示词结构：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> langchain_core.prompts <span class="hljs-keyword">import</span> ChatPromptTemplate<br><br><span class="hljs-comment"># 创建一个模板</span><br>prompt = ChatPromptTemplate.from_messages([<br>    (<span class="hljs-string">&quot;system&quot;</span>, <span class="hljs-string">&quot;你是一个专业的&#123;role&#125;助手&quot;</span>),<br>    (<span class="hljs-string">&quot;human&quot;</span>, <span class="hljs-string">&quot;&#123;question&#125;&quot;</span>)<br>])<br><br><span class="hljs-comment"># 使用模板</span><br>formatted = prompt.<span class="hljs-built_in">format</span>(role=<span class="hljs-string">&quot;法律&quot;</span>, question=<span class="hljs-string">&quot;什么是合同？&quot;</span>)<br><span class="hljs-comment"># 输出：</span><br><span class="hljs-comment"># System: 你是一个专业的法律助手</span><br><span class="hljs-comment"># Human: 什么是合同？</span><br></code></pre></td></tr></table></figure><h3 id="2-3-链（Chains）">2.3 链（Chains）</h3><p><strong>Chain = 多个组件的组合</strong>，形成完整的工作流。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> langchain_core.output_parsers <span class="hljs-keyword">import</span> StrOutputParser<br><span class="hljs-keyword">from</span> langchain_core.runnables <span class="hljs-keyword">import</span> RunnablePassthrough<br><br><span class="hljs-comment"># 最简单的 Chain：Prompt + LLM + Output Parser</span><br>chain = (<br>    prompt        <span class="hljs-comment"># 输入处理</span><br>    | llm         <span class="hljs-comment"># 模型处理</span><br>    | StrOutputParser()  <span class="hljs-comment"># 输出解析</span><br>)<br><br><span class="hljs-comment"># 使用 chain</span><br>result = chain.invoke(&#123;<span class="hljs-string">&quot;role&quot;</span>: <span class="hljs-string">&quot;法律&quot;</span>, <span class="hljs-string">&quot;question&quot;</span>: <span class="hljs-string">&quot;什么是合同？&quot;</span>&#125;)<br></code></pre></td></tr></table></figure><p><strong>链式调用的本质</strong>：数据像流水线一样经过每个组件</p><figure class="highlight mipsasm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs mipsasm">输入 → Prompt → <span class="hljs-keyword">LLM </span>→ Parser → 输出<br>       ↓        ↓      ↓<br>    格式化   生成    解析<br></code></pre></td></tr></table></figure><h3 id="2-4-工具（Tools）">2.4 工具（Tools）</h3><p><strong>Tools</strong> 让 LLM 能够执行具体操作（搜索、计算、调用 API 等）。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> langchain.tools <span class="hljs-keyword">import</span> tool<br><span class="hljs-keyword">from</span> langchain_community.tools <span class="hljs-keyword">import</span> DuckDuckGoSearchRun<br><br><span class="hljs-comment"># 使用现成的工具</span><br>search = DuckDuckGoSearchRun()<br>result = search.invoke(<span class="hljs-string">&quot;今天北京的天气&quot;</span>)<br><br><span class="hljs-comment"># 自定义工具</span><br><span class="hljs-meta">@tool</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">calculate_bmi</span>(<span class="hljs-params">weight: <span class="hljs-built_in">float</span>, height: <span class="hljs-built_in">float</span></span>) -&gt; <span class="hljs-built_in">float</span>:<br>    <span class="hljs-string">&quot;&quot;&quot;计算 BMI 指数。</span><br><span class="hljs-string">    </span><br><span class="hljs-string">    Args:</span><br><span class="hljs-string">        weight: 体重 (kg)</span><br><span class="hljs-string">        height: 身高 (m)</span><br><span class="hljs-string">    </span><br><span class="hljs-string">    Returns:</span><br><span class="hljs-string">        BMI 指数</span><br><span class="hljs-string">    &quot;&quot;&quot;</span><br>    <span class="hljs-keyword">return</span> weight / (height ** <span class="hljs-number">2</span>)<br><br><span class="hljs-comment"># 工具需要告诉 LLM 它能做什么</span><br><span class="hljs-built_in">print</span>(calculate_bmi.name)        <span class="hljs-comment"># &quot;calculate_bmi&quot;</span><br><span class="hljs-built_in">print</span>(calculate_bmi.description) <span class="hljs-comment"># &quot;计算 BMI 指数...&quot;</span><br></code></pre></td></tr></table></figure><h3 id="2-5-代理（Agents）">2.5 代理（Agents）</h3><p><strong>Agent = LLM + Tools</strong>，让 LLM 自主决定使用哪个工具。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> langchain.agents <span class="hljs-keyword">import</span> create_agent<br><br><span class="hljs-comment"># 创建 agent</span><br>agent = create_agent(<br>    model=<span class="hljs-string">&quot;gpt-4o-mini&quot;</span>,<br>    tools=[search, calculate_bmi],<br>    prompt=<span class="hljs-string">&quot;你是一个有帮助的助手&quot;</span><br>)<br><br><span class="hljs-comment"># agent 会自己决定：</span><br><span class="hljs-comment"># - 用户问&quot;今天天气&quot; → 使用 search 工具</span><br><span class="hljs-comment"># - 用户问&quot;70kg, 1.75m 的 BMI&quot; → 使用 calculate_bmi 工具</span><br></code></pre></td></tr></table></figure><p><strong>Agent 工作流程</strong>：</p><figure class="highlight mipsasm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs mipsasm">用户输入 → <span class="hljs-keyword">LLM </span>思考 → 决定使用工具 → 执行工具 → 得到结果 → 生成回答<br>           ↓<br>      <span class="hljs-string">&quot;我需要搜索天气信息&quot;</span><br></code></pre></td></tr></table></figure><h3 id="2-6-记忆（Memory）">2.6 记忆（Memory）</h3><p><strong>Memory</strong> 让对话有连续性，记住之前的对话内容。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> langchain.memory <span class="hljs-keyword">import</span> ConversationBufferMemory<br><br><span class="hljs-comment"># 创建记忆</span><br>memory = ConversationBufferMemory(return_messages=<span class="hljs-literal">True</span>)<br><br><span class="hljs-comment"># 保存对话</span><br>memory.save_context(&#123;<span class="hljs-string">&quot;input&quot;</span>: <span class="hljs-string">&quot;你好&quot;</span>&#125;, &#123;<span class="hljs-string">&quot;output&quot;</span>: <span class="hljs-string">&quot;你好！有什么可以帮助你的？&quot;</span>&#125;)<br>memory.save_context(&#123;<span class="hljs-string">&quot;input&quot;</span>: <span class="hljs-string">&quot;我叫小明&quot;</span>&#125;, &#123;<span class="hljs-string">&quot;output&quot;</span>: <span class="hljs-string">&quot;你好小明！&quot;</span>&#125;)<br><br><span class="hljs-comment"># 获取历史对话</span><br>history = memory.load_memory_variables(&#123;&#125;)<br><span class="hljs-comment"># 输出：[Human: 你好, AI: 你好！..., Human: 我叫小明, AI: 你好小明！]</span><br></code></pre></td></tr></table></figure><hr><h2 id="3-RAG-原理详解">3. RAG 原理详解</h2><h3 id="3-1-RAG-的核心流程">3.1 RAG 的核心流程</h3><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><code class="hljs markdown">┌─────────────────────────────────────────────────────────────────┐<br>│                        RAG 完整流程                              │<br>└─────────────────────────────────────────────────────────────────┘<br><br>【索引阶段 - 离线】<br><span class="hljs-code">    文档 → 分块 → 向量化 → 存储到向量数据库</span><br><span class="hljs-code">    </span><br><span class="hljs-code">    ┌────────┐   ┌────────┐   ┌────────┐   ┌────────────┐</span><br><span class="hljs-code">    │ PDF/   │ → │ 切成小 │ → │ 转换成 │ → │ 向量数据库 │</span><br><span class="hljs-code">    │ Word/  │   │ 段落   │   │ 向量   │   │ (FAISS等)  │</span><br><span class="hljs-code">    │ 网页   │   │        │   │        │   │            │</span><br><span class="hljs-code">    └────────┘   └────────┘   └────────┘   └────────────┘</span><br><span class="hljs-code"></span><br>【检索阶段 - 在线】<br><span class="hljs-code">    用户问题 → 向量化 → 相似度搜索 → 返回相关文档</span><br><span class="hljs-code">    </span><br><span class="hljs-code">    ┌────────┐   ┌────────┐   ┌────────┐   ┌────────┐</span><br><span class="hljs-code">    │ 用户   │ → │ 转换   │ → │ 在向量 │ → │ 找到最 │</span><br><span class="hljs-code">    │ 提问   │   │ 为向量 │   │ 库搜索 │   │ 相似的 │</span><br><span class="hljs-code">    └────────┘   └────────┘   └────────┘   └────────┘</span><br><span class="hljs-code">                                         ↓</span><br><span class="hljs-code">                                    ┌────────┐</span><br><span class="hljs-code">                                    │ 相关   │</span><br><span class="hljs-code">                                    │ 文档块 │</span><br><span class="hljs-code">                                    └────────┘</span><br><span class="hljs-code"></span><br>【生成阶段 - 在线】<br><span class="hljs-code">    问题 + 文档 → LLM → 生成答案</span><br><span class="hljs-code">    </span><br><span class="hljs-code">    ┌────────┐   ┌────────┐   ┌────────┐</span><br><span class="hljs-code">    │ 问题   │ + │ 检索到 │ → │  LLM   │ →  答案</span><br><span class="hljs-code">    │        │   │ 的文档 │   │  生成  │</span><br><span class="hljs-code">    └────────┘   └────────┘   └────────┘</span><br></code></pre></td></tr></table></figure><h3 id="3-2-核心概念解释">3.2 核心概念解释</h3><h4 id="什么是向量（Vector）？">什么是向量（Vector）？</h4><p>向量是一串数字，用来表示文本的&quot;语义&quot;。</p><figure class="highlight jboss-cli"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs jboss-cli">文本：<span class="hljs-string">&quot;我喜欢吃苹果&quot;</span><br>向量：[0.1, -0.5, 0.8, <span class="hljs-string">...</span>, 0.3]  <span class="hljs-params">(可能是 1536 维的数字)</span><br><br>文本：<span class="hljs-string">&quot;苹果很好吃&quot;</span><br>向量：[0.09, -0.48, 0.82, <span class="hljs-string">...</span>, 0.31]  <span class="hljs-params">(非常接近！)</span><br><br>文本：<span class="hljs-string">&quot;今天天气不错&quot;</span><br>向量：[-0.7, 0.2, -0.1, <span class="hljs-string">...</span>, -0.5]  <span class="hljs-params">(完全不同)</span><br></code></pre></td></tr></table></figure><p><strong>相似的内容，向量也相似</strong> → 可以用数学方法找到&quot;语义相似&quot;的文本</p><h4 id="什么是-Embedding（嵌入）？">什么是 Embedding（嵌入）？</h4><p>Embedding 模型负责把文本转换成向量。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> langchain_openai <span class="hljs-keyword">import</span> OpenAIEmbeddings<br><br>embeddings = OpenAIEmbeddings(model=<span class="hljs-string">&quot;text-embedding-3-small&quot;</span>)<br><br><span class="hljs-comment"># 把文本转换成向量</span><br>vector = embeddings.embed_query(<span class="hljs-string">&quot;我喜欢吃苹果&quot;</span>)<br><span class="hljs-built_in">print</span>(<span class="hljs-built_in">len</span>(vector))  <span class="hljs-comment"># 1536 维的向量</span><br></code></pre></td></tr></table></figure><p>常见 Embedding 模型：</p><table><thead><tr><th>模型</th><th>维度</th><th>特点</th></tr></thead><tbody><tr><td>text-embedding-3-small</td><td>1536</td><td>快速、便宜</td></tr><tr><td>text-embedding-3-large</td><td>3072</td><td>更准确</td></tr><tr><td>jina-embeddings-v2</td><td>768</td><td>开源、免费</td></tr></tbody></table><h4 id="什么是向量数据库？">什么是向量数据库？</h4><p>专门用来存储和搜索向量的数据库。</p><figure class="highlight gherkin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs gherkin">普通数据库：WHERE name = <span class="hljs-string">&quot;张三&quot;</span>  (精确匹配)<br>向量数据库：找和这个向量<span class="hljs-string">&quot;最相似&quot;</span>的向量 (语义匹配)<br><br>相似度计算（余弦相似度）：<br>similarity = cos(向量 A, 向量 B) = (A·B) / (|<span class="hljs-string">A</span>|<span class="hljs-string">×</span>|<span class="hljs-string">B</span>|<span class="hljs-string">)</span><br><span class="hljs-string"></span><br><span class="hljs-string">结果范围：0 (完全不同) ~ 1 (完全相同)</span><br></code></pre></td></tr></table></figure><p>常见向量数据库：</p><ul><li><strong>InMemoryVectorStore</strong> - 内存存储，适合学习/测试</li><li><strong>FAISS</strong> - Facebook 开源，快速</li><li><strong>Chroma</strong> - 轻量级，易上手</li><li><strong>Milvus</strong> - 专业向量数据库，适合生产</li><li><strong>Elasticsearch</strong> - 支持混合搜索</li></ul><h3 id="3-3-文档分块（Chunking）">3.3 文档分块（Chunking）</h3><p>为什么要分块？</p><ol><li>LLM 有上下文长度限制</li><li>检索时需要精确匹配相关内容</li><li>太大块包含噪声，太小块丢失上下文</li></ol><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> langchain_text_splitters <span class="hljs-keyword">import</span> RecursiveCharacterTextSplitter<br><br>text_splitter = RecursiveCharacterTextSplitter(<br>    chunk_size=<span class="hljs-number">500</span>,      <span class="hljs-comment"># 每块最多 500 个字符</span><br>    chunk_overlap=<span class="hljs-number">100</span>,   <span class="hljs-comment"># 块之间重叠 100 个字符（保持上下文连续）</span><br>    length_function=<span class="hljs-built_in">len</span>, <span class="hljs-comment"># 计算长度的函数</span><br>)<br><br><span class="hljs-comment"># 示例</span><br>text = <span class="hljs-string">&quot;这是一段很长的文本...&quot;</span> * <span class="hljs-number">100</span><br>chunks = text_splitter.split_text(text)<br><br><span class="hljs-comment"># 输出：[块 1, 块 2, 块 3, ...]</span><br></code></pre></td></tr></table></figure><p><strong>分块策略选择</strong>：</p><table><thead><tr><th>场景</th><th>chunk_size</th><th>chunk_overlap</th></tr></thead><tbody><tr><td>简短问答</td><td>200-400</td><td>50-100</td></tr><tr><td>技术文档</td><td>500-1000</td><td>100-200</td></tr><tr><td>法律合同</td><td>1000-2000</td><td>200-300</td></tr><tr><td>书籍章节</td><td>500-800</td><td>100-150</td></tr></tbody></table><hr><h2 id="4-实战：构建完整的-RAG-应用">4. 实战：构建完整的 RAG 应用</h2><h3 id="4-1-最简-RAG-示例（10-行代码）">4.1 最简 RAG 示例（10 行代码）</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> langchain_core.vectorstores <span class="hljs-keyword">import</span> InMemoryVectorStore<br><span class="hljs-keyword">from</span> langchain_openai <span class="hljs-keyword">import</span> OpenAIEmbeddings, ChatOpenAI<br><span class="hljs-keyword">from</span> langchain_core.documents <span class="hljs-keyword">import</span> Document<br><span class="hljs-keyword">from</span> langchain_core.runnables <span class="hljs-keyword">import</span> RunnablePassthrough<br><span class="hljs-keyword">from</span> langchain_core.output_parsers <span class="hljs-keyword">import</span> StrOutputParser<br><span class="hljs-keyword">from</span> langchain_core.prompts <span class="hljs-keyword">import</span> ChatPromptTemplate<br><br><span class="hljs-comment"># 1. 准备文档</span><br>docs = [<br>    Document(page_content=<span class="hljs-string">&quot;苹果公司 2024 年营收达到 5000 亿美元&quot;</span>),<br>    Document(page_content=<span class="hljs-string">&quot;苹果的总部在美国加州库比蒂诺&quot;</span>),<br>    Document(page_content=<span class="hljs-string">&quot;苹果的代表产品有 iPhone, iPad, Mac&quot;</span>),<br>]<br><br><span class="hljs-comment"># 2. 创建向量存储</span><br>vectorstore = InMemoryVectorStore.from_documents(docs, OpenAIEmbeddings())<br>retriever = vectorstore.as_retriever(k=<span class="hljs-number">2</span>)  <span class="hljs-comment"># 每次检索 2 个最相关的</span><br><br><span class="hljs-comment"># 3. 创建提示词</span><br>prompt = ChatPromptTemplate.from_template(<br>    <span class="hljs-string">&quot;&quot;&quot;根据以下上下文回答问题。如果不知道，就说不知道。</span><br><span class="hljs-string"></span><br><span class="hljs-string">上下文：</span><br><span class="hljs-string">&#123;context&#125;</span><br><span class="hljs-string"></span><br><span class="hljs-string">问题：&#123;question&#125;</span><br><span class="hljs-string">答案：&quot;&quot;&quot;</span><br>)<br><br><span class="hljs-comment"># 4. 创建 RAG 链</span><br>chain = (<br>    &#123;<span class="hljs-string">&quot;context&quot;</span>: retriever, <span class="hljs-string">&quot;question&quot;</span>: RunnablePassthrough()&#125;<br>    | prompt<br>    | ChatOpenAI(model=<span class="hljs-string">&quot;gpt-4o-mini&quot;</span>)<br>    | StrOutputParser()<br>)<br><br><span class="hljs-comment"># 5. 使用</span><br>answer = chain.invoke(<span class="hljs-string">&quot;苹果的总部在哪里？&quot;</span>)<br><span class="hljs-built_in">print</span>(answer)  <span class="hljs-comment"># &quot;苹果的总部在美国加州库比蒂诺&quot;</span><br></code></pre></td></tr></table></figure><h3 id="4-2-完整的-RAG-应用（从文档加载到问答）">4.2 完整的 RAG 应用（从文档加载到问答）</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-comment"># ========== 第一步：加载文档 ==========</span><br><span class="hljs-keyword">from</span> langchain_community.document_loaders <span class="hljs-keyword">import</span> (<br>    PyPDFLoader,      <span class="hljs-comment"># PDF</span><br>    Docx2txtLoader,   <span class="hljs-comment"># Word</span><br>    TextLoader,       <span class="hljs-comment"># 文本</span><br>    WebBaseLoader,    <span class="hljs-comment"># 网页</span><br>)<br><br><span class="hljs-comment"># 加载 PDF</span><br>loader = PyPDFLoader(<span class="hljs-string">&quot;company_handbook.pdf&quot;</span>)<br>documents = loader.load()<br><br><span class="hljs-comment"># 加载多个文件</span><br>loaders = [<br>    PyPDFLoader(<span class="hljs-string">&quot;handbook.pdf&quot;</span>),<br>    Docx2txtLoader(<span class="hljs-string">&quot;policy.docx&quot;</span>),<br>    WebBaseLoader(<span class="hljs-string">&quot;https://example.com/faq&quot;</span>),<br>]<br>documents = []<br><span class="hljs-keyword">for</span> loader <span class="hljs-keyword">in</span> loaders:<br>    documents.extend(loader.load())<br><br><span class="hljs-comment"># ========== 第二步：分割文档 ==========</span><br><span class="hljs-keyword">from</span> langchain_text_splitters <span class="hljs-keyword">import</span> RecursiveCharacterTextSplitter<br><br>text_splitter = RecursiveCharacterTextSplitter(<br>    chunk_size=<span class="hljs-number">1000</span>,<br>    chunk_overlap=<span class="hljs-number">200</span>,<br>)<br>splits = text_splitter.split_documents(documents)<br><br><span class="hljs-built_in">print</span>(<span class="hljs-string">f&quot;原始文档数：<span class="hljs-subst">&#123;<span class="hljs-built_in">len</span>(documents)&#125;</span>&quot;</span>)<br><span class="hljs-built_in">print</span>(<span class="hljs-string">f&quot;分块后文档数：<span class="hljs-subst">&#123;<span class="hljs-built_in">len</span>(splits)&#125;</span>&quot;</span>)<br><br><span class="hljs-comment"># ========== 第三步：创建向量存储 ==========</span><br><span class="hljs-keyword">from</span> langchain_openai <span class="hljs-keyword">import</span> OpenAIEmbeddings<br><span class="hljs-keyword">from</span> langchain_community.vectorstores <span class="hljs-keyword">import</span> FAISS<br><br><span class="hljs-comment"># 创建嵌入模型</span><br>embeddings = OpenAIEmbeddings(model=<span class="hljs-string">&quot;text-embedding-3-small&quot;</span>)<br><br><span class="hljs-comment"># 创建向量存储</span><br>vectorstore = FAISS.from_documents(splits, embeddings)<br><br><span class="hljs-comment"># 保存到本地（可选）</span><br>vectorstore.save_local(<span class="hljs-string">&quot;faiss_index&quot;</span>)<br><br><span class="hljs-comment"># 从本地加载（可选）</span><br><span class="hljs-comment"># vectorstore = FAISS.load_local(&quot;faiss_index&quot;, embeddings)</span><br><br><span class="hljs-comment"># ========== 第四步：创建检索器 ==========</span><br>retriever = vectorstore.as_retriever(<br>    search_type=<span class="hljs-string">&quot;similarity&quot;</span>,  <span class="hljs-comment"># similarity | mmr | similarity_score_threshold</span><br>    search_kwargs=&#123;<span class="hljs-string">&quot;k&quot;</span>: <span class="hljs-number">4</span>&#125;     <span class="hljs-comment"># 返回 4 个最相关的文档</span><br>)<br><br><span class="hljs-comment"># ========== 第五步：创建 RAG 链 ==========</span><br><span class="hljs-keyword">from</span> langchain_core.prompts <span class="hljs-keyword">import</span> ChatPromptTemplate<br><span class="hljs-keyword">from</span> langchain_core.runnables <span class="hljs-keyword">import</span> RunnablePassthrough<br><span class="hljs-keyword">from</span> langchain_core.output_parsers <span class="hljs-keyword">import</span> StrOutputParser<br><span class="hljs-keyword">from</span> langchain_openai <span class="hljs-keyword">import</span> ChatOpenAI<br><br><span class="hljs-comment"># 定义提示词</span><br>rag_prompt = ChatPromptTemplate.from_template(<span class="hljs-string">&quot;&quot;&quot;你是一个专业的问答助手。</span><br><span class="hljs-string">请根据下面的【上下文】来回答【问题】。</span><br><span class="hljs-string"></span><br><span class="hljs-string">【要求】</span><br><span class="hljs-string">1. 只根据上下文回答，不要编造信息</span><br><span class="hljs-string">2. 如果上下文中没有答案，直接说&quot;根据提供的文档，无法回答这个问题&quot;</span><br><span class="hljs-string">3. 回答要简洁、准确</span><br><span class="hljs-string">4. 引用原文时请说明</span><br><span class="hljs-string"></span><br><span class="hljs-string">【上下文】</span><br><span class="hljs-string">&#123;context&#125;</span><br><span class="hljs-string"></span><br><span class="hljs-string">【问题】</span><br><span class="hljs-string">&#123;question&#125;</span><br><span class="hljs-string"></span><br><span class="hljs-string">【答案】</span><br><span class="hljs-string">&quot;&quot;&quot;</span>)<br><br><span class="hljs-comment"># 定义文档格式化函数</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">format_docs</span>(<span class="hljs-params">docs</span>):<br>    <span class="hljs-string">&quot;&quot;&quot;把检索到的文档合并成字符串&quot;&quot;&quot;</span><br>    <span class="hljs-keyword">return</span> <span class="hljs-string">&quot;\n\n---\n\n&quot;</span>.join(<br>        <span class="hljs-string">f&quot;[来源：<span class="hljs-subst">&#123;doc.metadata.get(<span class="hljs-string">&#x27;source&#x27;</span>, <span class="hljs-string">&#x27;未知&#x27;</span>)&#125;</span>, 页码：<span class="hljs-subst">&#123;doc.metadata.get(<span class="hljs-string">&#x27;page&#x27;</span>, <span class="hljs-string">&#x27;N/A&#x27;</span>)&#125;</span>]\n<span class="hljs-subst">&#123;doc.page_content&#125;</span>&quot;</span><br>        <span class="hljs-keyword">for</span> doc <span class="hljs-keyword">in</span> docs<br>    )<br><br><span class="hljs-comment"># 创建 RAG 链</span><br>rag_chain = (<br>    &#123;<span class="hljs-string">&quot;context&quot;</span>: retriever | format_docs, <span class="hljs-string">&quot;question&quot;</span>: RunnablePassthrough()&#125;<br>    | rag_prompt<br>    | ChatOpenAI(model=<span class="hljs-string">&quot;gpt-4o-mini&quot;</span>, temperature=<span class="hljs-number">0</span>)<br>    | StrOutputParser()<br>)<br><br><span class="hljs-comment"># ========== 第六步：使用 ==========</span><br>questions = [<br>    <span class="hljs-string">&quot;公司的年假政策是什么？&quot;</span>,<br>    <span class="hljs-string">&quot;如何申请报销？&quot;</span>,<br>    <span class="hljs-string">&quot;工作时间是几点到几点？&quot;</span>,<br>]<br><br><span class="hljs-keyword">for</span> q <span class="hljs-keyword">in</span> questions:<br>    <span class="hljs-built_in">print</span>(<span class="hljs-string">f&quot;\n问题：<span class="hljs-subst">&#123;q&#125;</span>&quot;</span>)<br>    answer = rag_chain.invoke(q)<br>    <span class="hljs-built_in">print</span>(<span class="hljs-string">f&quot;答案：<span class="hljs-subst">&#123;answer&#125;</span>&quot;</span>)<br></code></pre></td></tr></table></figure><h3 id="4-3-带对话历史的-RAG（多轮问答）">4.3 带对话历史的 RAG（多轮问答）</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> langchain.memory <span class="hljs-keyword">import</span> ConversationBufferMemory<br><span class="hljs-keyword">from</span> langchain_core.prompts <span class="hljs-keyword">import</span> MessagesPlaceholder<br><br><span class="hljs-comment"># 创建记忆</span><br>memory = ConversationBufferMemory(<br>    return_messages=<span class="hljs-literal">True</span>,<br>    output_key=<span class="hljs-string">&quot;answer&quot;</span>,<br>    input_key=<span class="hljs-string">&quot;question&quot;</span><br>)<br><br><span class="hljs-comment"># 带历史对话的提示词</span><br>rag_prompt = ChatPromptTemplate.from_messages([<br>    (<span class="hljs-string">&quot;system&quot;</span>, <span class="hljs-string">&quot;&quot;&quot;你是一个专业的问答助手。</span><br><span class="hljs-string">根据【上下文】回答用户的【问题】。</span><br><span class="hljs-string">如果有多轮对话历史，请参考历史来理解当前问题。&quot;&quot;&quot;</span>),<br>    MessagesPlaceholder(variable_name=<span class="hljs-string">&quot;history&quot;</span>),  <span class="hljs-comment"># 插入对话历史</span><br>    (<span class="hljs-string">&quot;human&quot;</span>, <span class="hljs-string">&quot;&quot;&quot;【上下文】</span><br><span class="hljs-string">&#123;context&#125;</span><br><span class="hljs-string"></span><br><span class="hljs-string">【当前问题】</span><br><span class="hljs-string">&#123;question&#125;</span><br><span class="hljs-string"></span><br><span class="hljs-string">【答案】&quot;&quot;&quot;</span>)<br>])<br><br><span class="hljs-comment"># 创建链</span><br><span class="hljs-keyword">from</span> langchain_core.runnables <span class="hljs-keyword">import</span> RunnableLambda<br><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">load_memory</span>(<span class="hljs-params">x</span>):<br>    <span class="hljs-keyword">return</span> &#123;<span class="hljs-string">&quot;history&quot;</span>: memory.load_memory_variables(&#123;&#125;)[<span class="hljs-string">&quot;history&quot;</span>]&#125;<br><br>rag_with_memory = (<br>    RunnableLambda(load_memory)<br>    | &#123;<span class="hljs-string">&quot;context&quot;</span>: retriever | format_docs, <span class="hljs-string">&quot;question&quot;</span>: <span class="hljs-keyword">lambda</span> x: x[<span class="hljs-string">&quot;question&quot;</span>], <span class="hljs-string">&quot;history&quot;</span>: <span class="hljs-keyword">lambda</span> x: x[<span class="hljs-string">&quot;history&quot;</span>]&#125;<br>    | rag_prompt<br>    | ChatOpenAI(model=<span class="hljs-string">&quot;gpt-4o-mini&quot;</span>)<br>    | StrOutputParser()<br>)<br><br><span class="hljs-comment"># 多轮对话示例</span><br>questions = [<br>    <span class="hljs-string">&quot;公司的年假政策是什么？&quot;</span>,<br>    <span class="hljs-string">&quot;那病假呢？&quot;</span>,  <span class="hljs-comment"># 需要理解&quot;那&quot;指的是年假政策</span><br>    <span class="hljs-string">&quot;这两个有什么区别？&quot;</span>,  <span class="hljs-comment"># 需要记住前面两个政策</span><br>]<br><br><span class="hljs-keyword">for</span> q <span class="hljs-keyword">in</span> questions:<br>    answer = rag_with_memory.invoke(&#123;<span class="hljs-string">&quot;question&quot;</span>: q&#125;)<br>    memory.save_context(&#123;<span class="hljs-string">&quot;input&quot;</span>: q&#125;, &#123;<span class="hljs-string">&quot;output&quot;</span>: answer&#125;)<br>    <span class="hljs-built_in">print</span>(<span class="hljs-string">f&quot;Q: <span class="hljs-subst">&#123;q&#125;</span>&quot;</span>)<br>    <span class="hljs-built_in">print</span>(<span class="hljs-string">f&quot;A: <span class="hljs-subst">&#123;answer&#125;</span>\n&quot;</span>)<br></code></pre></td></tr></table></figure><h3 id="4-4-高级-RAG：混合搜索-重排序">4.4 高级 RAG：混合搜索 + 重排序</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-comment"># 混合搜索 = 语义搜索 + 关键词搜索</span><br><span class="hljs-keyword">from</span> langchain.retrievers <span class="hljs-keyword">import</span> EnsembleRetriever<br><span class="hljs-keyword">from</span> langchain_community.retrievers <span class="hljs-keyword">import</span> BM25Retriever<br><br><span class="hljs-comment"># 语义检索器（向量搜索）</span><br>vector_retriever = vectorstore.as_retriever(search_kwargs=&#123;<span class="hljs-string">&quot;k&quot;</span>: <span class="hljs-number">5</span>&#125;)<br><br><span class="hljs-comment"># 关键词检索器（BM25）</span><br>keyword_retriever = BM25Retriever.from_documents(splits)<br>keyword_retriever.k = <span class="hljs-number">5</span><br><br><span class="hljs-comment"># 组合两个检索器</span><br>ensemble_retriever = EnsembleRetriever(<br>    retrievers=[vector_retriever, keyword_retriever],<br>    weights=[<span class="hljs-number">0.5</span>, <span class="hljs-number">0.5</span>]  <span class="hljs-comment"># 各占 50%</span><br>)<br><br><span class="hljs-comment"># 使用新的检索器</span><br>rag_chain = (<br>    &#123;<span class="hljs-string">&quot;context&quot;</span>: ensemble_retriever | format_docs, <span class="hljs-string">&quot;question&quot;</span>: RunnablePassthrough()&#125;<br>    | rag_prompt<br>    | ChatOpenAI(model=<span class="hljs-string">&quot;gpt-4o-mini&quot;</span>)<br>    | StrOutputParser()<br>)<br></code></pre></td></tr></table></figure><hr><h2 id="5-高级技巧与最佳实践">5. 高级技巧与最佳实践</h2><h3 id="5-1-提高检索质量">5.1 提高检索质量</h3><h4 id="技巧-1：Metadata-过滤">技巧 1：Metadata 过滤</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-comment"># 给文档添加元数据</span><br><span class="hljs-keyword">for</span> doc <span class="hljs-keyword">in</span> splits:<br>    doc.metadata[<span class="hljs-string">&quot;category&quot;</span>] = <span class="hljs-string">&quot;policy&quot;</span>  <span class="hljs-comment"># 类别</span><br>    doc.metadata[<span class="hljs-string">&quot;year&quot;</span>] = <span class="hljs-number">2024</span>          <span class="hljs-comment"># 年份</span><br>    doc.metadata[<span class="hljs-string">&quot;department&quot;</span>] = <span class="hljs-string">&quot;HR&quot;</span>    <span class="hljs-comment"># 部门</span><br><br><span class="hljs-comment"># 检索时过滤</span><br>retriever = vectorstore.as_retriever(<br>    search_kwargs=&#123;<br>        <span class="hljs-string">&quot;k&quot;</span>: <span class="hljs-number">4</span>,<br>        <span class="hljs-string">&quot;filter&quot;</span>: &#123;<span class="hljs-string">&quot;category&quot;</span>: <span class="hljs-string">&quot;policy&quot;</span>, <span class="hljs-string">&quot;year&quot;</span>: <span class="hljs-number">2024</span>&#125;<br>    &#125;<br>)<br></code></pre></td></tr></table></figure><h4 id="技巧-2：多查询检索">技巧 2：多查询检索</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> langchain.retrievers.multi_query <span class="hljs-keyword">import</span> MultiQueryRetriever<br><br><span class="hljs-comment"># 让 LLM 从不同角度重新表述问题</span><br>retriever = MultiQueryRetriever.from_llm(<br>    retriever=vectorstore.as_retriever(),<br>    llm=ChatOpenAI(model=<span class="hljs-string">&quot;gpt-4o-mini&quot;</span>),<br>)<br><br><span class="hljs-comment"># 原问题：&quot;公司休假政策&quot;</span><br><span class="hljs-comment"># 会生成多个变体：</span><br><span class="hljs-comment"># - &quot;年假有多少天？&quot;</span><br><span class="hljs-comment"># - &quot;病假怎么申请？&quot;</span><br><span class="hljs-comment"># - &quot;法定节假日放假吗？&quot;</span><br></code></pre></td></tr></table></figure><h4 id="技巧-3：Parent-Document-Retriever">技巧 3：Parent Document Retriever</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> langchain.retrievers.parent_document_retriever <span class="hljs-keyword">import</span> ParentDocumentRetriever<br><span class="hljs-keyword">from</span> langchain.storage <span class="hljs-keyword">import</span> InMemoryStore<br><span class="hljs-keyword">from</span> langchain_text_splitters <span class="hljs-keyword">import</span> RecursiveCharacterTextSplitter<br><br><span class="hljs-comment"># 大块存储，小块检索</span><br>parent_splitter = RecursiveCharacterTextSplitter(chunk_size=<span class="hljs-number">2000</span>)<br>child_splitter = RecursiveCharacterTextSplitter(chunk_size=<span class="hljs-number">400</span>)<br><br>store = InMemoryStore()  <span class="hljs-comment"># 存储大块文档</span><br><br>retriever = ParentDocumentRetriever(<br>    vectorstore=vectorstore,<br>    docstore=store,<br>    child_splitter=child_splitter,<br>    parent_splitter=parent_splitter,<br>)<br></code></pre></td></tr></table></figure><h3 id="5-2-优化-RAG-响应质量">5.2 优化 RAG 响应质量</h3><h4 id="添加引用来源">添加引用来源</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> langchain_core.runnables <span class="hljs-keyword">import</span> RunnableParallel<br><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">format_docs_with_citation</span>(<span class="hljs-params">docs</span>):<br>    <span class="hljs-string">&quot;&quot;&quot;格式化文档并添加引用编号&quot;&quot;&quot;</span><br>    formatted = []<br>    <span class="hljs-keyword">for</span> i, doc <span class="hljs-keyword">in</span> <span class="hljs-built_in">enumerate</span>(docs):<br>        source = doc.metadata.get(<span class="hljs-string">&quot;source&quot;</span>, <span class="hljs-string">&quot;未知来源&quot;</span>)<br>        page = doc.metadata.get(<span class="hljs-string">&quot;page&quot;</span>, <span class="hljs-string">&quot;&quot;</span>)<br>        formatted.append(<br>            <span class="hljs-string">f&quot;[<span class="hljs-subst">&#123;i+<span class="hljs-number">1</span>&#125;</span>] <span class="hljs-subst">&#123;source&#125;</span><span class="hljs-subst">&#123;<span class="hljs-string">&#x27; 第&#x27;</span>+<span class="hljs-built_in">str</span>(page)+<span class="hljs-string">&#x27;页&#x27;</span> <span class="hljs-keyword">if</span> page <span class="hljs-keyword">else</span> <span class="hljs-string">&#x27;&#x27;</span>&#125;</span>\n<span class="hljs-subst">&#123;doc.page_content&#125;</span>&quot;</span><br>        )<br>    <span class="hljs-keyword">return</span> <span class="hljs-string">&quot;\n\n&quot;</span>.join(formatted)<br><br>prompt_with_citation = ChatPromptTemplate.from_template(<span class="hljs-string">&quot;&quot;&quot;根据上下文回答问题，并在回答中标注引用来源（如 [1], [2]）。</span><br><span class="hljs-string"></span><br><span class="hljs-string">上下文：</span><br><span class="hljs-string">&#123;context&#125;</span><br><span class="hljs-string"></span><br><span class="hljs-string">问题：&#123;question&#125;</span><br><span class="hljs-string"></span><br><span class="hljs-string">答案（带引用）：&quot;&quot;&quot;</span>)<br><br><span class="hljs-comment"># 同时返回答案和来源文档</span><br>rag_with_sources = RunnableParallel(<br>    answer=(<br>        &#123;<span class="hljs-string">&quot;context&quot;</span>: retriever | format_docs_with_citation, <span class="hljs-string">&quot;question&quot;</span>: RunnablePassthrough()&#125;<br>        | prompt_with_citation<br>        | ChatOpenAI(model=<span class="hljs-string">&quot;gpt-4o-mini&quot;</span>)<br>        | StrOutputParser()<br>    ),<br>    sources=retriever<br>)<br><br>result = rag_with_sources.invoke(<span class="hljs-string">&quot;问题...&quot;</span>)<br><span class="hljs-built_in">print</span>(result[<span class="hljs-string">&quot;answer&quot;</span>])<br><span class="hljs-built_in">print</span>(<span class="hljs-string">&quot;\n来源文档:&quot;</span>)<br><span class="hljs-keyword">for</span> doc <span class="hljs-keyword">in</span> result[<span class="hljs-string">&quot;sources&quot;</span>]:<br>    <span class="hljs-built_in">print</span>(<span class="hljs-string">f&quot;- <span class="hljs-subst">&#123;doc.metadata.get(<span class="hljs-string">&#x27;source&#x27;</span>)&#125;</span>&quot;</span>)<br></code></pre></td></tr></table></figure><h4 id="添加思考过程">添加思考过程</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-comment"># 在提示词中引导模型逐步思考</span><br>cot_prompt = ChatPromptTemplate.from_template(<span class="hljs-string">&quot;&quot;&quot;请逐步思考并回答问题。</span><br><span class="hljs-string"></span><br><span class="hljs-string">上下文：</span><br><span class="hljs-string">&#123;context&#125;</span><br><span class="hljs-string"></span><br><span class="hljs-string">问题：&#123;question&#125;</span><br><span class="hljs-string"></span><br><span class="hljs-string">请按以下步骤回答：</span><br><span class="hljs-string">1. 分析问题：这个问题在问什么？</span><br><span class="hljs-string">2. 查找信息：从上下文中找到相关信息</span><br><span class="hljs-string">3. 组织答案：整合信息形成完整回答</span><br><span class="hljs-string">4. 检查验证：确认答案准确且基于上下文</span><br><span class="hljs-string"></span><br><span class="hljs-string">你的回答：&quot;&quot;&quot;</span>)<br></code></pre></td></tr></table></figure><h3 id="5-3-性能优化">5.3 性能优化</h3><h4 id="批量处理文档">批量处理文档</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-comment"># 批量添加向量，而不是一个一个添加</span><br><span class="hljs-keyword">from</span> langchain_community.vectorstores <span class="hljs-keyword">import</span> FAISS<br><br><span class="hljs-comment"># 慢的方式</span><br><span class="hljs-keyword">for</span> doc <span class="hljs-keyword">in</span> splits:<br>    vectorstore.add_documents([doc])<br><br><span class="hljs-comment"># 快的方式（批量）</span><br>vectorstore.add_documents(splits, batch_size=<span class="hljs-number">100</span>)<br></code></pre></td></tr></table></figure><h4 id="异步处理">异步处理</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-comment"># 异步调用</span><br>answer = <span class="hljs-keyword">await</span> rag_chain.ainvoke(<span class="hljs-string">&quot;问题...&quot;</span>)<br><br><span class="hljs-comment"># 批量异步</span><br>questions = [<span class="hljs-string">&quot;问题 1&quot;</span>, <span class="hljs-string">&quot;问题 2&quot;</span>, <span class="hljs-string">&quot;问题 3&quot;</span>]<br>answers = <span class="hljs-keyword">await</span> rag_chain.abatch(questions)<br></code></pre></td></tr></table></figure><h3 id="5-4-评估-RAG-质量">5.4 评估 RAG 质量</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> langchain_openai <span class="hljs-keyword">import</span> ChatOpenAI<br><span class="hljs-keyword">from</span> langchain_core.prompts <span class="hljs-keyword">import</span> ChatPromptTemplate<br><br><span class="hljs-comment"># 创建评估链</span><br>eval_prompt = ChatPromptTemplate.from_template(<span class="hljs-string">&quot;&quot;&quot;评估 RAG 系统的答案质量。</span><br><span class="hljs-string"></span><br><span class="hljs-string">问题：&#123;question&#125;</span><br><span class="hljs-string">上下文：&#123;context&#125;</span><br><span class="hljs-string">答案：&#123;answer&#125;</span><br><span class="hljs-string"></span><br><span class="hljs-string">请从以下维度评分（1-5 分）：</span><br><span class="hljs-string">1. 相关性：答案是否回答了问题？</span><br><span class="hljs-string">2. 准确性：答案是否基于上下文且正确？</span><br><span class="hljs-string">3. 完整性：答案是否完整全面？</span><br><span class="hljs-string">4. 引用：是否正确引用了来源？</span><br><span class="hljs-string"></span><br><span class="hljs-string">评分和理由：&quot;&quot;&quot;</span>)<br><br>eval_chain = (<br>    eval_prompt<br>    | ChatOpenAI(model=<span class="hljs-string">&quot;gpt-4o-mini&quot;</span>, temperature=<span class="hljs-number">0</span>)<br>    | StrOutputParser()<br>)<br><br><span class="hljs-comment"># 评估</span><br>evaluation = eval_chain.invoke(&#123;<br>    <span class="hljs-string">&quot;question&quot;</span>: <span class="hljs-string">&quot;问题...&quot;</span>,<br>    <span class="hljs-string">&quot;context&quot;</span>: <span class="hljs-string">&quot;检索到的上下文...&quot;</span>,<br>    <span class="hljs-string">&quot;answer&quot;</span>: <span class="hljs-string">&quot;生成的答案...&quot;</span><br>&#125;)<br><span class="hljs-built_in">print</span>(evaluation)<br></code></pre></td></tr></table></figure><hr><h2 id="6-常见问题与解决方案">6. 常见问题与解决方案</h2><h3 id="Q1-检索到的文档不相关怎么办？">Q1: 检索到的文档不相关怎么办？</h3><p><strong>原因分析：</strong></p><ul><li>文档分块太大或太小</li><li>Embedding 模型不适合你的领域</li><li>检索参数设置不当</li></ul><p><strong>解决方案：</strong></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-comment"># 1. 调整分块大小</span><br>text_splitter = RecursiveCharacterTextSplitter(<br>    chunk_size=<span class="hljs-number">300</span>,    <span class="hljs-comment"># 减小块大小</span><br>    chunk_overlap=<span class="hljs-number">50</span>,<br>)<br><br><span class="hljs-comment"># 2. 使用更好的 Embedding</span><br>embeddings = OpenAIEmbeddings(model=<span class="hljs-string">&quot;text-embedding-3-large&quot;</span>)<br><br><span class="hljs-comment"># 3. 增加检索数量</span><br>retriever = vectorstore.as_retriever(search_kwargs=&#123;<span class="hljs-string">&quot;k&quot;</span>: <span class="hljs-number">10</span>&#125;)<br><br><span class="hljs-comment"># 4. 使用混合搜索</span><br><span class="hljs-keyword">from</span> langchain.retrievers <span class="hljs-keyword">import</span> EnsembleRetriever<br></code></pre></td></tr></table></figure><h3 id="Q2-LLM-编造信息（幻觉）怎么办？">Q2: LLM 编造信息（幻觉）怎么办？</h3><p><strong>解决方案：</strong></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-comment"># 1. 在提示词中明确要求</span><br>prompt = ChatPromptTemplate.from_template(<span class="hljs-string">&quot;&quot;&quot;严格根据以下上下文回答问题。</span><br><span class="hljs-string">如果上下文中没有答案，请直接说&quot;根据提供的文档，无法回答这个问题&quot;，不要编造。</span><br><span class="hljs-string"></span><br><span class="hljs-string">上下文：</span><br><span class="hljs-string">&#123;context&#125;</span><br><span class="hljs-string"></span><br><span class="hljs-string">问题：&#123;question&#125;</span><br><span class="hljs-string">答案：&quot;&quot;&quot;</span>)<br><br><span class="hljs-comment"># 2. 降低 temperature</span><br>llm = ChatOpenAI(model=<span class="hljs-string">&quot;gpt-4o-mini&quot;</span>, temperature=<span class="hljs-number">0</span>)<br><br><span class="hljs-comment"># 3. 添加验证步骤</span><br>verify_prompt = ChatPromptTemplate.from_template(<span class="hljs-string">&quot;&quot;&quot;检查以下答案是否完全基于上下文。</span><br><span class="hljs-string">如果答案包含上下文中没有的信息，请指出。</span><br><span class="hljs-string"></span><br><span class="hljs-string">上下文：&#123;context&#125;</span><br><span class="hljs-string">答案：&#123;answer&#125;</span><br><span class="hljs-string">验证结果：&quot;&quot;&quot;</span>)<br></code></pre></td></tr></table></figure><h3 id="Q3-如何处理大量文档？">Q3: 如何处理大量文档？</h3><p><strong>解决方案：</strong></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-comment"># 1. 使用专业向量数据库（不是内存存储）</span><br><span class="hljs-keyword">from</span> langchain_community.vectorstores <span class="hljs-keyword">import</span> FAISS  <span class="hljs-comment"># 支持磁盘存储</span><br><span class="hljs-comment"># 或</span><br><span class="hljs-keyword">from</span> langchain_milvus <span class="hljs-keyword">import</span> Milvus  <span class="hljs-comment"># 专业向量数据库</span><br><br><span class="hljs-comment"># 2. 增量添加文档</span><br>vectorstore = FAISS.load_local(<span class="hljs-string">&quot;index_path&quot;</span>, embeddings)<br>vectorstore.add_documents(new_documents)<br>vectorstore.save_local(<span class="hljs-string">&quot;index_path&quot;</span>)<br><br><span class="hljs-comment"># 3. 分批次处理</span><br>batch_size = <span class="hljs-number">1000</span><br><span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> <span class="hljs-built_in">range</span>(<span class="hljs-number">0</span>, <span class="hljs-built_in">len</span>(documents), batch_size):<br>    batch = documents[i:i+batch_size]<br>    vectorstore.add_documents(batch)<br></code></pre></td></tr></table></figure><h3 id="Q4-如何实现多语言支持？">Q4: 如何实现多语言支持？</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-comment"># 1. 使用多语言 Embedding</span><br><span class="hljs-keyword">from</span> langchain_community.embeddings <span class="hljs-keyword">import</span> HuggingFaceEmbeddings<br>embeddings = HuggingFaceEmbeddings(<br>    model_name=<span class="hljs-string">&quot;sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2&quot;</span><br>)<br><br><span class="hljs-comment"># 2. 在提示词中指定语言</span><br>prompt = ChatPromptTemplate.from_template(<span class="hljs-string">&quot;&quot;&quot;用中文回答以下问题。</span><br><span class="hljs-string"></span><br><span class="hljs-string">上下文：&#123;context&#125;</span><br><span class="hljs-string">问题：&#123;question&#125;</span><br><span class="hljs-string">答案：&quot;&quot;&quot;</span>)<br></code></pre></td></tr></table></figure><hr><h2 id="附录：快速参考手册">附录：快速参考手册</h2><h3 id="常用-Import">常用 Import</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-comment"># 模型</span><br><span class="hljs-keyword">from</span> langchain_openai <span class="hljs-keyword">import</span> ChatOpenAI, OpenAIEmbeddings<br><span class="hljs-keyword">from</span> langchain_anthropic <span class="hljs-keyword">import</span> ChatAnthropic<br><br><span class="hljs-comment"># 文档处理</span><br><span class="hljs-keyword">from</span> langchain_community.document_loaders <span class="hljs-keyword">import</span> PyPDFLoader, WebBaseLoader<br><span class="hljs-keyword">from</span> langchain_text_splitters <span class="hljs-keyword">import</span> RecursiveCharacterTextSplitter<br><br><span class="hljs-comment"># 向量存储</span><br><span class="hljs-keyword">from</span> langchain_community.vectorstores <span class="hljs-keyword">import</span> FAISS, Chroma<br><span class="hljs-keyword">from</span> langchain_core.vectorstores <span class="hljs-keyword">import</span> InMemoryVectorStore<br><br><span class="hljs-comment"># 检索</span><br><span class="hljs-keyword">from</span> langchain_core.runnables <span class="hljs-keyword">import</span> RunnablePassthrough<br><span class="hljs-keyword">from</span> langchain_core.output_parsers <span class="hljs-keyword">import</span> StrOutputParser<br><br><span class="hljs-comment"># 提示词</span><br><span class="hljs-keyword">from</span> langchain_core.prompts <span class="hljs-keyword">import</span> ChatPromptTemplate, MessagesPlaceholder<br><br><span class="hljs-comment"># 记忆</span><br><span class="hljs-keyword">from</span> langchain.memory <span class="hljs-keyword">import</span> ConversationBufferMemory<br><br><span class="hljs-comment"># 工具与代理</span><br><span class="hljs-keyword">from</span> langchain.tools <span class="hljs-keyword">import</span> tool<br><span class="hljs-keyword">from</span> langchain.agents <span class="hljs-keyword">import</span> create_agent<br></code></pre></td></tr></table></figure><h3 id="核心流程图">核心流程图</h3><figure class="highlight nix"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><code class="hljs nix">完整 RAG 应用架构<br><br>┌─────────────────────────────────────────────────────────────┐<br>│                      用户界面                                │<br>│                  (Streamlit <span class="hljs-symbol">/</span> Web <span class="hljs-symbol">/</span> CLI)                    │<br>└─────────────────────────────────────────────────────────────┘<br>                              │<br>                              ▼<br>┌─────────────────────────────────────────────────────────────┐<br>│                    LangChain 应用层                          │<br>│  ┌─────────────┐  ┌─────────────┐  ┌─────────────────────┐  │<br>│  │ RAG Chain   │  │ Agent       │  │ Memory Management   │  │<br>│  │ (问答核心)   │  │ (工具调用)  │  │ (对话历史)          │  │<br>│  └─────────────┘  └─────────────┘  └─────────────────────┘  │<br>└─────────────────────────────────────────────────────────────┘<br>                              │<br>                              ▼<br>┌─────────────────────────────────────────────────────────────┐<br>│                    检索层                                    │<br>│  ┌─────────────┐  ┌─────────────┐  ┌─────────────────────┐  │<br>│  │ Retriever   │  │ Vector Store│  │ Embedding Model     │  │<br>│  │ (检索策略)  │  │ (FAISS等)   │  │ (向量生成)          │  │<br>│  └─────────────┘  └─────────────┘  └─────────────────────┘  │<br>└─────────────────────────────────────────────────────────────┘<br>                              │<br>                              ▼<br>┌─────────────────────────────────────────────────────────────┐<br>│                    数据层                                    │<br>│  ┌─────────────┐  ┌─────────────┐  ┌─────────────────────┐  │<br>│  │ PDF<span class="hljs-symbol">/Word</span>    │  │ 数据库       │  │ API<span class="hljs-operator">/</span>网页            │  │<br>│  │ (文档加载)  │  │ (结构化数据)│  │ (实时数据)          │  │<br>│  └─────────────┘  └─────────────┘  └─────────────────────┘  │<br>└─────────────────────────────────────────────────────────────┘<br></code></pre></td></tr></table></figure><h3 id="学习路线建议">学习路线建议</h3><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><code class="hljs markdown">第 1 天：LangChain 基础<br><span class="hljs-bullet">  -</span> 安装和环境配置<br><span class="hljs-bullet">  -</span> 模型调用（ChatOpenAI）<br><span class="hljs-bullet">  -</span> 提示词模板（ChatPromptTemplate）<br><br>第 2 天：链和输出解析<br><span class="hljs-bullet">  -</span> 创建简单的 Chain<br><span class="hljs-bullet">  -</span> 输出解析器（StrOutputParser）<br><span class="hljs-bullet">  -</span> Runnable 接口<br><br>第 3 天：RAG 入门<br><span class="hljs-bullet">  -</span> Embedding 和向量<br><span class="hljs-bullet">  -</span> 文档加载和分块<br><span class="hljs-bullet">  -</span> 向量存储和检索<br><br>第 4 天：完整 RAG 应用<br><span class="hljs-bullet">  -</span> 构建端到端 RAG<br><span class="hljs-bullet">  -</span> 添加对话历史<br><span class="hljs-bullet">  -</span> 优化检索质量<br><br>第 5 天：高级主题<br><span class="hljs-bullet">  -</span> Agent 和 Tools<br><span class="hljs-bullet">  -</span> 混合搜索<br><span class="hljs-bullet">  -</span> 评估和优化<br><br>第 6-7 天：实战项目<br><span class="hljs-bullet">  -</span> 选择实际场景<br><span class="hljs-bullet">  -</span> 完整实现<br><span class="hljs-bullet">  -</span> 调优和部署<br></code></pre></td></tr></table></figure><hr><h2 id="环境搭建">环境搭建</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-comment"># 基础安装</span><br>pip install langchain langchain-core langchain-community<br><br><span class="hljs-comment"># OpenAI 支持</span><br>pip install langchain-openai<br><br><span class="hljs-comment"># 文档加载器</span><br>pip install pypdf docx2txt beautifulsoup4<br><br><span class="hljs-comment"># 向量数据库</span><br>pip install faiss-cpu chromadb<br><br><span class="hljs-comment"># 可选：Web 界面</span><br>pip install streamlit<br><br><span class="hljs-comment"># 验证安装</span><br>python -c <span class="hljs-string">&quot;from langchain_openai import ChatOpenAI; print(&#x27;安装成功!&#x27;)&quot;</span><br></code></pre></td></tr></table></figure><hr><h2 id="总结">总结</h2><p><strong>LangChain 核心要点：</strong></p><ol><li><strong>Models</strong> - LLM 是大脑</li><li><strong>Prompts</strong> - 好的提示词 = 好的输出</li><li><strong>Chains</strong> - 组合组件形成工作流</li><li><strong>Agents</strong> - 让 LLM 自主使用工具</li><li><strong>Memory</strong> - 保持对话连续性</li></ol><p><strong>RAG 核心要点：</strong></p><ol><li><strong>文档分块</strong> - 大小要合适</li><li><strong>向量检索</strong> - 找到相关内容</li><li><strong>提示词设计</strong> - 引导 LLM 正确回答</li><li><strong>评估优化</strong> - 持续改进质量</li></ol><p><strong>学习建议：</strong></p><ul><li>先跑通最简单的示例</li><li>理解每个组件的作用</li><li>逐步添加功能</li><li>在实际项目中练习</li></ul>]]>
    </content>
    <id>https://www.furinaluna.top/posts/297d2532.html</id>
    <link href="https://www.furinaluna.top/posts/297d2532.html"/>
    <published>2026-04-05T11:04:09.000Z</published>
    <summary>
      <![CDATA[<h1>LangChain &amp; RAG 简单快速入门</h1>
<blockquote>
<p>可能会有问题，欢迎评论区留言</p>
</blockquote>
<hr>
<h2 id="目录">目录</h2>
<ol>
<li><a href="#1-%E6%A0%B8]]>
    </summary>
    <title>langchain与rag简单快速入门</title>
    <updated>2026-09-11T13:48:57.151Z</updated>
  </entry>
  <entry>
    <author>
      <name>Furina</name>
    </author>
    <category term="SQLAlchemy" scheme="https://www.furinaluna.top/categories/SQLAlchemy/"/>
    <category term="SQLAlchemy" scheme="https://www.furinaluna.top/tags/SQLAlchemy/"/>
    <content>
      <![CDATA[<h1>SQLAlchemy 精通教程</h1><blockquote><p><strong>之前写了一个sqlalchemy的简单实践，这次从入门到精通，自我总结，可能有缺，持续更新。。。</strong></p></blockquote><hr><h2 id="目录">目录</h2><ol><li><a href="#1-sqlalchemy-%E7%AE%80%E4%BB%8B">SQLAlchemy 简介</a></li><li><a href="#2-%E6%A0%B8%E5%BF%83%E6%A6%82%E5%BF%B5%E4%B8%80%E5%9B%BE%E6%B5%81">核心概念一图流</a></li><li><a href="#3-%E5%BF%AB%E9%80%9F%E5%BC%80%E5%A7%8B">快速开始</a></li><li><a href="#4-%E5%A3%B0%E6%98%8E%E5%BC%8F%E6%A8%A1%E5%9E%8B">声明式模型</a></li><li><a href="#5-%E6%A0%B8%E5%BF%83%E6%A6%82%E5%BF%B5%E8%AF%A6%E8%A7%A3">核心概念详解</a></li><li><a href="#6-crud-%E6%93%8D%E4%BD%9C">CRUD 操作</a></li><li><a href="#7-%E5%85%B3%E7%B3%BB%E6%98%A0%E5%B0%84">关系映射</a></li><li><a href="#8-%E6%9F%A5%E8%AF%A2%E9%AB%98%E7%BA%A7%E7%94%A8%E6%B3%95">查询高级用法</a></li><li><a href="#9-%E4%BC%9A%E8%AF%9D%E7%AE%A1%E7%90%86">会话管理</a></li><li><a href="#10-%E4%BA%8B%E5%8A%A1%E4%B8%8E%E9%94%81">事务与锁</a></li><li><a href="#11-%E5%BC%82%E6%AD%A5%E6%94%AF%E6%8C%81-20">异步支持 (2.0+)</a></li><li><a href="#12-%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96">性能优化</a></li><li><a href="#13-%E8%BF%81%E7%A7%BB%E7%AE%A1%E7%90%86-alembic">迁移管理 (Alembic)</a></li><li><a href="#14-%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5">最佳实践</a></li></ol><hr><h2 id="1-SQLAlchemy-简介">1. SQLAlchemy 简介</h2><h3 id="什么是-SQLAlchemy？">什么是 SQLAlchemy？</h3><p><strong>SQLAlchemy</strong> 是 Python 中最著名的 SQL 工具包和 ORM（对象关系映射）框架。</p><figure class="highlight lasso"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs lasso">┌─────────────────────────────────────────────────────────┐<br>│                    SQLAlchemy 架构                        │<br>├─────────────────────────────────────────────────────────┤<br>│                                                         │<br>│   应用层：你的 Python 代码                                  │<br>│         ↓                                               │<br>│   ORM 层：将 Python 类映射到数据库表                         │<br>│         ↓                                               │<br>│   Core 层：SQL 表达式语言                                 │<br>│         ↓                                               │<br>│   方言层：适配不同数据库 (MySQL, PostgreSQL, SQLite<span class="hljs-params">...</span>)   │<br>│         ↓                                               │<br>│   DBAPI：数据库驱动 (pymysql, psycopg2, aiomysql<span class="hljs-params">...</span>)     │<br>│                                                         │<br>└─────────────────────────────────────────────────────────┘<br></code></pre></td></tr></table></figure><h3 id="SQLAlchemy-1-x-vs-2-0">SQLAlchemy 1.x vs 2.0</h3><table><thead><tr><th>特性</th><th>1.x</th><th>2.0</th></tr></thead><tbody><tr><td>异步支持</td><td>实验性</td><td>完整支持</td></tr><tr><td>类型提示</td><td>有限</td><td>完整支持</td></tr><tr><td>API 风格</td><td>混合</td><td>更清晰分离</td></tr><tr><td>推荐用法</td><td><code>session.query()</code></td><td><code>select()</code></td></tr></tbody></table><h3 id="安装">安装</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-comment"># 基础安装</span><br>pip install sqlalchemy<br><br><span class="hljs-comment"># MySQL 支持</span><br>pip install pymysql<br>pip install sqlalchemy[mysql]<br><br><span class="hljs-comment"># PostgreSQL 支持</span><br>pip install psycopg2-binary<br>pip install sqlalchemy[postgresql]<br><br><span class="hljs-comment"># 异步支持</span><br>pip install aiomysql      <span class="hljs-comment"># MySQL 异步</span><br>pip install asyncpg       <span class="hljs-comment"># PostgreSQL 异步</span><br></code></pre></td></tr></table></figure><hr><h2 id="2-核心概念一图流">2. 核心概念一图流</h2><figure class="highlight pgsql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><code class="hljs pgsql">┌────────────────────────────────────────────────────────────────┐<br>│                     SQLAlchemy 核心概念                          │<br>├────────────────────────────────────────────────────────────────┤<br>│                                                                │<br>│  Engine（引擎）                                                 │<br>│  └── 数据库连接的工厂，管理连接池                               │<br>│      └── URL: &quot;dialect+driver://user:pass@host/db&quot;             │<br>│                                                                │<br>│  Metadata（元数据）                                             │<br>│  └── 存储表结构信息，用于创建/删除表                            │<br>│                                                                │<br>│  <span class="hljs-keyword">Table</span>（表） - Core 层                                          │<br>│  └── 定义数据库表结构（列、约束等）                              │<br>│                                                                │<br>│  Model（模型） - ORM 层                                         │<br>│  └── Python 类，映射到数据库表                                   │<br>│      └── 属性 → 列，实例 → 行                                   │<br>│                                                                │<br>│  <span class="hljs-keyword">Session</span>（会话）                                                │<br>│  └── 与数据库的&quot;对话&quot;，负责 CRUD 操作                            │<br>│      └── 类似&quot;暂存区&quot;，<span class="hljs-keyword">commit</span> 后才写入数据库                      │<br>│                                                                │<br>│  Query / <span class="hljs-keyword">select</span>()                                               │<br>│  └── 构建 <span class="hljs-keyword">SQL</span> 查询                                               │<br>│                                                                │<br>└────────────────────────────────────────────────────────────────┘<br></code></pre></td></tr></table></figure><h3 id="典型工作流程">典型工作流程</h3><figure class="highlight pgsql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs pgsql"><span class="hljs-number">1.</span> 创建 Engine → <span class="hljs-number">2.</span> 创建 <span class="hljs-keyword">Session</span> → <span class="hljs-number">3.</span> 定义模型<br>                                        ↓<br><span class="hljs-number">4.</span> 使用 <span class="hljs-keyword">Session</span> 执行查询/操作 → <span class="hljs-number">5.</span> 提交事务 → <span class="hljs-number">6.</span> 关闭 <span class="hljs-keyword">Session</span><br></code></pre></td></tr></table></figure><hr><h2 id="3-快速开始">3. 快速开始</h2><h3 id="最小可运行示例">最小可运行示例</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> sqlalchemy <span class="hljs-keyword">import</span> create_engine, Column, Integer, String<br><span class="hljs-keyword">from</span> sqlalchemy.orm <span class="hljs-keyword">import</span> declarative_base, sessionmaker<br><br><span class="hljs-comment"># 1. 创建引擎（数据库连接）</span><br>engine = create_engine(<span class="hljs-string">&quot;sqlite:///example.db&quot;</span>, echo=<span class="hljs-literal">True</span>)<br><br><span class="hljs-comment"># 2. 创建基类</span><br>Base = declarative_base()<br><br><span class="hljs-comment"># 3. 定义模型</span><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">User</span>(<span class="hljs-title class_ inherited__">Base</span>):<br>    __tablename__ = <span class="hljs-string">&quot;users&quot;</span><br>    <br>    <span class="hljs-built_in">id</span> = Column(Integer, primary_key=<span class="hljs-literal">True</span>)<br>    name = Column(String(<span class="hljs-number">50</span>))<br>    email = Column(String(<span class="hljs-number">100</span>))<br><br><span class="hljs-comment"># 4. 创建表</span><br>Base.metadata.create_all(engine)<br><br><span class="hljs-comment"># 5. 创建会话</span><br>Session = sessionmaker(bind=engine)<br>session = Session()<br><br><span class="hljs-comment"># 6. 添加数据</span><br>user = User(name=<span class="hljs-string">&quot;张三&quot;</span>, email=<span class="hljs-string">&quot;zhangsan@example.com&quot;</span>)<br>session.add(user)<br>session.commit()<br><br><span class="hljs-comment"># 7. 查询数据</span><br>users = session.query(User).<span class="hljs-built_in">all</span>()<br><span class="hljs-keyword">for</span> u <span class="hljs-keyword">in</span> users:<br>    <span class="hljs-built_in">print</span>(<span class="hljs-string">f&quot;<span class="hljs-subst">&#123;u.<span class="hljs-built_in">id</span>&#125;</span>: <span class="hljs-subst">&#123;u.name&#125;</span> - <span class="hljs-subst">&#123;u.email&#125;</span>&quot;</span>)<br><br><span class="hljs-comment"># 8. 关闭会话</span><br>session.close()<br></code></pre></td></tr></table></figure><h3 id="连接字符串详解">连接字符串详解</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> sqlalchemy <span class="hljs-keyword">import</span> create_engine<br><br><span class="hljs-comment"># SQLite（文件）</span><br>engine = create_engine(<span class="hljs-string">&quot;sqlite:///example.db&quot;</span>)<br><br><span class="hljs-comment"># SQLite（内存）</span><br>engine = create_engine(<span class="hljs-string">&quot;sqlite:///:memory:&quot;</span>)<br><br><span class="hljs-comment"># MySQL</span><br>engine = create_engine(<br>    <span class="hljs-string">&quot;mysql+pymysql://user:password@localhost:3306/dbname?charset=utf8mb4&quot;</span><br>)<br><br><span class="hljs-comment"># PostgreSQL</span><br>engine = create_engine(<br>    <span class="hljs-string">&quot;postgresql+psycopg2://user:password@localhost:5432/dbname&quot;</span><br>)<br><br><span class="hljs-comment"># PostgreSQL（异步）</span><br>engine = create_async_engine(<br>    <span class="hljs-string">&quot;postgresql+asyncpg://user:password@localhost:5432/dbname&quot;</span><br>)<br><br><span class="hljs-comment"># 带连接池配置</span><br>engine = create_engine(<br>    <span class="hljs-string">&quot;sqlite:///example.db&quot;</span>,<br>    pool_size=<span class="hljs-number">10</span>,           <span class="hljs-comment"># 连接池大小</span><br>    max_overflow=<span class="hljs-number">20</span>,        <span class="hljs-comment"># 超出 pool_size 后允许的最大连接数</span><br>    pool_pre_ping=<span class="hljs-literal">True</span>,     <span class="hljs-comment"># 使用前检查连接是否有效</span><br>    pool_recycle=<span class="hljs-number">3600</span>,      <span class="hljs-comment"># 连接回收时间（秒）</span><br>)<br></code></pre></td></tr></table></figure><hr><h2 id="4-声明式模型">4. 声明式模型</h2><h3 id="基础模型定义">基础模型定义</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> sqlalchemy <span class="hljs-keyword">import</span> Column, Integer, String, Float, DateTime, Boolean, Text<br><span class="hljs-keyword">from</span> sqlalchemy.orm <span class="hljs-keyword">import</span> declarative_base<br><span class="hljs-keyword">from</span> datetime <span class="hljs-keyword">import</span> datetime<br><br>Base = declarative_base()<br><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">User</span>(<span class="hljs-title class_ inherited__">Base</span>):<br>    __tablename__ = <span class="hljs-string">&quot;users&quot;</span>  <span class="hljs-comment"># 表名</span><br>    <br>    <span class="hljs-comment"># 主键</span><br>    <span class="hljs-built_in">id</span> = Column(Integer, primary_key=<span class="hljs-literal">True</span>, autoincrement=<span class="hljs-literal">True</span>)<br>    <br>    <span class="hljs-comment"># 普通字段</span><br>    username = Column(String(<span class="hljs-number">50</span>), nullable=<span class="hljs-literal">False</span>, unique=<span class="hljs-literal">True</span>)<br>    email = Column(String(<span class="hljs-number">100</span>), nullable=<span class="hljs-literal">False</span>)<br>    password_hash = Column(String(<span class="hljs-number">255</span>), nullable=<span class="hljs-literal">False</span>)<br>    <br>    <span class="hljs-comment"># 带默认值</span><br>    is_active = Column(Boolean, default=<span class="hljs-literal">True</span>)<br>    created_at = Column(DateTime, default=datetime.utcnow)<br>    <br>    <span class="hljs-comment"># 大文本</span><br>    bio = Column(Text)<br>    <br>    <span class="hljs-comment"># 数值类型</span><br>    age = Column(Integer)<br>    balance = Column(Float, default=<span class="hljs-number">0.0</span>)<br>    <br>    <span class="hljs-keyword">def</span> <span class="hljs-title function_">__repr__</span>(<span class="hljs-params">self</span>):<br>        <span class="hljs-keyword">return</span> <span class="hljs-string">f&quot;&lt;User(id=<span class="hljs-subst">&#123;self.<span class="hljs-built_in">id</span>&#125;</span>, username=<span class="hljs-subst">&#123;self.username&#125;</span>)&gt;&quot;</span><br></code></pre></td></tr></table></figure><h3 id="常用字段类型">常用字段类型</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> sqlalchemy <span class="hljs-keyword">import</span> (<br>    <span class="hljs-comment"># 字符串类型</span><br>    String, Text, Unicode,<br>    <br>    <span class="hljs-comment"># 整数类型</span><br>    Integer, SmallInteger, BigInteger,<br>    <br>    <span class="hljs-comment"># 浮点类型</span><br>    Float, Numeric, Decimal,<br>    <br>    <span class="hljs-comment"># 日期时间</span><br>    Date, Time, DateTime,<br>    <br>    <span class="hljs-comment"># 布尔</span><br>    Boolean,<br>    <br>    <span class="hljs-comment"># 二进制</span><br>    LargeBinary, PickleType,<br>    <br>    <span class="hljs-comment"># JSON (PostgreSQL/MySQL 5.7+)</span><br>    JSON,<br>    <br>    <span class="hljs-comment"># UUID</span><br>    UUID,<br>    <br>    <span class="hljs-comment"># 其他</span><br>    Enum, Interval,<br>)<br><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">AllTypes</span>(<span class="hljs-title class_ inherited__">Base</span>):<br>    __tablename__ = <span class="hljs-string">&quot;all_types&quot;</span><br>    <br>    <span class="hljs-built_in">id</span> = Column(Integer, primary_key=<span class="hljs-literal">True</span>)<br>    <br>    <span class="hljs-comment"># 字符串</span><br>    short_text = Column(String(<span class="hljs-number">50</span>))      <span class="hljs-comment"># VARCHAR(50)</span><br>    long_text = Column(Text)             <span class="hljs-comment"># TEXT</span><br>    <br>    <span class="hljs-comment"># 整数</span><br>    small_num = Column(SmallInteger)     <span class="hljs-comment"># SMALLINT</span><br>    normal_num = Column(Integer)         <span class="hljs-comment"># INTEGER</span><br>    big_num = Column(BigInteger)         <span class="hljs-comment"># BIGINT</span><br>    <br>    <span class="hljs-comment"># 浮点</span><br>    float_num = Column(Float)            <span class="hljs-comment"># FLOAT</span><br>    precise_num = Column(Numeric(<span class="hljs-number">10</span>, <span class="hljs-number">2</span>)) <span class="hljs-comment"># DECIMAL(10, 2)</span><br>    <br>    <span class="hljs-comment"># 日期时间</span><br>    date_only = Column(Date)             <span class="hljs-comment"># DATE</span><br>    time_only = Column(Time)             <span class="hljs-comment"># TIME</span><br>    datetime_full = Column(DateTime)     <span class="hljs-comment"># DATETIME</span><br>    <br>    <span class="hljs-comment"># 布尔</span><br>    is_true = Column(Boolean)            <span class="hljs-comment"># BOOLEAN</span><br>    <br>    <span class="hljs-comment"># JSON</span><br>    data = Column(JSON)                  <span class="hljs-comment"># JSON</span><br>    <br>    <span class="hljs-comment"># 带枚举</span><br>    status = Column(Enum(<span class="hljs-string">&quot;active&quot;</span>, <span class="hljs-string">&quot;inactive&quot;</span>, <span class="hljs-string">&quot;banned&quot;</span>))<br></code></pre></td></tr></table></figure><h3 id="字段选项详解">字段选项详解</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> sqlalchemy <span class="hljs-keyword">import</span> Column, Integer, String, ForeignKey, Index, UniqueConstraint<br><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">DetailedModel</span>(<span class="hljs-title class_ inherited__">Base</span>):<br>    __tablename__ = <span class="hljs-string">&quot;detailed&quot;</span><br>    <br>    <span class="hljs-built_in">id</span> = Column(<br>        Integer,<br>        primary_key=<span class="hljs-literal">True</span>,        <span class="hljs-comment"># 主键</span><br>        autoincrement=<span class="hljs-literal">True</span>,      <span class="hljs-comment"># 自增</span><br>        nullable=<span class="hljs-literal">False</span>,          <span class="hljs-comment"># 不允许 NULL</span><br>        index=<span class="hljs-literal">True</span>,              <span class="hljs-comment"># 创建索引</span><br>        unique=<span class="hljs-literal">True</span>,             <span class="hljs-comment"># 唯一约束</span><br>    )<br>    <br>    name = Column(<br>        String(<span class="hljs-number">50</span>),<br>        nullable=<span class="hljs-literal">False</span>,          <span class="hljs-comment"># 不允许 NULL</span><br>        default=<span class="hljs-string">&quot;Unnamed&quot;</span>,       <span class="hljs-comment"># 默认值（Python 层）</span><br>        server_default=<span class="hljs-string">&quot;Unnamed&quot;</span>, <span class="hljs-comment"># 默认值（数据库层）</span><br>    )<br>    <br>    email = Column(<br>        String(<span class="hljs-number">100</span>),<br>        unique=<span class="hljs-literal">True</span>,             <span class="hljs-comment"># 唯一约束</span><br>        index=<span class="hljs-literal">True</span>,              <span class="hljs-comment"># 索引</span><br>    )<br>    <br>    category_id = Column(<br>        Integer,<br>        ForeignKey(<span class="hljs-string">&quot;categories.id&quot;</span>, ondelete=<span class="hljs-string">&quot;CASCADE&quot;</span>),  <span class="hljs-comment"># 外键 + 级联删除</span><br>        nullable=<span class="hljs-literal">True</span>,<br>    )<br>    <br>    <span class="hljs-comment"># 复合唯一约束</span><br>    __table_args__ = (<br>        UniqueConstraint(<span class="hljs-string">&quot;name&quot;</span>, <span class="hljs-string">&quot;category_id&quot;</span>, name=<span class="hljs-string">&quot;uq_name_category&quot;</span>),<br>        Index(<span class="hljs-string">&quot;ix_email_status&quot;</span>, <span class="hljs-string">&quot;email&quot;</span>, <span class="hljs-string">&quot;status&quot;</span>),  <span class="hljs-comment"># 复合索引</span><br>        &#123;<span class="hljs-string">&quot;mysql_engine&quot;</span>: <span class="hljs-string">&quot;InnoDB&quot;</span>, <span class="hljs-string">&quot;mysql_charset&quot;</span>: <span class="hljs-string">&quot;utf8mb4&quot;</span>&#125;,  <span class="hljs-comment"># MySQL 特定选项</span><br>    )<br></code></pre></td></tr></table></figure><hr><h2 id="5-核心概念详解">5. 核心概念详解</h2><h3 id="Engine（引擎）">Engine（引擎）</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> sqlalchemy <span class="hljs-keyword">import</span> create_engine, text<br><br><span class="hljs-comment"># 创建引擎</span><br>engine = create_engine(<br>    <span class="hljs-string">&quot;sqlite:///example.db&quot;</span>,<br>    echo=<span class="hljs-literal">True</span>,           <span class="hljs-comment"># 打印 SQL 语句（调试用）</span><br>    pool_pre_ping=<span class="hljs-literal">True</span>,  <span class="hljs-comment"># 自动重连</span><br>)<br><br><span class="hljs-comment"># 直接执行原生 SQL</span><br><span class="hljs-keyword">with</span> engine.connect() <span class="hljs-keyword">as</span> conn:<br>    result = conn.execute(text(<span class="hljs-string">&quot;SELECT version()&quot;</span>))<br>    <span class="hljs-built_in">print</span>(result.fetchone())<br><br><span class="hljs-comment"># 事务执行</span><br><span class="hljs-keyword">with</span> engine.begin() <span class="hljs-keyword">as</span> conn:<br>    conn.execute(text(<span class="hljs-string">&quot;INSERT INTO users (name) VALUES (&#x27;test&#x27;)&quot;</span>))<br>    <span class="hljs-comment"># 自动 commit，如果异常则 rollback</span><br></code></pre></td></tr></table></figure><h3 id="Metadata（元数据）">Metadata（元数据）</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> sqlalchemy <span class="hljs-keyword">import</span> MetaData, Table, create_engine<br><br>engine = create_engine(<span class="hljs-string">&quot;sqlite:///example.db&quot;</span>)<br>metadata = MetaData()<br><br><span class="hljs-comment"># 从现有数据库反射表结构</span><br>users_table = Table(<span class="hljs-string">&quot;users&quot;</span>, metadata, autoload_with=engine)<br><br><span class="hljs-comment"># 查看所有表</span><br><span class="hljs-built_in">print</span>(metadata.tables.keys())<br><br><span class="hljs-comment"># 创建所有表</span><br>metadata.create_all(engine)<br><br><span class="hljs-comment"># 删除所有表</span><br>metadata.drop_all(engine)<br><br><span class="hljs-comment"># 反射整个数据库</span><br>metadata.reflect(engine)<br></code></pre></td></tr></table></figure><h3 id="核心层-Table（不使用-ORM）">核心层 Table（不使用 ORM）</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> sqlalchemy <span class="hljs-keyword">import</span> Table, Column, Integer, String, MetaData, insert, select<br><br>metadata = MetaData()<br><br><span class="hljs-comment"># 定义表（Core 方式）</span><br>users = Table(<br>    <span class="hljs-string">&quot;users&quot;</span>,<br>    metadata,<br>    Column(<span class="hljs-string">&quot;id&quot;</span>, Integer, primary_key=<span class="hljs-literal">True</span>),<br>    Column(<span class="hljs-string">&quot;name&quot;</span>, String(<span class="hljs-number">50</span>)),<br>    Column(<span class="hljs-string">&quot;email&quot;</span>, String(<span class="hljs-number">100</span>)),<br>)<br><br><span class="hljs-comment"># 插入数据</span><br>stmt = insert(users).values(name=<span class="hljs-string">&quot;张三&quot;</span>, email=<span class="hljs-string">&quot;zhang@example.com&quot;</span>)<br><br><span class="hljs-keyword">with</span> engine.begin() <span class="hljs-keyword">as</span> conn:<br>    result = conn.execute(stmt)<br>    <span class="hljs-built_in">print</span>(<span class="hljs-string">f&quot;插入的 ID: <span class="hljs-subst">&#123;result.inserted_primary_key[<span class="hljs-number">0</span>]&#125;</span>&quot;</span>)<br><br><span class="hljs-comment"># 查询</span><br>stmt = select(users).where(users.c.name == <span class="hljs-string">&quot;张三&quot;</span>)<br><span class="hljs-keyword">with</span> engine.connect() <span class="hljs-keyword">as</span> conn:<br>    result = conn.execute(stmt)<br>    <span class="hljs-keyword">for</span> row <span class="hljs-keyword">in</span> result:<br>        <span class="hljs-built_in">print</span>(row.<span class="hljs-built_in">id</span>, row.name, row.email)<br></code></pre></td></tr></table></figure><hr><h2 id="6-CRUD-操作">6. CRUD 操作</h2><h3 id="创建-Create">创建 (Create)</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> sqlalchemy.orm <span class="hljs-keyword">import</span> Session<br><br><span class="hljs-comment"># 创建单个对象</span><br>user = User(name=<span class="hljs-string">&quot;李四&quot;</span>, email=<span class="hljs-string">&quot;lisi@example.com&quot;</span>)<br>session.add(user)<br>session.commit()<br>session.refresh(user)  <span class="hljs-comment"># 刷新获取自增 ID</span><br><span class="hljs-built_in">print</span>(<span class="hljs-string">f&quot;ID: <span class="hljs-subst">&#123;user.<span class="hljs-built_in">id</span>&#125;</span>&quot;</span>)<br><br><span class="hljs-comment"># 批量添加</span><br>users = [<br>    User(name=<span class="hljs-string">&quot;用户 1&quot;</span>, email=<span class="hljs-string">&quot;u1@example.com&quot;</span>),<br>    User(name=<span class="hljs-string">&quot;用户 2&quot;</span>, email=<span class="hljs-string">&quot;u2@example.com&quot;</span>),<br>    User(name=<span class="hljs-string">&quot;用户 3&quot;</span>, email=<span class="hljs-string">&quot;u3@example.com&quot;</span>),<br>]<br>session.add_all(users)<br>session.commit()<br><br><span class="hljs-comment"># 使用字典批量创建</span><br>data = [<br>    &#123;<span class="hljs-string">&quot;name&quot;</span>: <span class="hljs-string">&quot;张三&quot;</span>, <span class="hljs-string">&quot;email&quot;</span>: <span class="hljs-string">&quot;zhang@example.com&quot;</span>&#125;,<br>    &#123;<span class="hljs-string">&quot;name&quot;</span>: <span class="hljs-string">&quot;李四&quot;</span>, <span class="hljs-string">&quot;email&quot;</span>: <span class="hljs-string">&quot;li@example.com&quot;</span>&#125;,<br>]<br>session.bulk_insert_mappings(User, data)<br>session.commit()<br></code></pre></td></tr></table></figure><h3 id="查询-Read">查询 (Read)</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> sqlalchemy <span class="hljs-keyword">import</span> select<br><br><span class="hljs-comment"># 方法 1：传统 query API（1.x 风格，但仍可用）</span><br>user = session.query(User).first()           <span class="hljs-comment"># 第一条</span><br>user = session.query(User).get(<span class="hljs-number">1</span>)            <span class="hljs-comment"># 按 ID 获取</span><br>users = session.query(User).<span class="hljs-built_in">all</span>()            <span class="hljs-comment"># 全部</span><br>users = session.query(User).limit(<span class="hljs-number">10</span>).<span class="hljs-built_in">all</span>()  <span class="hljs-comment"># 限制 10 条</span><br>users = session.query(User).offset(<span class="hljs-number">10</span>).<span class="hljs-built_in">all</span>() <span class="hljs-comment"># 跳过 10 条</span><br><br><span class="hljs-comment"># 方法 2：2.0 推荐 select() API</span><br>stmt = select(User)<br>user = session.execute(stmt).first()<br><br>stmt = select(User).limit(<span class="hljs-number">10</span>)<br>users = session.execute(stmt).scalars().<span class="hljs-built_in">all</span>()<br><br><span class="hljs-comment"># 条件查询</span><br>user = session.query(User).<span class="hljs-built_in">filter</span>(User.<span class="hljs-built_in">id</span> == <span class="hljs-number">1</span>).first()<br>users = session.query(User).<span class="hljs-built_in">filter</span>(User.name == <span class="hljs-string">&quot;张三&quot;</span>).<span class="hljs-built_in">all</span>()<br><br><span class="hljs-comment"># 多个条件</span><br>users = session.query(User).<span class="hljs-built_in">filter</span>(<br>    User.is_active == <span class="hljs-literal">True</span>,<br>    User.age &gt;= <span class="hljs-number">18</span><br>).<span class="hljs-built_in">all</span>()<br><br><span class="hljs-comment"># OR 条件</span><br><span class="hljs-keyword">from</span> sqlalchemy <span class="hljs-keyword">import</span> or_<br>users = session.query(User).<span class="hljs-built_in">filter</span>(<br>    or_(User.name == <span class="hljs-string">&quot;张三&quot;</span>, User.name == <span class="hljs-string">&quot;李四&quot;</span>)<br>).<span class="hljs-built_in">all</span>()<br><br><span class="hljs-comment"># LIKE 查询</span><br>users = session.query(User).<span class="hljs-built_in">filter</span>(<br>    User.email.like(<span class="hljs-string">&quot;%@gmail.com&quot;</span>)<br>).<span class="hljs-built_in">all</span>()<br><br><span class="hljs-comment"># IN 查询</span><br>users = session.query(User).<span class="hljs-built_in">filter</span>(<br>    User.<span class="hljs-built_in">id</span>.in_([<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>])<br>).<span class="hljs-built_in">all</span>()<br><br><span class="hljs-comment"># 排序</span><br>users = session.query(User).order_by(User.created_at.desc()).<span class="hljs-built_in">all</span>()<br>users = session.query(User).order_by(User.name.asc(), User.<span class="hljs-built_in">id</span>.desc()).<span class="hljs-built_in">all</span>()<br><br><span class="hljs-comment"># 只查询特定字段</span><br>results = session.query(User.<span class="hljs-built_in">id</span>, User.name).<span class="hljs-built_in">all</span>()<br><span class="hljs-keyword">for</span> <span class="hljs-built_in">id</span>, name <span class="hljs-keyword">in</span> results:<br>    <span class="hljs-built_in">print</span>(<span class="hljs-built_in">id</span>, name)<br><br><span class="hljs-comment"># 计数</span><br>count = session.query(User).count()<br><br><span class="hljs-comment"># 聚合</span><br><span class="hljs-keyword">from</span> sqlalchemy <span class="hljs-keyword">import</span> func<br>max_id = session.query(func.<span class="hljs-built_in">max</span>(User.<span class="hljs-built_in">id</span>)).scalar()<br>avg_age = session.query(func.avg(User.age)).scalar()<br><br><span class="hljs-comment"># 分组</span><br>results = session.query(<br>    User.status,<br>    func.count(User.<span class="hljs-built_in">id</span>)<br>).group_by(User.status).<span class="hljs-built_in">all</span>()<br></code></pre></td></tr></table></figure><h3 id="更新-Update">更新 (Update)</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-comment"># 方法 1：获取对象后修改</span><br>user = session.query(User).get(<span class="hljs-number">1</span>)<br><span class="hljs-keyword">if</span> user:<br>    user.name = <span class="hljs-string">&quot;新名字&quot;</span><br>    user.email = <span class="hljs-string">&quot;new@example.com&quot;</span><br>    session.commit()<br><br><span class="hljs-comment"># 方法 2：bulk_update（批量更新）</span><br>session.bulk_update_mappings(<br>    User,<br>    [&#123;<span class="hljs-string">&quot;id&quot;</span>: <span class="hljs-number">1</span>, <span class="hljs-string">&quot;name&quot;</span>: <span class="hljs-string">&quot;更新 1&quot;</span>&#125;, &#123;<span class="hljs-string">&quot;id&quot;</span>: <span class="hljs-number">2</span>, <span class="hljs-string">&quot;name&quot;</span>: <span class="hljs-string">&quot;更新 2&quot;</span>&#125;]<br>)<br>session.commit()<br><br><span class="hljs-comment"># 方法 3：SQL 更新（高效，不加载对象）</span><br><span class="hljs-keyword">from</span> sqlalchemy <span class="hljs-keyword">import</span> update<br><br>stmt = update(User).where(User.<span class="hljs-built_in">id</span> == <span class="hljs-number">1</span>).values(name=<span class="hljs-string">&quot;新名字&quot;</span>)<br>session.execute(stmt)<br>session.commit()<br><br><span class="hljs-comment"># 批量条件更新</span><br>stmt = update(User).where(User.is_active == <span class="hljs-literal">False</span>).values(is_active=<span class="hljs-literal">True</span>)<br>session.execute(stmt)<br>session.commit()<br></code></pre></td></tr></table></figure><h3 id="删除-Delete">删除 (Delete)</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-comment"># 方法 1：获取对象后删除</span><br>user = session.query(User).get(<span class="hljs-number">1</span>)<br><span class="hljs-keyword">if</span> user:<br>    session.delete(user)<br>    session.commit()<br><br><span class="hljs-comment"># 方法 2：SQL 删除</span><br><span class="hljs-keyword">from</span> sqlalchemy <span class="hljs-keyword">import</span> delete<br><br>stmt = delete(User).where(User.<span class="hljs-built_in">id</span> == <span class="hljs-number">1</span>)<br>session.execute(stmt)<br>session.commit()<br><br><span class="hljs-comment"># 批量删除</span><br>stmt = delete(User).where(User.is_active == <span class="hljs-literal">False</span>)<br>result = session.execute(stmt)<br><span class="hljs-built_in">print</span>(<span class="hljs-string">f&quot;删除了 <span class="hljs-subst">&#123;result.rowcount&#125;</span> 条记录&quot;</span>)<br>session.commit()<br></code></pre></td></tr></table></figure><hr><h2 id="7-关系映射">7. 关系映射</h2><h3 id="一对一关系">一对一关系</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> sqlalchemy <span class="hljs-keyword">import</span> ForeignKey<br><span class="hljs-keyword">from</span> sqlalchemy.orm <span class="hljs-keyword">import</span> relationship<br><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">User</span>(<span class="hljs-title class_ inherited__">Base</span>):<br>    __tablename__ = <span class="hljs-string">&quot;users&quot;</span><br>    <br>    <span class="hljs-built_in">id</span> = Column(Integer, primary_key=<span class="hljs-literal">True</span>)<br>    name = Column(String(<span class="hljs-number">50</span>))<br>    <br>    <span class="hljs-comment"># 关系定义</span><br>    profile = relationship(<span class="hljs-string">&quot;Profile&quot;</span>, back_populates=<span class="hljs-string">&quot;user&quot;</span>, uselist=<span class="hljs-literal">False</span>)<br><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">Profile</span>(<span class="hljs-title class_ inherited__">Base</span>):<br>    __tablename__ = <span class="hljs-string">&quot;profiles&quot;</span><br>    <br>    <span class="hljs-built_in">id</span> = Column(Integer, primary_key=<span class="hljs-literal">True</span>)<br>    bio = Column(Text)<br>    user_id = Column(Integer, ForeignKey(<span class="hljs-string">&quot;users.id&quot;</span>), unique=<span class="hljs-literal">True</span>)<br>    <br>    <span class="hljs-comment"># 反向关系</span><br>    user = relationship(<span class="hljs-string">&quot;User&quot;</span>, back_populates=<span class="hljs-string">&quot;profile&quot;</span>)<br><br><span class="hljs-comment"># 使用示例</span><br>user = User(name=<span class="hljs-string">&quot;张三&quot;</span>)<br>user.profile = Profile(bio=<span class="hljs-string">&quot;这是我的简介&quot;</span>)<br><br>session.add(user)<br>session.commit()<br><br><span class="hljs-comment"># 访问关联对象</span><br><span class="hljs-built_in">print</span>(user.profile.bio)<br><span class="hljs-built_in">print</span>(user.profile.user.name)  <span class="hljs-comment"># 反向访问</span><br></code></pre></td></tr></table></figure><h3 id="一对多关系">一对多关系</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">class</span> <span class="hljs-title class_">Parent</span>(<span class="hljs-title class_ inherited__">Base</span>):<br>    __tablename__ = <span class="hljs-string">&quot;parents&quot;</span><br>    <br>    <span class="hljs-built_in">id</span> = Column(Integer, primary_key=<span class="hljs-literal">True</span>)<br>    name = Column(String(<span class="hljs-number">50</span>))<br>    <br>    <span class="hljs-comment"># 一方：可以有多个孩子</span><br>    children = relationship(<span class="hljs-string">&quot;Child&quot;</span>, back_populates=<span class="hljs-string">&quot;parent&quot;</span>)<br><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">Child</span>(<span class="hljs-title class_ inherited__">Base</span>):<br>    __tablename__ = <span class="hljs-string">&quot;children&quot;</span><br>    <br>    <span class="hljs-built_in">id</span> = Column(Integer, primary_key=<span class="hljs-literal">True</span>)<br>    name = Column(String(<span class="hljs-number">50</span>))<br>    parent_id = Column(Integer, ForeignKey(<span class="hljs-string">&quot;parents.id&quot;</span>))<br>    <br>    <span class="hljs-comment"># 多方：每个孩子有一个父亲</span><br>    parent = relationship(<span class="hljs-string">&quot;Parent&quot;</span>, back_populates=<span class="hljs-string">&quot;children&quot;</span>)<br><br><span class="hljs-comment"># 使用示例</span><br>parent = Parent(name=<span class="hljs-string">&quot;父亲&quot;</span>)<br>parent.children = [<br>    Child(name=<span class="hljs-string">&quot;孩子 1&quot;</span>),<br>    Child(name=<span class="hljs-string">&quot;孩子 2&quot;</span>),<br>    Child(name=<span class="hljs-string">&quot;孩子 3&quot;</span>),<br>]<br><br>session.add(parent)<br>session.commit()<br><br><span class="hljs-comment"># 访问</span><br><span class="hljs-keyword">for</span> child <span class="hljs-keyword">in</span> parent.children:<br>    <span class="hljs-built_in">print</span>(<span class="hljs-string">f&quot;<span class="hljs-subst">&#123;child.name&#125;</span> 的父亲是 <span class="hljs-subst">&#123;child.parent.name&#125;</span>&quot;</span>)<br><br><span class="hljs-comment"># 查询某个父亲的所有孩子</span><br>children = session.query(Child).<span class="hljs-built_in">filter</span>(Child.parent_id == <span class="hljs-number">1</span>).<span class="hljs-built_in">all</span>()<br></code></pre></td></tr></table></figure><h3 id="多对多关系">多对多关系</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> sqlalchemy <span class="hljs-keyword">import</span> Table<br><br><span class="hljs-comment"># 关联表</span><br>user_roles = Table(<br>    <span class="hljs-string">&quot;user_roles&quot;</span>,<br>    Base.metadata,<br>    Column(<span class="hljs-string">&quot;user_id&quot;</span>, Integer, ForeignKey(<span class="hljs-string">&quot;users.id&quot;</span>)),<br>    Column(<span class="hljs-string">&quot;role_id&quot;</span>, Integer, ForeignKey(<span class="hljs-string">&quot;roles.id&quot;</span>)),<br>    PrimaryKeyConstraint(<span class="hljs-string">&quot;user_id&quot;</span>, <span class="hljs-string">&quot;role_id&quot;</span>),  <span class="hljs-comment"># 复合主键</span><br>)<br><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">User</span>(<span class="hljs-title class_ inherited__">Base</span>):<br>    __tablename__ = <span class="hljs-string">&quot;users&quot;</span><br>    <br>    <span class="hljs-built_in">id</span> = Column(Integer, primary_key=<span class="hljs-literal">True</span>)<br>    name = Column(String(<span class="hljs-number">50</span>))<br>    <br>    <span class="hljs-comment"># 多对多关系</span><br>    roles = relationship(<br>        <span class="hljs-string">&quot;Role&quot;</span>,<br>        secondary=user_roles,<br>        back_populates=<span class="hljs-string">&quot;users&quot;</span><br>    )<br><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">Role</span>(<span class="hljs-title class_ inherited__">Base</span>):<br>    __tablename__ = <span class="hljs-string">&quot;roles&quot;</span><br>    <br>    <span class="hljs-built_in">id</span> = Column(Integer, primary_key=<span class="hljs-literal">True</span>)<br>    name = Column(String(<span class="hljs-number">50</span>))<br>    <br>    users = relationship(<span class="hljs-string">&quot;User&quot;</span>, secondary=user_roles, back_populates=<span class="hljs-string">&quot;roles&quot;</span>)<br><br><span class="hljs-comment"># 使用示例</span><br>admin_role = Role(name=<span class="hljs-string">&quot;管理员&quot;</span>)<br>editor_role = Role(name=<span class="hljs-string">&quot;编辑&quot;</span>)<br><br>user = User(name=<span class="hljs-string">&quot;张三&quot;</span>)<br>user.roles = [admin_role, editor_role]<br><br>session.add_all([user, admin_role, editor_role])<br>session.commit()<br><br><span class="hljs-comment"># 查询用户的所有角色</span><br>user = session.query(User).<span class="hljs-built_in">filter</span>(User.name == <span class="hljs-string">&quot;张三&quot;</span>).first()<br><span class="hljs-keyword">for</span> role <span class="hljs-keyword">in</span> user.roles:<br>    <span class="hljs-built_in">print</span>(<span class="hljs-string">f&quot;角色：<span class="hljs-subst">&#123;role.name&#125;</span>&quot;</span>)<br><br><span class="hljs-comment"># 查询有管理员角色的所有用户</span><br>admins = session.query(User).<span class="hljs-built_in">filter</span>(<br>    User.roles.<span class="hljs-built_in">any</span>(Role.name == <span class="hljs-string">&quot;管理员&quot;</span>)<br>).<span class="hljs-built_in">all</span>()<br></code></pre></td></tr></table></figure><h3 id="级联操作">级联操作</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">class</span> <span class="hljs-title class_">Parent</span>(<span class="hljs-title class_ inherited__">Base</span>):<br>    __tablename__ = <span class="hljs-string">&quot;parents&quot;</span><br>    <br>    <span class="hljs-built_in">id</span> = Column(Integer, primary_key=<span class="hljs-literal">True</span>)<br>    name = Column(String(<span class="hljs-number">50</span>))<br>    <br>    <span class="hljs-comment"># 级联配置</span><br>    children = relationship(<br>        <span class="hljs-string">&quot;Child&quot;</span>,<br>        back_populates=<span class="hljs-string">&quot;parent&quot;</span>,<br>        cascade=<span class="hljs-string">&quot;all, delete-orphan&quot;</span>,  <span class="hljs-comment"># 删除父亲时删除所有孩子</span><br>    )<br><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">Child</span>(<span class="hljs-title class_ inherited__">Base</span>):<br>    __tablename__ = <span class="hljs-string">&quot;children&quot;</span><br>    <br>    <span class="hljs-built_in">id</span> = Column(Integer, primary_key=<span class="hljs-literal">True</span>)<br>    name = Column(String(<span class="hljs-number">50</span>))<br>    parent_id = Column(Integer, ForeignKey(<span class="hljs-string">&quot;parents.id&quot;</span>))<br>    parent = relationship(<span class="hljs-string">&quot;Parent&quot;</span>, back_populates=<span class="hljs-string">&quot;children&quot;</span>)<br><br><span class="hljs-comment"># cascade 选项说明：</span><br><span class="hljs-comment"># &quot;save-update&quot; - 保存/更新时级联</span><br><span class="hljs-comment"># &quot;merge&quot; - merge 操作时级联</span><br><span class="hljs-comment"># &quot;refresh-expire&quot; - refresh/expire 时级联</span><br><span class="hljs-comment"># &quot;expunge&quot; - expunge 时级联</span><br><span class="hljs-comment"># &quot;delete&quot; - 删除时级联</span><br><span class="hljs-comment"># &quot;delete-orphan&quot; - 删除孤儿记录</span><br><span class="hljs-comment"># &quot;all&quot; - 以上所有</span><br><span class="hljs-comment"># &quot;all, delete-orphan&quot; - 常用组合</span><br></code></pre></td></tr></table></figure><h3 id="自引用关系">自引用关系</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">class</span> <span class="hljs-title class_">Employee</span>(<span class="hljs-title class_ inherited__">Base</span>):<br>    __tablename__ = <span class="hljs-string">&quot;employees&quot;</span><br>    <br>    <span class="hljs-built_in">id</span> = Column(Integer, primary_key=<span class="hljs-literal">True</span>)<br>    name = Column(String(<span class="hljs-number">50</span>))<br>    <br>    <span class="hljs-comment"># 外键指向自己</span><br>    manager_id = Column(Integer, ForeignKey(<span class="hljs-string">&quot;employees.id&quot;</span>))<br>    <br>    <span class="hljs-comment"># 下属（一对多）</span><br>    subordinates = relationship(<br>        <span class="hljs-string">&quot;Employee&quot;</span>,<br>        back_populates=<span class="hljs-string">&quot;manager&quot;</span>,<br>        foreign_keys=[manager_id]<br>    )<br>    <br>    <span class="hljs-comment"># 上级（多对一）</span><br>    manager = relationship(<br>        <span class="hljs-string">&quot;Employee&quot;</span>,<br>        back_populates=<span class="hljs-string">&quot;subordinates&quot;</span>,<br>        remote_side=[<span class="hljs-built_in">id</span>]<br>    )<br><br><span class="hljs-comment"># 使用示例</span><br>ceo = Employee(name=<span class="hljs-string">&quot;CEO&quot;</span>)<br>manager = Employee(name=<span class="hljs-string">&quot;经理&quot;</span>, manager=ceo)<br>employee = Employee(name=<span class="hljs-string">&quot;员工&quot;</span>, manager=manager)<br><br>session.add_all([ceo, manager, employee])<br>session.commit()<br><br><span class="hljs-comment"># 访问</span><br><span class="hljs-built_in">print</span>(ceo.subordinates[<span class="hljs-number">0</span>].name)  <span class="hljs-comment"># 经理</span><br><span class="hljs-built_in">print</span>(employee.manager.name)     <span class="hljs-comment"># 经理</span><br><span class="hljs-built_in">print</span>(employee.manager.manager.name)  <span class="hljs-comment"># CEO</span><br></code></pre></td></tr></table></figure><hr><h2 id="8-查询高级用法">8. 查询高级用法</h2><h3 id="Join-查询">Join 查询</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> sqlalchemy.orm <span class="hljs-keyword">import</span> joinedload, selectinload<br><br><span class="hljs-comment"># 内连接</span><br>results = session.query(User, Profile).join(Profile).<span class="hljs-built_in">all</span>()<br><br><span class="hljs-comment"># 左外连接</span><br>results = session.query(User).outerjoin(Profile).<span class="hljs-built_in">all</span>()<br><br><span class="hljs-comment"># 多个 join</span><br>results = session.query(User).join(Profile).join(Post).<span class="hljs-built_in">all</span>()<br><br><span class="hljs-comment"># 使用关系自动推断连接条件</span><br>users_with_posts = session.query(User).join(User.posts).<span class="hljs-built_in">all</span>()<br><br><span class="hljs-comment"># 明确指定连接条件</span><br>results = session.query(User).join(<br>    Profile, User.<span class="hljs-built_in">id</span> == Profile.user_id<br>).<span class="hljs-built_in">all</span>()<br></code></pre></td></tr></table></figure><h3 id="预加载（解决-N-1-问题）">预加载（解决 N+1 问题）</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-comment"># 问题：N+1 查询</span><br><span class="hljs-comment"># 执行 1 次查询获取用户，然后每个用户执行 1 次查询获取帖子</span><br>users = session.query(User).<span class="hljs-built_in">all</span>()<br><span class="hljs-keyword">for</span> user <span class="hljs-keyword">in</span> users:<br>    <span class="hljs-built_in">print</span>(user.posts)  <span class="hljs-comment"># 每次都触发新查询！</span><br><br><span class="hljs-comment"># 解决 1：joinedload（适合一对一、一对少）</span><br>users = session.query(User).options(<br>    joinedload(User.profile)<br>).<span class="hljs-built_in">all</span>()<br><br><span class="hljs-comment"># 解决 2：selectinload（适合一对多）</span><br>users = session.query(User).options(<br>    selectinload(User.posts)<br>).<span class="hljs-built_in">all</span>()<br><br><span class="hljs-comment"># 解决 3：selectinload_all（多层嵌套）</span><br>users = session.query(User).options(<br>    selectinload_all(User.posts, User.profile)<br>).<span class="hljs-built_in">all</span>()<br><br><span class="hljs-comment"># 多层预加载</span><br>users = session.query(User).options(<br>    joinedload(User.posts).joinedload(Post.comments)<br>).<span class="hljs-built_in">all</span>()<br></code></pre></td></tr></table></figure><h3 id="子查询">子查询</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> sqlalchemy <span class="hljs-keyword">import</span> func<br><br><span class="hljs-comment"># 标量子查询</span><br>subquery = session.query(func.count(Post.<span class="hljs-built_in">id</span>)).<span class="hljs-built_in">filter</span>(<br>    Post.user_id == User.<span class="hljs-built_in">id</span><br>).correlate(User).as_scalar()<br><br>users = session.query(User, subquery.label(<span class="hljs-string">&quot;post_count&quot;</span>)).<span class="hljs-built_in">all</span>()<br><br><span class="hljs-comment"># IN 子查询</span><br>active_user_ids = session.query(User.<span class="hljs-built_in">id</span>).<span class="hljs-built_in">filter</span>(User.is_active == <span class="hljs-literal">True</span>)<br>posts = session.query(Post).<span class="hljs-built_in">filter</span>(<br>    Post.user_id.in_(active_user_ids)<br>).<span class="hljs-built_in">all</span>()<br><br><span class="hljs-comment"># EXISTS 子查询</span><br><span class="hljs-keyword">from</span> sqlalchemy <span class="hljs-keyword">import</span> exists<br><br>exists_query = exists().where(Post.user_id == User.<span class="hljs-built_in">id</span>)<br>users_with_posts = session.query(User).<span class="hljs-built_in">filter</span>(exists_query).<span class="hljs-built_in">all</span>()<br></code></pre></td></tr></table></figure><h3 id="窗口函数">窗口函数</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> sqlalchemy <span class="hljs-keyword">import</span> func, over<br><br><span class="hljs-comment"># ROW_NUMBER</span><br>query = session.query(<br>    User.name,<br>    User.salary,<br>    func.row_number().over(<br>        partition_by=User.department,<br>        order_by=User.salary.desc()<br>    ).label(<span class="hljs-string">&quot;rank&quot;</span>)<br>)<br><br><span class="hljs-comment"># 聚合窗口函数</span><br>query = session.query(<br>    User.name,<br>    User.salary,<br>    func.avg(User.salary).over(<br>        partition_by=User.department<br>    ).label(<span class="hljs-string">&quot;dept_avg&quot;</span>)<br>)<br></code></pre></td></tr></table></figure><h3 id="原生-SQL-查询">原生 SQL 查询</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> sqlalchemy <span class="hljs-keyword">import</span> text<br><br><span class="hljs-comment"># 参数化查询（防止 SQL 注入）</span><br>stmt = text(<span class="hljs-string">&quot;SELECT * FROM users WHERE id = :id&quot;</span>)<br>result = session.execute(stmt, &#123;<span class="hljs-string">&quot;id&quot;</span>: <span class="hljs-number">1</span>&#125;)<br><br><span class="hljs-comment"># 返回 ORM 对象</span><br>stmt = text(<span class="hljs-string">&quot;SELECT * FROM users WHERE id = :id&quot;</span>)<br>result = session.execute(stmt, &#123;<span class="hljs-string">&quot;id&quot;</span>: <span class="hljs-number">1</span>&#125;)<br>user = result.scalars().first()<br><br><span class="hljs-comment"># 混合使用 ORM 和原生 SQL</span><br><span class="hljs-keyword">from</span> sqlalchemy.orm <span class="hljs-keyword">import</span> Session<br><br><span class="hljs-keyword">with</span> Session(engine) <span class="hljs-keyword">as</span> session:<br>    <span class="hljs-comment"># 原生 SQL</span><br>    session.execute(text(<span class="hljs-string">&quot;UPDATE users SET active = 1 WHERE id = :id&quot;</span>), &#123;<span class="hljs-string">&quot;id&quot;</span>: <span class="hljs-number">1</span>&#125;)<br>    session.commit()<br></code></pre></td></tr></table></figure><hr><h2 id="9-会话管理">9. 会话管理</h2><h3 id="Session-生命周期">Session 生命周期</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> sqlalchemy.orm <span class="hljs-keyword">import</span> sessionmaker, Session, scoped_session<br><br><span class="hljs-comment"># 创建 Session 工厂</span><br>SessionLocal = sessionmaker(bind=engine, autocommit=<span class="hljs-literal">False</span>, autoflush=<span class="hljs-literal">False</span>)<br><br><span class="hljs-comment"># 方法 1：手动管理</span><br>session = SessionLocal()<br><span class="hljs-keyword">try</span>:<br>    <span class="hljs-comment"># 操作...</span><br>    session.commit()<br><span class="hljs-keyword">except</span>:<br>    session.rollback()<br>    <span class="hljs-keyword">raise</span><br><span class="hljs-keyword">finally</span>:<br>    session.close()<br><br><span class="hljs-comment"># 方法 2：上下文管理器（推荐）</span><br><span class="hljs-keyword">with</span> SessionLocal() <span class="hljs-keyword">as</span> session:<br>    <span class="hljs-keyword">try</span>:<br>        <span class="hljs-comment"># 操作...</span><br>        session.commit()<br>    <span class="hljs-keyword">except</span>:<br>        session.rollback()<br>        <span class="hljs-keyword">raise</span><br><br><span class="hljs-comment"># 方法 3：scoped_session（线程安全，适合 Web 应用）</span><br>db_session = scoped_session(SessionLocal)<br><br><span class="hljs-comment"># 在请求结束时清理</span><br><span class="hljs-meta">@app.teardown_appcontext</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">shutdown_session</span>(<span class="hljs-params">exception=<span class="hljs-literal">None</span></span>):<br>    db_session.remove()<br></code></pre></td></tr></table></figure><h3 id="会话状态详解">会话状态详解</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-comment"># 对象状态转换：</span><br><span class="hljs-comment"># </span><br><span class="hljs-comment"># transient → [add()] → pending → [commit()] → persistent</span><br><span class="hljs-comment">#                                ↓</span><br><span class="hljs-comment">#                           [rollback()]</span><br><span class="hljs-comment">#                                ↓</span><br><span class="hljs-comment">#                           detached ← [close()/expire()]</span><br><br><span class="hljs-keyword">from</span> sqlalchemy.orm <span class="hljs-keyword">import</span> Session<br><br>session = Session()<br><br>user = User(name=<span class="hljs-string">&quot;test&quot;</span>)<br><span class="hljs-built_in">print</span>(session.contains(user))  <span class="hljs-comment"># False - transient（瞬态）</span><br><br>session.add(user)<br><span class="hljs-built_in">print</span>(session.contains(user))  <span class="hljs-comment"># True - pending（待处理）</span><br><br>session.commit()<br><span class="hljs-built_in">print</span>(session.contains(user))  <span class="hljs-comment"># True - persistent（持久化）</span><br><br>session.close()<br><span class="hljs-built_in">print</span>(session.contains(user))  <span class="hljs-comment"># False - detached（游离）</span><br><br><span class="hljs-comment"># 重新附加</span><br>session.add(user)  <span class="hljs-comment"># 或 session.merge(user)</span><br></code></pre></td></tr></table></figure><h3 id="会话技巧">会话技巧</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-comment"># 批量保存（每 N 条提交一次）</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">batch_insert</span>(<span class="hljs-params">session, items, batch_size=<span class="hljs-number">1000</span></span>):<br>    <span class="hljs-keyword">for</span> i, item <span class="hljs-keyword">in</span> <span class="hljs-built_in">enumerate</span>(items):<br>        session.add(item)<br>        <span class="hljs-keyword">if</span> (i + <span class="hljs-number">1</span>) % batch_size == <span class="hljs-number">0</span>:<br>            session.commit()<br>            session.expunge_all()  <span class="hljs-comment"># 清空会话，释放内存</span><br>    session.commit()<br><br><span class="hljs-comment"># 刷新特定对象</span><br>session.refresh(user)  <span class="hljs-comment"># 从数据库重新加载数据</span><br><br><span class="hljs-comment"># 使对象过期（下次访问时重新加载）</span><br>session.expire(user)<br><br><span class="hljs-comment"># 清空整个会话</span><br>session.expunge_all()<br><br><span class="hljs-comment"># 部分刷新（只刷新特定属性）</span><br>session.refresh(user, attribute_names=[<span class="hljs-string">&quot;name&quot;</span>, <span class="hljs-string">&quot;email&quot;</span>])<br></code></pre></td></tr></table></figure><hr><h2 id="10-事务与锁">10. 事务与锁</h2><h3 id="事务管理">事务管理</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> sqlalchemy <span class="hljs-keyword">import</span> create_engine<br><br>engine = create_engine(<span class="hljs-string">&quot;sqlite:///example.db&quot;</span>)<br><br><span class="hljs-comment"># 方法 1：自动事务（推荐）</span><br><span class="hljs-keyword">with</span> engine.begin() <span class="hljs-keyword">as</span> conn:<br>    conn.execute(text(<span class="hljs-string">&quot;INSERT INTO users (name) VALUES (&#x27;Alice&#x27;)&quot;</span>))<br>    conn.execute(text(<span class="hljs-string">&quot;INSERT INTO users (name) VALUES (&#x27;Bob&#x27;)&quot;</span>))<br>    <span class="hljs-comment"># 如果都成功则 commit，否则自动 rollback</span><br><br><span class="hljs-comment"># 方法 2：显式事务</span><br><span class="hljs-keyword">with</span> engine.connect() <span class="hljs-keyword">as</span> conn:<br>    trans = conn.begin()<br>    <span class="hljs-keyword">try</span>:<br>        conn.execute(text(<span class="hljs-string">&quot;INSERT INTO users VALUES (1, &#x27;Alice&#x27;)&quot;</span>))<br>        conn.execute(text(<span class="hljs-string">&quot;INSERT INTO users VALUES (2, &#x27;Bob&#x27;)&quot;</span>))<br>        trans.commit()<br>    <span class="hljs-keyword">except</span>:<br>        trans.rollback()<br>        <span class="hljs-keyword">raise</span><br><br><span class="hljs-comment"># 方法 3：嵌套事务（保存点）</span><br><span class="hljs-keyword">with</span> engine.begin() <span class="hljs-keyword">as</span> conn:<br>    conn.execute(text(<span class="hljs-string">&quot;INSERT INTO users VALUES (1, &#x27;Alice&#x27;)&quot;</span>))<br>    <br>    <span class="hljs-keyword">with</span> conn.begin_nested():  <span class="hljs-comment"># 保存点</span><br>        conn.execute(text(<span class="hljs-string">&quot;INSERT INTO users VALUES (2, &#x27;Bob&#x27;)&quot;</span>))<br>        <span class="hljs-comment"># 如果这里失败，只回滚到这个保存点</span><br>        <span class="hljs-comment"># Alice 的插入仍然有效</span><br></code></pre></td></tr></table></figure><h3 id="锁机制">锁机制</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> sqlalchemy <span class="hljs-keyword">import</span> select<br><span class="hljs-keyword">from</span> sqlalchemy.orm <span class="hljs-keyword">import</span> Session<br><br>session = Session()<br><br><span class="hljs-comment"># 行级锁 - for_update（排他锁）</span><br>user = session.query(User).with_for_update().<span class="hljs-built_in">filter</span>(User.<span class="hljs-built_in">id</span> == <span class="hljs-number">1</span>).first()<br><span class="hljs-comment"># 其他事务无法修改或锁定此行</span><br><br><span class="hljs-comment"># for_update 的参数</span><br>user = session.query(User).with_for_update(<br>    nowait=<span class="hljs-literal">True</span>,    <span class="hljs-comment"># 无法获取锁时立即抛出异常</span><br>    skip_locked=<span class="hljs-literal">True</span>,  <span class="hljs-comment"># 跳过已锁定的行</span><br>    read=<span class="hljs-literal">False</span>,     <span class="hljs-comment"># False=排他锁，True=共享锁</span><br>).<span class="hljs-built_in">filter</span>(User.<span class="hljs-built_in">id</span> == <span class="hljs-number">1</span>).first()<br><br><span class="hljs-comment"># 共享锁（允许其他事务读，但不允许写）</span><br>user = session.query(User).with_for_update(read=<span class="hljs-literal">True</span>).<span class="hljs-built_in">filter</span>(User.<span class="hljs-built_in">id</span> == <span class="hljs-number">1</span>).first()<br><br><span class="hljs-comment"># 表级锁（MySQL 示例）</span><br>session.execute(text(<span class="hljs-string">&quot;LOCK TABLES users WRITE&quot;</span>))<br><span class="hljs-keyword">try</span>:<br>    <span class="hljs-comment"># 操作...</span><br>    <span class="hljs-keyword">pass</span><br><span class="hljs-keyword">finally</span>:<br>    session.execute(text(<span class="hljs-string">&quot;UNLOCK TABLES&quot;</span>))<br></code></pre></td></tr></table></figure><h3 id="乐观锁">乐观锁</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> sqlalchemy <span class="hljs-keyword">import</span> Column, Integer<br><span class="hljs-keyword">from</span> sqlalchemy.orm <span class="hljs-keyword">import</span> relationship, Session<br><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">Product</span>(<span class="hljs-title class_ inherited__">Base</span>):<br>    __tablename__ = <span class="hljs-string">&quot;products&quot;</span><br>    <br>    <span class="hljs-built_in">id</span> = Column(Integer, primary_key=<span class="hljs-literal">True</span>)<br>    name = Column(String(<span class="hljs-number">50</span>))<br>    stock = Column(Integer)<br>    version = Column(Integer, default=<span class="hljs-number">0</span>, nullable=<span class="hljs-literal">False</span>)  <span class="hljs-comment"># 版本号</span><br><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">update_stock_optimistic</span>(<span class="hljs-params">session, product_id, new_stock</span>):<br>    product = session.query(Product).<span class="hljs-built_in">filter</span>(Product.<span class="hljs-built_in">id</span> == product_id).first()<br>    <br>    <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> product:<br>        <span class="hljs-keyword">return</span> <span class="hljs-literal">False</span><br>    <br>    <span class="hljs-comment"># 检查版本号是否变化</span><br>    current_version = product.version<br>    <br>    <span class="hljs-comment"># 更新</span><br>    product.stock = new_stock<br>    product.version = current_version + <span class="hljs-number">1</span><br>    <br>    <span class="hljs-keyword">try</span>:<br>        session.commit()<br>        <span class="hljs-keyword">return</span> <span class="hljs-literal">True</span><br>    <span class="hljs-keyword">except</span>:<br>        session.rollback()<br>        <span class="hljs-keyword">return</span> <span class="hljs-literal">False</span>  <span class="hljs-comment"># 版本号冲突，更新失败</span><br></code></pre></td></tr></table></figure><hr><h2 id="11-异步支持-2-0">11. 异步支持 (2.0+)</h2><h3 id="异步基础">异步基础</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> sqlalchemy.ext.asyncio <span class="hljs-keyword">import</span> create_async_engine, AsyncSession<br><span class="hljs-keyword">from</span> sqlalchemy.orm <span class="hljs-keyword">import</span> sessionmaker, declarative_base<br><span class="hljs-keyword">from</span> sqlalchemy <span class="hljs-keyword">import</span> select<br><br><span class="hljs-comment"># 异步引擎</span><br>engine = create_async_engine(<br>    <span class="hljs-string">&quot;sqlite+aiosqlite:///example.db&quot;</span>,<br>    echo=<span class="hljs-literal">True</span><br>)<br><br><span class="hljs-comment"># 异步 Session</span><br>AsyncSessionLocal = sessionmaker(<br>    bind=engine,<br>    class_=AsyncSession,<br>    expire_on_commit=<span class="hljs-literal">False</span><br>)<br><br>Base = declarative_base()<br><br><span class="hljs-comment"># 异步 CRUD</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">main</span>():<br>    <span class="hljs-keyword">async</span> <span class="hljs-keyword">with</span> AsyncSessionLocal() <span class="hljs-keyword">as</span> session:<br>        <span class="hljs-comment"># 创建表</span><br>        <span class="hljs-keyword">async</span> <span class="hljs-keyword">with</span> engine.begin() <span class="hljs-keyword">as</span> conn:<br>            <span class="hljs-keyword">await</span> conn.run_sync(Base.metadata.create_all)<br>        <br>        <span class="hljs-comment"># 添加数据</span><br>        user = User(name=<span class="hljs-string">&quot;异步用户&quot;</span>, email=<span class="hljs-string">&quot;async@example.com&quot;</span>)<br>        session.add(user)<br>        <span class="hljs-keyword">await</span> session.commit()<br>        <br>        <span class="hljs-comment"># 查询</span><br>        result = <span class="hljs-keyword">await</span> session.execute(select(User))<br>        users = result.scalars().<span class="hljs-built_in">all</span>()<br>        <br>        <span class="hljs-comment"># 获取单个</span><br>        result = <span class="hljs-keyword">await</span> session.execute(select(User).where(User.<span class="hljs-built_in">id</span> == <span class="hljs-number">1</span>))<br>        user = result.scalars().first()<br></code></pre></td></tr></table></figure><h3 id="异步关系预加载">异步关系预加载</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> sqlalchemy.ext.asyncio <span class="hljs-keyword">import</span> AsyncSession<br><span class="hljs-keyword">from</span> sqlalchemy <span class="hljs-keyword">import</span> select<br><span class="hljs-keyword">from</span> sqlalchemy.orm <span class="hljs-keyword">import</span> selectinload<br><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">get_user_with_posts</span>(<span class="hljs-params">session: AsyncSession, user_id: <span class="hljs-built_in">int</span></span>):<br>    result = <span class="hljs-keyword">await</span> session.execute(<br>        select(User)<br>        .options(selectinload(User.posts))<br>        .where(User.<span class="hljs-built_in">id</span> == user_id)<br>    )<br>    <span class="hljs-keyword">return</span> result.scalars().first()<br><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">get_all_users_with_data</span>(<span class="hljs-params">session: AsyncSession</span>):<br>    result = <span class="hljs-keyword">await</span> session.execute(<br>        select(User)<br>        .options(<br>            selectinload(User.profile),<br>            selectinload(User.posts).selectinload(Post.comments)<br>        )<br>    )<br>    <span class="hljs-keyword">return</span> result.scalars().<span class="hljs-built_in">all</span>()<br></code></pre></td></tr></table></figure><h3 id="FastAPI-异步-SQLAlchemy">FastAPI + 异步 SQLAlchemy</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI, Depends<br><span class="hljs-keyword">from</span> sqlalchemy.ext.asyncio <span class="hljs-keyword">import</span> AsyncSession, create_async_engine<br><span class="hljs-keyword">from</span> sqlalchemy.orm <span class="hljs-keyword">import</span> sessionmaker, declarative_base<br><br>app = FastAPI()<br><br>engine = create_async_engine(<span class="hljs-string">&quot;sqlite+aiosqlite:///example.db&quot;</span>)<br>AsyncSessionLocal = sessionmaker(engine, class_=AsyncSession)<br>Base = declarative_base()<br><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">get_db</span>():<br>    <span class="hljs-keyword">async</span> <span class="hljs-keyword">with</span> AsyncSessionLocal() <span class="hljs-keyword">as</span> session:<br>        <span class="hljs-keyword">yield</span> session<br><br><span class="hljs-meta">@app.get(<span class="hljs-params"><span class="hljs-string">&quot;/users&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">get_users</span>(<span class="hljs-params">db: AsyncSession = Depends(<span class="hljs-params">get_db</span>)</span>):<br>    result = <span class="hljs-keyword">await</span> db.execute(select(User))<br>    users = result.scalars().<span class="hljs-built_in">all</span>()<br>    <span class="hljs-keyword">return</span> [&#123;<span class="hljs-string">&quot;id&quot;</span>: u.<span class="hljs-built_in">id</span>, <span class="hljs-string">&quot;name&quot;</span>: u.name&#125; <span class="hljs-keyword">for</span> u <span class="hljs-keyword">in</span> users]<br></code></pre></td></tr></table></figure><hr><h2 id="12-性能优化">12. 性能优化</h2><h3 id="批量操作">批量操作</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-comment"># 批量插入（高效）</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">bulk_insert_users</span>(<span class="hljs-params">users_data</span>):<br>    <span class="hljs-comment"># users_data = [&#123;&quot;name&quot;: ..., &quot;email&quot;: ...&#125;, ...]</span><br>    session.bulk_insert_mappings(User, users_data)<br>    session.commit()<br><br><span class="hljs-comment"># 批量更新</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">bulk_update_users</span>(<span class="hljs-params">updates</span>):<br>    <span class="hljs-comment"># updates = [&#123;&quot;id&quot;: 1, &quot;name&quot;: &quot;新名字&quot;&#125;, ...]</span><br>    session.bulk_update_mappings(User, updates)<br>    session.commit()<br><br><span class="hljs-comment"># 批量插入（使用 execute_values - PostgreSQL）</span><br><span class="hljs-keyword">from</span> sqlalchemy.dialects.postgresql <span class="hljs-keyword">import</span> insert<br><br>stmt = insert(User).values(users_data)<br>session.execute(stmt)<br>session.commit()<br></code></pre></td></tr></table></figure><h3 id="查询优化">查询优化</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-comment"># 1. 只查询需要的字段</span><br>results = session.query(User.<span class="hljs-built_in">id</span>, User.name).<span class="hljs-built_in">all</span>()<br><br><span class="hljs-comment"># 2. 使用 EXISTS 代替 IN（大数据集）</span><br><span class="hljs-keyword">from</span> sqlalchemy <span class="hljs-keyword">import</span> exists<br>users_with_posts = session.query(User).<span class="hljs-built_in">filter</span>(<br>    exists().where(Post.user_id == User.<span class="hljs-built_in">id</span>)<br>).<span class="hljs-built_in">all</span>()<br><br><span class="hljs-comment"># 3. 使用延迟加载（适合大对象）</span><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">Article</span>(<span class="hljs-title class_ inherited__">Base</span>):<br>    content = Column(Text, lazy=<span class="hljs-string">&quot;dynamic&quot;</span>)  <span class="hljs-comment"># 访问时才查询</span><br><br><span class="hljs-comment"># 4. 禁用自动刷新</span><br>session = Session()<br>session.autoflush = <span class="hljs-literal">False</span><br><br><span class="hljs-comment"># 5. 使用 yield_per 分批处理大数据集</span><br>query = session.query(User).yield_per(<span class="hljs-number">100</span>)<br><span class="hljs-keyword">for</span> user <span class="hljs-keyword">in</span> query:<br>    process(user)  <span class="hljs-comment"># 每次只加载 100 条到内存</span><br></code></pre></td></tr></table></figure><h3 id="连接池优化">连接池优化</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs python">engine = create_engine(<br>    <span class="hljs-string">&quot;mysql+pymysql://user:pass@localhost/db&quot;</span>,<br>    <br>    <span class="hljs-comment"># 连接池配置</span><br>    pool_size=<span class="hljs-number">20</span>,         <span class="hljs-comment"># 正常连接数</span><br>    max_overflow=<span class="hljs-number">40</span>,      <span class="hljs-comment"># 超出后可创建的最大连接</span><br>    pool_timeout=<span class="hljs-number">30</span>,      <span class="hljs-comment"># 等待连接的超时时间</span><br>    pool_recycle=<span class="hljs-number">1800</span>,    <span class="hljs-comment"># 连接回收时间（秒）</span><br>    pool_pre_ping=<span class="hljs-literal">True</span>,   <span class="hljs-comment"># 使用前检查连接</span><br>    <br>    <span class="hljs-comment"># 其他优化</span><br>    echo=<span class="hljs-literal">False</span>,           <span class="hljs-comment"># 生产环境关闭 SQL 日志</span><br>)<br></code></pre></td></tr></table></figure><hr><h2 id="13-迁移管理-Alembic">13. 迁移管理 (Alembic)</h2><h3 id="Alembic-基础">Alembic 基础</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-comment"># 安装</span><br>pip install alembic<br><br><span class="hljs-comment"># 初始化</span><br>alembic init alembic<br><br><span class="hljs-comment"># 配置 alembic.ini</span><br><span class="hljs-comment"># sqlalchemy.url = mysql+pymysql://user:pass@localhost/db</span><br></code></pre></td></tr></table></figure><h3 id="目录结构">目录结构</h3><figure class="highlight nix"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs nix">alembic<span class="hljs-symbol">/</span><br>├── versions<span class="hljs-symbol">/</span>          <span class="hljs-comment"># 迁移脚本</span><br>│   ├── <span class="hljs-number">001</span>_initial.py<br>│   └── <span class="hljs-number">002</span>_add_user_table.py<br>├── env.py            <span class="hljs-comment"># 环境配置</span><br>└── script.py.mako    <span class="hljs-comment"># 模板</span><br></code></pre></td></tr></table></figure><h3 id="常用命令">常用命令</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-comment"># 生成新迁移</span><br>alembic revision --autogenerate -m <span class="hljs-string">&quot;创建用户表&quot;</span><br><br><span class="hljs-comment"># 应用迁移</span><br>alembic upgrade <span class="hljs-built_in">head</span><br><br><span class="hljs-comment"># 回退一个版本</span><br>alembic downgrade -1<br><br><span class="hljs-comment"># 查看当前版本</span><br>alembic current<br><br><span class="hljs-comment"># 查看历史</span><br>alembic <span class="hljs-built_in">history</span><br><br><span class="hljs-comment"># 生成空迁移（手动写 SQL）</span><br>alembic revision -m <span class="hljs-string">&quot;添加索引&quot;</span><br></code></pre></td></tr></table></figure><h3 id="迁移脚本示例">迁移脚本示例</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-string">&quot;&quot;&quot;创建用户表</span><br><span class="hljs-string"></span><br><span class="hljs-string">Revision ID: abc123</span><br><span class="hljs-string">Revises: </span><br><span class="hljs-string">Create Date: 2024-01-01</span><br><span class="hljs-string"></span><br><span class="hljs-string">&quot;&quot;&quot;</span><br><span class="hljs-keyword">from</span> alembic <span class="hljs-keyword">import</span> op<br><span class="hljs-keyword">import</span> sqlalchemy <span class="hljs-keyword">as</span> sa<br><br>revision = <span class="hljs-string">&quot;abc123&quot;</span><br>down_revision = <span class="hljs-literal">None</span><br><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">upgrade</span>():<br>    op.create_table(<br>        <span class="hljs-string">&quot;users&quot;</span>,<br>        sa.Column(<span class="hljs-string">&quot;id&quot;</span>, sa.Integer(), nullable=<span class="hljs-literal">False</span>),<br>        sa.Column(<span class="hljs-string">&quot;username&quot;</span>, sa.String(length=<span class="hljs-number">50</span>), nullable=<span class="hljs-literal">False</span>),<br>        sa.Column(<span class="hljs-string">&quot;email&quot;</span>, sa.String(length=<span class="hljs-number">100</span>), nullable=<span class="hljs-literal">False</span>),<br>        sa.Column(<span class="hljs-string">&quot;created_at&quot;</span>, sa.DateTime(), nullable=<span class="hljs-literal">True</span>),<br>        sa.PrimaryKeyConstraint(<span class="hljs-string">&quot;id&quot;</span>),<br>        sa.UniqueConstraint(<span class="hljs-string">&quot;email&quot;</span>),<br>        sa.UniqueConstraint(<span class="hljs-string">&quot;username&quot;</span>),<br>    )<br>    op.create_index(<span class="hljs-string">&quot;ix_users_email&quot;</span>, <span class="hljs-string">&quot;users&quot;</span>, [<span class="hljs-string">&quot;email&quot;</span>], unique=<span class="hljs-literal">False</span>)<br><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">downgrade</span>():<br>    op.drop_index(<span class="hljs-string">&quot;ix_users_email&quot;</span>, table_name=<span class="hljs-string">&quot;users&quot;</span>)<br>    op.drop_table(<span class="hljs-string">&quot;users&quot;</span>)<br></code></pre></td></tr></table></figure><hr><h2 id="14-最佳实践">14. 最佳实践</h2><h3 id="项目结构">项目结构</h3><figure class="highlight nix"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs nix">project<span class="hljs-symbol">/</span><br>├── app<span class="hljs-symbol">/</span><br>│   ├── __init__.py<br>│   ├── database.py      <span class="hljs-comment"># 引擎、Session 配置</span><br>│   ├── models<span class="hljs-symbol">/</span>          <span class="hljs-comment"># 模型</span><br>│   │   ├── __init__.py<br>│   │   ├── user.py<br>│   │   └── post.py<br>│   └── schemas<span class="hljs-symbol">/</span>         <span class="hljs-comment"># Pydantic 模型</span><br>│       └── user.py<br>├── alembic<span class="hljs-symbol">/</span>             <span class="hljs-comment"># 迁移</span><br>├── tests<span class="hljs-symbol">/</span><br>└── requirements.txt<br></code></pre></td></tr></table></figure><h3 id="database-py-示例"><a href="http://database.py">database.py</a> 示例</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> sqlalchemy <span class="hljs-keyword">import</span> create_engine<br><span class="hljs-keyword">from</span> sqlalchemy.ext.declarative <span class="hljs-keyword">import</span> declarative_base<br><span class="hljs-keyword">from</span> sqlalchemy.orm <span class="hljs-keyword">import</span> sessionmaker<br><br>SQLALCHEMY_DATABASE_URL = <span class="hljs-string">&quot;sqlite:///./app.db&quot;</span><br><br>engine = create_engine(<br>    SQLALCHEMY_DATABASE_URL,<br>    pool_pre_ping=<span class="hljs-literal">True</span>,<br>    echo=<span class="hljs-literal">False</span><br>)<br><br>SessionLocal = sessionmaker(<br>    autocommit=<span class="hljs-literal">False</span>,<br>    autoflush=<span class="hljs-literal">False</span>,<br>    bind=engine<br>)<br><br>Base = declarative_base()<br><br><span class="hljs-comment"># 依赖注入用的函数</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">get_db</span>():<br>    db = SessionLocal()<br>    <span class="hljs-keyword">try</span>:<br>        <span class="hljs-keyword">yield</span> db<br>    <span class="hljs-keyword">finally</span>:<br>        db.close()<br></code></pre></td></tr></table></figure><h3 id="模型定义最佳实践">模型定义最佳实践</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> sqlalchemy <span class="hljs-keyword">import</span> Column, Integer, String, DateTime, func<br><span class="hljs-keyword">from</span> sqlalchemy.orm <span class="hljs-keyword">import</span> declarative_base, Mapped, mapped_column<br><span class="hljs-keyword">from</span> datetime <span class="hljs-keyword">import</span> datetime<br><span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> <span class="hljs-type">Optional</span><br><br>Base = declarative_base()<br><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">User</span>(<span class="hljs-title class_ inherited__">Base</span>):<br>    __tablename__ = <span class="hljs-string">&quot;users&quot;</span><br>    <br>    <span class="hljs-comment"># SQLAlchemy 2.0 风格</span><br>    <span class="hljs-built_in">id</span>: Mapped[<span class="hljs-built_in">int</span>] = mapped_column(primary_key=<span class="hljs-literal">True</span>)<br>    username: Mapped[<span class="hljs-built_in">str</span>] = mapped_column(String(<span class="hljs-number">50</span>), unique=<span class="hljs-literal">True</span>, nullable=<span class="hljs-literal">False</span>)<br>    email: Mapped[<span class="hljs-built_in">str</span>] = mapped_column(String(<span class="hljs-number">100</span>), unique=<span class="hljs-literal">True</span>, nullable=<span class="hljs-literal">False</span>)<br>    created_at: Mapped[datetime] = mapped_column(<br>        DateTime, default=datetime.utcnow, server_default=func.now()<br>    )<br>    <br>    <span class="hljs-keyword">def</span> <span class="hljs-title function_">to_dict</span>(<span class="hljs-params">self</span>) -&gt; <span class="hljs-built_in">dict</span>:<br>        <span class="hljs-keyword">return</span> &#123;<br>            <span class="hljs-string">&quot;id&quot;</span>: <span class="hljs-variable language_">self</span>.<span class="hljs-built_in">id</span>,<br>            <span class="hljs-string">&quot;username&quot;</span>: <span class="hljs-variable language_">self</span>.username,<br>            <span class="hljs-string">&quot;email&quot;</span>: <span class="hljs-variable language_">self</span>.email,<br>        &#125;<br>    <br>    <span class="hljs-keyword">def</span> <span class="hljs-title function_">__repr__</span>(<span class="hljs-params">self</span>) -&gt; <span class="hljs-built_in">str</span>:<br>        <span class="hljs-keyword">return</span> <span class="hljs-string">f&quot;&lt;User(id=<span class="hljs-subst">&#123;self.<span class="hljs-built_in">id</span>&#125;</span>, username=<span class="hljs-subst">&#123;self.username&#125;</span>)&gt;&quot;</span><br></code></pre></td></tr></table></figure><h3 id="常见陷阱与避免">常见陷阱与避免</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-comment"># ❌ 错误：忘记 commit</span><br>session.add(user)<br><span class="hljs-comment"># 应该：session.commit()</span><br><br><span class="hljs-comment"># ❌ 错误：在循环中查询</span><br><span class="hljs-keyword">for</span> user_id <span class="hljs-keyword">in</span> user_ids:<br>    user = session.query(User).get(user_id)  <span class="hljs-comment"># N+1 问题</span><br><br><span class="hljs-comment"># ✅ 正确：使用 IN</span><br>users = session.query(User).<span class="hljs-built_in">filter</span>(User.<span class="hljs-built_in">id</span>.in_(user_ids)).<span class="hljs-built_in">all</span>()<br><br><span class="hljs-comment"># ❌ 错误：会话泄漏</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">get_user</span>(<span class="hljs-params">user_id</span>):<br>    session = SessionLocal()<br>    <span class="hljs-keyword">return</span> session.query(User).get(user_id)<br>    <span class="hljs-comment"># 应该：用上下文管理器</span><br><br><span class="hljs-comment"># ✅ 正确：</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">get_user</span>(<span class="hljs-params">user_id</span>):<br>    <span class="hljs-keyword">with</span> SessionLocal() <span class="hljs-keyword">as</span> session:<br>        <span class="hljs-keyword">return</span> session.query(User).get(user_id)<br><br><span class="hljs-comment"># ❌ 错误：在事务中进行耗时操作</span><br><span class="hljs-keyword">with</span> session.begin():<br>    user = session.query(User).get(<span class="hljs-number">1</span>)<br>    send_email(user.email)  <span class="hljs-comment"># 耗时的邮件发送</span><br>    <span class="hljs-comment"># 事务一直保持打开</span><br><br><span class="hljs-comment"># ✅ 正确：先提交，再发送</span><br><span class="hljs-keyword">with</span> session.begin():<br>    user = session.query(User).get(<span class="hljs-number">1</span>)<br>send_email(user.email)<br></code></pre></td></tr></table></figure><hr><h2 id="附录：快速参考">附录：快速参考</h2><h3 id="常用类型速查">常用类型速查</h3><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs sql">String(n)       <span class="hljs-operator">-</span> <span class="hljs-type">VARCHAR</span>(n)<br>Text            <span class="hljs-operator">-</span> TEXT<br><span class="hljs-type">Integer</span>         <span class="hljs-operator">-</span> <span class="hljs-type">INTEGER</span><br><span class="hljs-type">Float</span>           <span class="hljs-operator">-</span> <span class="hljs-type">FLOAT</span><br><span class="hljs-type">Numeric</span>(p, s)   <span class="hljs-operator">-</span> <span class="hljs-type">DECIMAL</span>(p, s)<br><span class="hljs-type">Boolean</span>         <span class="hljs-operator">-</span> <span class="hljs-type">BOOLEAN</span><br><span class="hljs-type">Date</span>            <span class="hljs-operator">-</span> <span class="hljs-type">DATE</span><br>DateTime        <span class="hljs-operator">-</span> DATETIME<br><span class="hljs-type">Time</span>            <span class="hljs-operator">-</span> <span class="hljs-type">TIME</span><br>JSON            <span class="hljs-operator">-</span> JSON<br>LargeBinary     <span class="hljs-operator">-</span> <span class="hljs-type">BLOB</span><br>Enum(...)       <span class="hljs-operator">-</span> ENUM<br></code></pre></td></tr></table></figure><h3 id="约束速查">约束速查</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs python">primary_key=<span class="hljs-literal">True</span>          <span class="hljs-comment"># 主键</span><br>nullable=<span class="hljs-literal">False</span>            <span class="hljs-comment"># NOT NULL</span><br>unique=<span class="hljs-literal">True</span>               <span class="hljs-comment"># UNIQUE</span><br>index=<span class="hljs-literal">True</span>                <span class="hljs-comment"># INDEX</span><br>default=value             <span class="hljs-comment"># Python 默认值</span><br>server_default=text       <span class="hljs-comment"># SQL 默认值</span><br>ForeignKey(<span class="hljs-string">&quot;table.col&quot;</span>)   <span class="hljs-comment"># 外键</span><br></code></pre></td></tr></table></figure><h3 id="关系速查">关系速查</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-comment"># 一对一</span><br>child = relationship(<span class="hljs-string">&quot;Child&quot;</span>, uselist=<span class="hljs-literal">False</span>)<br><br><span class="hljs-comment"># 一对多</span><br>children = relationship(<span class="hljs-string">&quot;Child&quot;</span>)<br><br><span class="hljs-comment"># 多对多</span><br>roles = relationship(<span class="hljs-string">&quot;Role&quot;</span>, secondary=association_table)<br><br><span class="hljs-comment"># 预加载</span><br>joinedload(Model.relation)      <span class="hljs-comment"># JOIN</span><br>selectinload(Model.relation)    <span class="hljs-comment"># IN 查询</span><br></code></pre></td></tr></table></figure><hr><h2 id="学习资源">学习资源</h2><ul><li>官方文档：<a href="https://docs.sqlalchemy.org/">https://docs.sqlalchemy.org/</a></li><li>ORM 教程：<a href="https://docs.sqlalchemy.org/en/20/orm/quickstart.html">https://docs.sqlalchemy.org/en/20/orm/quickstart.html</a></li><li>Alembic 文档：<a href="https://alembic.sqlalchemy.org/">https://alembic.sqlalchemy.org/</a></li><li>SQLAlchemy 2.0 迁移指南：<a href="https://docs.sqlalchemy.org/en/20/changelog/migration_20.html">https://docs.sqlalchemy.org/en/20/changelog/migration_20.html</a></li></ul><hr>]]>
    </content>
    <id>https://www.furinaluna.top/posts/40cc3c1.html</id>
    <link href="https://www.furinaluna.top/posts/40cc3c1.html"/>
    <published>2026-04-04T15:27:39.000Z</published>
    <summary>
      <![CDATA[<h1>SQLAlchemy 精通教程</h1>
<blockquote>
<p><strong>之前写了一个sqlalchemy的简单实践，这次从入门到精通，自我总结，可能有缺，持续更新。。。</strong></p>
</blockquote>
<hr>
<h2 id="目录]]>
    </summary>
    <title>SQLAlchemy快速入门自我总结</title>
    <updated>2026-09-11T13:48:57.151Z</updated>
  </entry>
  <entry>
    <author>
      <name>Furina</name>
    </author>
    <category term="FastAPI" scheme="https://www.furinaluna.top/categories/FastAPI/"/>
    <category term="FastAPI" scheme="https://www.furinaluna.top/tags/FastAPI/"/>
    <content>
      <![CDATA[<h1>FastAPI 精通教程</h1><blockquote><p><strong>从入门到精通，自我总结，可能有缺，持续更新。。。</strong></p></blockquote><hr><h2 id="目录">目录</h2><ol><li><a href="#1-fastapi-%E7%AE%80%E4%BB%8B">FastAPI 简介</a></li><li><a href="#2-%E5%BF%AB%E9%80%9F%E5%BC%80%E5%A7%8B">快速开始</a></li><li><a href="#3-%E8%B7%AF%E5%BE%84%E5%8F%82%E6%95%B0">路径参数</a></li><li><a href="#4-%E6%9F%A5%E8%AF%A2%E5%8F%82%E6%95%B0">查询参数</a></li><li><a href="#5-%E8%AF%B7%E6%B1%82%E4%BD%93%E4%B8%8E-pydantic">请求体与 Pydantic</a></li><li><a href="#6-%E5%93%8D%E5%BA%94%E6%A8%A1%E5%9E%8B">响应模型</a></li><li><a href="#7-%E9%94%99%E8%AF%AF%E5%A4%84%E7%90%86">错误处理</a></li><li><a href="#8-%E4%BE%9D%E8%B5%96%E6%B3%A8%E5%85%A5%E7%B3%BB%E7%BB%9F">依赖注入系统</a></li><li><a href="#9-%E4%B8%AD%E9%97%B4%E4%BB%B6">中间件</a></li><li><a href="#10-websocket-%E6%94%AF%E6%8C%81">WebSocket 支持</a></li><li><a href="#11-%E5%90%8E%E5%8F%B0%E4%BB%BB%E5%8A%A1">后台任务</a></li><li><a href="#12-%E6%95%B0%E6%8D%AE%E5%BA%93%E9%9B%86%E6%88%90">数据库集成</a></li><li><a href="#13-%E8%AE%A4%E8%AF%81%E4%B8%8E%E6%8E%88%E6%9D%83">认证与授权</a></li><li><a href="#14-%E6%B5%8B%E8%AF%95">测试</a></li><li><a href="#15-%E9%A1%B9%E7%9B%AE%E7%BB%93%E6%9E%84%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5">项目结构最佳实践</a></li><li><a href="#16-%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96">性能优化</a></li></ol><hr><h2 id="1-FastAPI-简介">1. FastAPI 简介</h2><h3 id="什么是-FastAPI？">什么是 FastAPI？</h3><p><strong>FastAPI</strong> 是一个现代、高性能的 Python Web 框架，用于构建 API。它的核心特点：</p><table><thead><tr><th>特性</th><th>说明</th></tr></thead><tbody><tr><td>🚀 <strong>高性能</strong></td><td>基于 Starlette 和 Pydantic，性能接近 Node.js 和 Go</td></tr><tr><td>📝 <strong>自动文档</strong></td><td>自动生成 OpenAPI 和 ReDoc 文档</td></tr><tr><td>✅ <strong>数据验证</strong></td><td>基于 Python 类型提示的自动验证</td></tr><tr><td>🎯 <strong>开发速度快</strong></td><td>代码量减少约 40%，bug 减少</td></tr><tr><td>🔧 <strong>生产就绪</strong></td><td>支持异步、WebSocket、GraphQL 等</td></tr></tbody></table><h3 id="为什么选择-FastAPI？">为什么选择 FastAPI？</h3><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs scss">传统框架 (Flask/Django)     FastAPI<br>├── 手动验证输入            ├── 自动类型验证<br>├── 手动写 API 文档          ├── 自动生成文档<br>├── 同步处理为主            ├── 原生异步支持<br>└── 返回值无类型检查        └── 响应模型验证<br></code></pre></td></tr></table></figure><h3 id="核心概念一图流">核心概念一图流</h3><figure class="highlight coffeescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs coffeescript">请求 → 路由 (Router) → 依赖注入 → 端点函数 → 响应模型 → <span class="hljs-built_in">JSON</span><br>              ↓              ↓           ↓<br>         路径参数       数据库连接    数据验证<br>         查询参数       认证检查     序列化<br></code></pre></td></tr></table></figure><hr><h2 id="2-快速开始">2. 快速开始</h2><h3 id="安装">安装</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">pip install fastapi uvicorn[standard]<br></code></pre></td></tr></table></figure><h3 id="Hello-World">Hello World</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI<br><br>app = FastAPI(<br>    title=<span class="hljs-string">&quot;我的 API&quot;</span>,<br>    description=<span class="hljs-string">&quot;这是一个示例 API&quot;</span>,<br>    version=<span class="hljs-string">&quot;1.0.0&quot;</span><br>)<br><br><span class="hljs-meta">@app.get(<span class="hljs-params"><span class="hljs-string">&quot;/&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">root</span>():<br>    <span class="hljs-string">&quot;&quot;&quot;</span><br><span class="hljs-string">    根路径端点</span><br><span class="hljs-string">    </span><br><span class="hljs-string">    这是你的第一个 FastAPI 端点！</span><br><span class="hljs-string">    async def 表示这是一个异步函数</span><br><span class="hljs-string">    &quot;&quot;&quot;</span><br>    <span class="hljs-keyword">return</span> &#123;<span class="hljs-string">&quot;message&quot;</span>: <span class="hljs-string">&quot;Hello World&quot;</span>&#125;<br><br><span class="hljs-meta">@app.get(<span class="hljs-params"><span class="hljs-string">&quot;/items/&#123;item_id&#125;&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">read_item</span>(<span class="hljs-params">item_id: <span class="hljs-built_in">int</span></span>):<br>    <span class="hljs-string">&quot;&quot;&quot;</span><br><span class="hljs-string">    带路径参数的端点</span><br><span class="hljs-string">    </span><br><span class="hljs-string">    item_id: 会自动转换为整数，如果传入了非整数会返回验证错误</span><br><span class="hljs-string">    &quot;&quot;&quot;</span><br>    <span class="hljs-keyword">return</span> &#123;<span class="hljs-string">&quot;item_id&quot;</span>: item_id&#125;<br></code></pre></td></tr></table></figure><h3 id="运行服务器">运行服务器</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-comment"># 开发模式（自动重载）</span><br>uvicorn main:app --reload<br><br><span class="hljs-comment"># 生产模式</span><br>uvicorn main:app --host 0.0.0.0 --port 8000 --workers 4<br><br><span class="hljs-comment"># 指定端口</span><br>uvicorn main:app --port 8080<br></code></pre></td></tr></table></figure><h3 id="访问自动文档">访问自动文档</h3><ul><li>Swagger UI: <code>http://localhost:8000/docs</code></li><li>ReDoc: <code>http://localhost:8000/redoc</code></li><li>OpenAPI JSON: <code>http://localhost:8000/openapi.json</code></li></ul><hr><h2 id="3-路径参数">3. 路径参数</h2><p>路径参数是 URL 路径的一部分，用于标识特定资源。</p><h3 id="基础用法">基础用法</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI<br><br>app = FastAPI()<br><br><span class="hljs-meta">@app.get(<span class="hljs-params"><span class="hljs-string">&quot;/users/&#123;user_id&#125;&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">get_user</span>(<span class="hljs-params">user_id: <span class="hljs-built_in">int</span></span>):<br>    <span class="hljs-string">&quot;&quot;&quot;</span><br><span class="hljs-string">    获取用户信息</span><br><span class="hljs-string">    </span><br><span class="hljs-string">    user_id 会从 URL 中提取并验证为整数</span><br><span class="hljs-string">    例如：/users/123 → user_id = 123</span><br><span class="hljs-string">    &quot;&quot;&quot;</span><br>    <span class="hljs-keyword">return</span> &#123;<span class="hljs-string">&quot;user_id&quot;</span>: user_id&#125;<br></code></pre></td></tr></table></figure><h3 id="类型转换详解">类型转换详解</h3><p>FastAPI 会自动将路径参数转换为指定的类型：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> datetime <span class="hljs-keyword">import</span> datetime<br><span class="hljs-keyword">from</span> uuid <span class="hljs-keyword">import</span> UUID<br><br><span class="hljs-meta">@app.get(<span class="hljs-params"><span class="hljs-string">&quot;/items/&#123;item_id&#125;&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">read_item</span>(<span class="hljs-params">item_id: <span class="hljs-built_in">int</span></span>):<br>    <span class="hljs-comment"># item_id 被转换为 int</span><br>    <span class="hljs-comment"># /items/42 ✓ → &#123;&quot;item_id&quot;: 42&#125;</span><br>    <span class="hljs-comment"># /items/abc ✗ → 422 验证错误</span><br>    <span class="hljs-keyword">return</span> &#123;<span class="hljs-string">&quot;item_id&quot;</span>: item_id&#125;<br><br><span class="hljs-meta">@app.get(<span class="hljs-params"><span class="hljs-string">&quot;/users/&#123;user_id&#125;&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">read_user</span>(<span class="hljs-params">user_id: UUID</span>):<br>    <span class="hljs-comment"># UUID 类型验证</span><br>    <span class="hljs-comment"># /users/123e4567-e89b-12d3-a456-426614174000 ✓</span><br>    <span class="hljs-keyword">return</span> &#123;<span class="hljs-string">&quot;user_id&quot;</span>: user_id&#125;<br><br><span class="hljs-meta">@app.get(<span class="hljs-params"><span class="hljs-string">&quot;/logs/&#123;date&#125;&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">read_logs</span>(<span class="hljs-params">date: datetime</span>):<br>    <span class="hljs-comment"># 日期时间验证</span><br>    <span class="hljs-comment"># /logs/2024-01-15T10:30:00 ✓</span><br>    <span class="hljs-keyword">return</span> &#123;<span class="hljs-string">&quot;date&quot;</span>: date&#125;<br></code></pre></td></tr></table></figure><h3 id="预定义值（Enum）">预定义值（Enum）</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> enum <span class="hljs-keyword">import</span> Enum<br><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI<br><br>app = FastAPI()<br><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">ModelName</span>(<span class="hljs-built_in">str</span>, Enum):<br>    <span class="hljs-string">&quot;&quot;&quot;定义允许的值&quot;&quot;&quot;</span><br>    alexnet = <span class="hljs-string">&quot;alexnet&quot;</span><br>    resnet = <span class="hljs-string">&quot;resnet&quot;</span><br>    squeezenet = <span class="hljs-string">&quot;squeezenet&quot;</span><br><br><span class="hljs-meta">@app.get(<span class="hljs-params"><span class="hljs-string">&quot;/models/&#123;model_name&#125;&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">get_model</span>(<span class="hljs-params">model_name: ModelName</span>):<br>    <span class="hljs-string">&quot;&quot;&quot;</span><br><span class="hljs-string">    model_name 只能是 ModelName 中定义的值</span><br><span class="hljs-string">    </span><br><span class="hljs-string">    /models/alexnet ✓</span><br><span class="hljs-string">    /models/vgg ✗ → 422 错误</span><br><span class="hljs-string">    &quot;&quot;&quot;</span><br>    <span class="hljs-keyword">return</span> &#123;<span class="hljs-string">&quot;model_name&quot;</span>: model_name, <span class="hljs-string">&quot;message&quot;</span>: <span class="hljs-string">f&quot;Using <span class="hljs-subst">&#123;model_name.value&#125;</span>&quot;</span>&#125;<br></code></pre></td></tr></table></figure><h3 id="文件路径参数">文件路径参数</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI<br><br>app = FastAPI()<br><br><span class="hljs-comment"># 捕获剩余路径</span><br><span class="hljs-meta">@app.get(<span class="hljs-params"><span class="hljs-string">&quot;/files/&#123;file_path:path&#125;&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">read_file</span>(<span class="hljs-params">file_path: <span class="hljs-built_in">str</span></span>):<br>    <span class="hljs-string">&quot;&quot;&quot;</span><br><span class="hljs-string">    file_path:path 会捕获剩余的所有路径</span><br><span class="hljs-string">    </span><br><span class="hljs-string">    /files/users/john/doc.txt → file_path = &quot;users/john/doc.txt&quot;</span><br><span class="hljs-string">    &quot;&quot;&quot;</span><br>    <span class="hljs-keyword">return</span> &#123;<span class="hljs-string">&quot;file_path&quot;</span>: file_path&#125;<br></code></pre></td></tr></table></figure><hr><h2 id="4-查询参数">4. 查询参数</h2><p>查询参数是 URL <code>?</code> 后面的键值对。</p><h3 id="基础用法-2">基础用法</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI<br><span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> <span class="hljs-type">Optional</span><br><br>app = FastAPI()<br><br><span class="hljs-meta">@app.get(<span class="hljs-params"><span class="hljs-string">&quot;/items&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">read_items</span>(<span class="hljs-params"></span><br><span class="hljs-params">    skip: <span class="hljs-built_in">int</span> = <span class="hljs-number">0</span>,      <span class="hljs-comment"># 默认值为 0</span></span><br><span class="hljs-params">    limit: <span class="hljs-built_in">int</span> = <span class="hljs-number">10</span>,    <span class="hljs-comment"># 默认值为 10</span></span><br><span class="hljs-params">    q: <span class="hljs-type">Optional</span>[<span class="hljs-built_in">str</span>] = <span class="hljs-literal">None</span>  <span class="hljs-comment"># 可选参数</span></span><br><span class="hljs-params"></span>):<br>    <span class="hljs-string">&quot;&quot;&quot;</span><br><span class="hljs-string">    查询参数示例</span><br><span class="hljs-string">    </span><br><span class="hljs-string">    /items?skip=0&amp;limit=10&amp;q=search</span><br><span class="hljs-string">    /items → skip=0, limit=10, q=None</span><br><span class="hljs-string">    &quot;&quot;&quot;</span><br>    <span class="hljs-keyword">return</span> &#123;<span class="hljs-string">&quot;skip&quot;</span>: skip, <span class="hljs-string">&quot;limit&quot;</span>: limit, <span class="hljs-string">&quot;q&quot;</span>: q&#125;<br></code></pre></td></tr></table></figure><h3 id="必需参数-vs-可选参数">必需参数 vs 可选参数</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI<br><br>app = FastAPI()<br><br><span class="hljs-comment"># 必需参数（没有默认值）</span><br><span class="hljs-meta">@app.get(<span class="hljs-params"><span class="hljs-string">&quot;/users&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">get_users</span>(<span class="hljs-params">region: <span class="hljs-built_in">str</span></span>):<br>    <span class="hljs-string">&quot;&quot;&quot;</span><br><span class="hljs-string">    /users?region=beijing ✓</span><br><span class="hljs-string">    /users ✗ → 缺少必需参数</span><br><span class="hljs-string">    &quot;&quot;&quot;</span><br>    <span class="hljs-keyword">return</span> &#123;<span class="hljs-string">&quot;region&quot;</span>: region&#125;<br><br><span class="hljs-comment"># 可选参数（有默认值）</span><br><span class="hljs-meta">@app.get(<span class="hljs-params"><span class="hljs-string">&quot;/products&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">get_products</span>(<span class="hljs-params"></span><br><span class="hljs-params">    category: <span class="hljs-built_in">str</span> = <span class="hljs-string">&quot;all&quot;</span>,  <span class="hljs-comment"># 有默认值=可选</span></span><br><span class="hljs-params">    page: <span class="hljs-built_in">int</span> = <span class="hljs-number">1</span>,</span><br><span class="hljs-params">    sort: <span class="hljs-built_in">str</span> = <span class="hljs-string">&quot;default&quot;</span></span><br><span class="hljs-params"></span>):<br>    <span class="hljs-string">&quot;&quot;&quot;</span><br><span class="hljs-string">    /products ✓ → category=&quot;all&quot;, page=1, sort=&quot;default&quot;</span><br><span class="hljs-string">    /products?category=electronics&amp;page=2 ✓</span><br><span class="hljs-string">    &quot;&quot;&quot;</span><br>    <span class="hljs-keyword">return</span> &#123;<span class="hljs-string">&quot;category&quot;</span>: category, <span class="hljs-string">&quot;page&quot;</span>: page, <span class="hljs-string">&quot;sort&quot;</span>: sort&#125;<br></code></pre></td></tr></table></figure><h3 id="类型验证和约束">类型验证和约束</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI, Query<br><span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> <span class="hljs-type">Optional</span>, <span class="hljs-type">List</span><br><br>app = FastAPI()<br><br><span class="hljs-meta">@app.get(<span class="hljs-params"><span class="hljs-string">&quot;/items&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">read_items</span>(<span class="hljs-params"></span><br><span class="hljs-params">    <span class="hljs-comment"># 使用 Query 添加验证规则</span></span><br><span class="hljs-params">    q: <span class="hljs-type">Optional</span>[<span class="hljs-built_in">str</span>] = Query(<span class="hljs-params"></span></span><br><span class="hljs-params"><span class="hljs-params">        <span class="hljs-literal">None</span>,</span></span><br><span class="hljs-params"><span class="hljs-params">        min_length=<span class="hljs-number">1</span>,      <span class="hljs-comment"># 最小长度</span></span></span><br><span class="hljs-params"><span class="hljs-params">        max_length=<span class="hljs-number">50</span>,     <span class="hljs-comment"># 最大长度</span></span></span><br><span class="hljs-params"><span class="hljs-params">        pattern=<span class="hljs-string">r&quot;^[a-z]+$&quot;</span>  <span class="hljs-comment"># 正则表达式</span></span></span><br><span class="hljs-params"><span class="hljs-params">    </span>),</span><br><span class="hljs-params">    <span class="hljs-comment"># 数值范围</span></span><br><span class="hljs-params">    price_min: <span class="hljs-built_in">float</span> = Query(<span class="hljs-params"><span class="hljs-number">0</span>, ge=<span class="hljs-number">0</span></span>),  <span class="hljs-comment"># ge: greater or equal</span></span><br><span class="hljs-params">    price_max: <span class="hljs-built_in">float</span> = Query(<span class="hljs-params"><span class="hljs-number">1000</span>, le=<span class="hljs-number">10000</span></span>),  <span class="hljs-comment"># le: less or equal</span></span><br><span class="hljs-params">    <span class="hljs-comment"># 列表参数</span></span><br><span class="hljs-params">    tags: <span class="hljs-type">List</span>[<span class="hljs-built_in">str</span>] = Query(<span class="hljs-params">default=[]</span>),  <span class="hljs-comment"># /items?tags=tag1&amp;tags=tag2</span></span><br><span class="hljs-params">    <span class="hljs-comment"># 描述（显示在文档中）</span></span><br><span class="hljs-params">    sort_by: <span class="hljs-built_in">str</span> = Query(<span class="hljs-params"><span class="hljs-string">&quot;created_at&quot;</span>, description=<span class="hljs-string">&quot;排序字段&quot;</span></span>)</span><br><span class="hljs-params"></span>):<br>    <span class="hljs-string">&quot;&quot;&quot;带验证的查询参数&quot;&quot;&quot;</span><br>    <span class="hljs-keyword">return</span> &#123;<br>        <span class="hljs-string">&quot;q&quot;</span>: q,<br>        <span class="hljs-string">&quot;price_range&quot;</span>: [price_min, price_max],<br>        <span class="hljs-string">&quot;tags&quot;</span>: tags,<br>        <span class="hljs-string">&quot;sort_by&quot;</span>: sort_by<br>    &#125;<br></code></pre></td></tr></table></figure><h3 id="别名和废弃参数">别名和废弃参数</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI, Query<br><br>app = FastAPI()<br><br><span class="hljs-meta">@app.get(<span class="hljs-params"><span class="hljs-string">&quot;/items&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">read_items</span>(<span class="hljs-params"></span><br><span class="hljs-params">    <span class="hljs-comment"># 使用别名（URL 中用 page-num，函数中用 page_num）</span></span><br><span class="hljs-params">    page_num: <span class="hljs-built_in">int</span> = Query(<span class="hljs-params"><span class="hljs-number">1</span>, alias=<span class="hljs-string">&quot;page-num&quot;</span></span>),</span><br><span class="hljs-params">    <span class="hljs-comment"># 标记为废弃</span></span><br><span class="hljs-params">    old_param: <span class="hljs-built_in">str</span> = Query(<span class="hljs-params"></span></span><br><span class="hljs-params"><span class="hljs-params">        <span class="hljs-literal">None</span>,</span></span><br><span class="hljs-params"><span class="hljs-params">        deprecated=<span class="hljs-literal">True</span>,</span></span><br><span class="hljs-params"><span class="hljs-params">        description=<span class="hljs-string">&quot;此参数已废弃，请使用 new_param&quot;</span></span></span><br><span class="hljs-params"><span class="hljs-params">    </span>),</span><br><span class="hljs-params">    new_param: <span class="hljs-built_in">str</span> = Query(<span class="hljs-params"><span class="hljs-literal">None</span></span>)</span><br><span class="hljs-params"></span>):<br>    <span class="hljs-keyword">return</span> &#123;<span class="hljs-string">&quot;page&quot;</span>: page_num, <span class="hljs-string">&quot;old&quot;</span>: old_param, <span class="hljs-string">&quot;new&quot;</span>: new_param&#125;<br></code></pre></td></tr></table></figure><hr><h2 id="5-请求体与-Pydantic">5. 请求体与 Pydantic</h2><p>请求体用于发送复杂数据（通常是 JSON）。</p><h3 id="Pydantic-模型基础">Pydantic 模型基础</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI<br><span class="hljs-keyword">from</span> pydantic <span class="hljs-keyword">import</span> BaseModel, Field, EmailStr<br><span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> <span class="hljs-type">Optional</span><br><span class="hljs-keyword">from</span> datetime <span class="hljs-keyword">import</span> datetime<br><br>app = FastAPI()<br><br><span class="hljs-comment"># 定义数据模型</span><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">Item</span>(<span class="hljs-title class_ inherited__">BaseModel</span>):<br>    <span class="hljs-string">&quot;&quot;&quot;</span><br><span class="hljs-string">    Pydantic 模型 = 数据验证 + 序列化</span><br><span class="hljs-string">    </span><br><span class="hljs-string">    作用：</span><br><span class="hljs-string">    1. 验证传入的数据类型</span><br><span class="hljs-string">    2. 自动转换为正确的类型</span><br><span class="hljs-string">    3. 生成 OpenAPI 文档</span><br><span class="hljs-string">    &quot;&quot;&quot;</span><br>    name: <span class="hljs-built_in">str</span> = Field(..., min_length=<span class="hljs-number">1</span>, max_length=<span class="hljs-number">50</span>)<br>    price: <span class="hljs-built_in">float</span> = Field(..., gt=<span class="hljs-number">0</span>, description=<span class="hljs-string">&quot;价格必须大于 0&quot;</span>)<br>    description: <span class="hljs-type">Optional</span>[<span class="hljs-built_in">str</span>] = <span class="hljs-literal">None</span><br>    quantity: <span class="hljs-built_in">int</span> = Field(<span class="hljs-number">1</span>, ge=<span class="hljs-number">1</span>)  <span class="hljs-comment"># 默认值 1，必须 &gt;= 1</span><br>    tags: <span class="hljs-built_in">list</span>[<span class="hljs-built_in">str</span>] = []<br><br><span class="hljs-meta">@app.post(<span class="hljs-params"><span class="hljs-string">&quot;/items&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">create_item</span>(<span class="hljs-params">item: Item</span>):<br>    <span class="hljs-string">&quot;&quot;&quot;</span><br><span class="hljs-string">    FastAPI 会：</span><br><span class="hljs-string">    1. 读取 JSON 请求体</span><br><span class="hljs-string">    2. 验证数据类型</span><br><span class="hljs-string">    3. 转换为 Item 对象</span><br><span class="hljs-string">    4. 验证失败时返回 422</span><br><span class="hljs-string">    &quot;&quot;&quot;</span><br>    <span class="hljs-comment"># item 已经是验证过的 Item 对象</span><br>    <span class="hljs-keyword">return</span> &#123;<br>        <span class="hljs-string">&quot;name&quot;</span>: item.name,<br>        <span class="hljs-string">&quot;price&quot;</span>: item.price,<br>        <span class="hljs-string">&quot;total&quot;</span>: item.price * item.quantity<br>    &#125;<br></code></pre></td></tr></table></figure><h3 id="嵌套模型">嵌套模型</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> pydantic <span class="hljs-keyword">import</span> BaseModel<br><span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> <span class="hljs-type">Optional</span><br><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">Address</span>(<span class="hljs-title class_ inherited__">BaseModel</span>):<br>    street: <span class="hljs-built_in">str</span><br>    city: <span class="hljs-built_in">str</span><br>    country: <span class="hljs-built_in">str</span><br>    zipcode: <span class="hljs-built_in">str</span><br><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">User</span>(<span class="hljs-title class_ inherited__">BaseModel</span>):<br>    name: <span class="hljs-built_in">str</span><br>    email: <span class="hljs-built_in">str</span><br>    age: <span class="hljs-built_in">int</span><br>    address: <span class="hljs-type">Optional</span>[Address] = <span class="hljs-literal">None</span>  <span class="hljs-comment"># 嵌套模型</span><br><br><span class="hljs-meta">@app.post(<span class="hljs-params"><span class="hljs-string">&quot;/users&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">create_user</span>(<span class="hljs-params">user: User</span>):<br>    <span class="hljs-string">&quot;&quot;&quot;</span><br><span class="hljs-string">    发送 JSON:</span><br><span class="hljs-string">    &#123;</span><br><span class="hljs-string">        &quot;name&quot;: &quot;John&quot;,</span><br><span class="hljs-string">        &quot;email&quot;: &quot;john@example.com&quot;,</span><br><span class="hljs-string">        &quot;age&quot;: 30,</span><br><span class="hljs-string">        &quot;address&quot;: &#123;</span><br><span class="hljs-string">            &quot;street&quot;: &quot;123 Main St&quot;,</span><br><span class="hljs-string">            &quot;city&quot;: &quot;NYC&quot;,</span><br><span class="hljs-string">            &quot;country&quot;: &quot;USA&quot;,</span><br><span class="hljs-string">            &quot;zipcode&quot;: &quot;10001&quot;</span><br><span class="hljs-string">        &#125;</span><br><span class="hljs-string">    &#125;</span><br><span class="hljs-string">    &quot;&quot;&quot;</span><br>    <span class="hljs-keyword">return</span> user<br></code></pre></td></tr></table></figure><h3 id="验证器（Validators）">验证器（Validators）</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> pydantic <span class="hljs-keyword">import</span> BaseModel, field_validator, model_validator<br><span class="hljs-keyword">from</span> datetime <span class="hljs-keyword">import</span> datetime<br><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">Product</span>(<span class="hljs-title class_ inherited__">BaseModel</span>):<br>    name: <span class="hljs-built_in">str</span><br>    price: <span class="hljs-built_in">float</span><br>    discount: <span class="hljs-built_in">float</span> = <span class="hljs-number">0</span><br>    final_price: <span class="hljs-built_in">float</span> = <span class="hljs-literal">None</span><br><br>    <span class="hljs-comment"># 字段验证器</span><br><span class="hljs-meta">    @field_validator(<span class="hljs-params"><span class="hljs-string">&#x27;name&#x27;</span></span>)</span><br><span class="hljs-meta">    @classmethod</span><br>    <span class="hljs-keyword">def</span> <span class="hljs-title function_">name_must_not_be_empty</span>(<span class="hljs-params">cls, v</span>):<br>        <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> v.strip():<br>            <span class="hljs-keyword">raise</span> ValueError(<span class="hljs-string">&#x27;name 不能为空&#x27;</span>)<br>        <span class="hljs-keyword">return</span> v.strip()<br><br><span class="hljs-meta">    @field_validator(<span class="hljs-params"><span class="hljs-string">&#x27;price&#x27;</span></span>)</span><br><span class="hljs-meta">    @classmethod</span><br>    <span class="hljs-keyword">def</span> <span class="hljs-title function_">price_must_be_positive</span>(<span class="hljs-params">cls, v</span>):<br>        <span class="hljs-keyword">if</span> v &lt;= <span class="hljs-number">0</span>:<br>            <span class="hljs-keyword">raise</span> ValueError(<span class="hljs-string">&#x27;价格必须大于 0&#x27;</span>)<br>        <span class="hljs-keyword">return</span> v<br><br>    <span class="hljs-comment"># 模型验证器（跨字段验证）</span><br><span class="hljs-meta">    @model_validator(<span class="hljs-params">mode=<span class="hljs-string">&#x27;after&#x27;</span></span>)</span><br>    <span class="hljs-keyword">def</span> <span class="hljs-title function_">calculate_final_price</span>(<span class="hljs-params">self</span>):<br>        <span class="hljs-keyword">if</span> <span class="hljs-variable language_">self</span>.final_price <span class="hljs-keyword">is</span> <span class="hljs-literal">None</span>:<br>            <span class="hljs-variable language_">self</span>.final_price = <span class="hljs-variable language_">self</span>.price * (<span class="hljs-number">1</span> - <span class="hljs-variable language_">self</span>.discount / <span class="hljs-number">100</span>)<br>        <span class="hljs-keyword">return</span> <span class="hljs-variable language_">self</span><br><br><span class="hljs-meta">@app.post(<span class="hljs-params"><span class="hljs-string">&quot;/products&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">create_product</span>(<span class="hljs-params">product: Product</span>):<br>    <span class="hljs-keyword">return</span> product<br></code></pre></td></tr></table></figure><h3 id="请求体-路径-查询参数">请求体 + 路径 + 查询参数</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI, Path, Query<br><span class="hljs-keyword">from</span> pydantic <span class="hljs-keyword">import</span> BaseModel<br><br>app = FastAPI()<br><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">ItemUpdate</span>(<span class="hljs-title class_ inherited__">BaseModel</span>):<br>    name: <span class="hljs-built_in">str</span> | <span class="hljs-literal">None</span> = <span class="hljs-literal">None</span><br>    price: <span class="hljs-built_in">float</span> | <span class="hljs-literal">None</span> = <span class="hljs-literal">None</span><br><br><span class="hljs-meta">@app.put(<span class="hljs-params"><span class="hljs-string">&quot;/items/&#123;item_id&#125;&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">update_item</span>(<span class="hljs-params"></span><br><span class="hljs-params">    item_id: <span class="hljs-built_in">int</span> = Path(<span class="hljs-params">..., description=<span class="hljs-string">&quot;要更新的物品 ID&quot;</span></span>),</span><br><span class="hljs-params">    q: <span class="hljs-built_in">str</span> | <span class="hljs-literal">None</span> = Query(<span class="hljs-params"><span class="hljs-literal">None</span>, description=<span class="hljs-string">&quot;查询参数&quot;</span></span>),</span><br><span class="hljs-params">    item: ItemUpdate = <span class="hljs-literal">None</span>  <span class="hljs-comment"># 请求体</span></span><br><span class="hljs-params"></span>):<br>    <span class="hljs-string">&quot;&quot;&quot;</span><br><span class="hljs-string">    PUT /items/123?q=test</span><br><span class="hljs-string">    Body: &#123;&quot;name&quot;: &quot;新名字&quot;, &quot;price&quot;: 99.9&#125;</span><br><span class="hljs-string">    </span><br><span class="hljs-string">    参数顺序：Path → Query → Body</span><br><span class="hljs-string">    &quot;&quot;&quot;</span><br>    <span class="hljs-keyword">return</span> &#123;<span class="hljs-string">&quot;item_id&quot;</span>: item_id, <span class="hljs-string">&quot;q&quot;</span>: q, <span class="hljs-string">&quot;update&quot;</span>: item&#125;<br></code></pre></td></tr></table></figure><hr><h2 id="6-响应模型">6. 响应模型</h2><p>响应模型用于控制返回给客户端的数据。</p><h3 id="基础响应模型">基础响应模型</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI<br><span class="hljs-keyword">from</span> pydantic <span class="hljs-keyword">import</span> BaseModel, EmailStr<br><span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> <span class="hljs-type">Optional</span><br><br>app = FastAPI()<br><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">UserIn</span>(<span class="hljs-title class_ inherited__">BaseModel</span>):<br>    <span class="hljs-string">&quot;&quot;&quot;输入模型&quot;&quot;&quot;</span><br>    username: <span class="hljs-built_in">str</span><br>    password: <span class="hljs-built_in">str</span><br>    email: EmailStr<br><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">UserOut</span>(<span class="hljs-title class_ inherited__">BaseModel</span>):<br>    <span class="hljs-string">&quot;&quot;&quot;输出模型 - 不包含密码&quot;&quot;&quot;</span><br>    username: <span class="hljs-built_in">str</span><br>    email: EmailStr<br>    <br>    <span class="hljs-keyword">class</span> <span class="hljs-title class_">Config</span>:<br>        from_attributes = <span class="hljs-literal">True</span>  <span class="hljs-comment"># 允许从 ORM 对象读取</span><br><br><span class="hljs-meta">@app.post(<span class="hljs-params"><span class="hljs-string">&quot;/users&quot;</span>, response_model=UserOut</span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">create_user</span>(<span class="hljs-params">user: UserIn</span>):<br>    <span class="hljs-string">&quot;&quot;&quot;</span><br><span class="hljs-string">    即使返回完整的 user 对象</span><br><span class="hljs-string">    响应模型会过滤掉 password 字段</span><br><span class="hljs-string">    &quot;&quot;&quot;</span><br>    <span class="hljs-keyword">return</span> user  <span class="hljs-comment"># 实际返回只有 username 和 email</span><br><br><span class="hljs-comment"># 或者使用装饰器方式</span><br><span class="hljs-meta">@app.post(<span class="hljs-params"><span class="hljs-string">&quot;/users2&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">create_user2</span>(<span class="hljs-params">user: UserIn</span>):<br>    <span class="hljs-keyword">return</span> UserOut(**user.model_dump())<br></code></pre></td></tr></table></figure><h3 id="响应模型参数详解">响应模型参数详解</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI<br><span class="hljs-keyword">from</span> pydantic <span class="hljs-keyword">import</span> BaseModel, SecretStr<br><span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> <span class="hljs-type">Optional</span>, <span class="hljs-type">List</span><br><br>app = FastAPI()<br><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">Item</span>(<span class="hljs-title class_ inherited__">BaseModel</span>):<br>    name: <span class="hljs-built_in">str</span><br>    description: <span class="hljs-type">Optional</span>[<span class="hljs-built_in">str</span>] = <span class="hljs-literal">None</span><br>    price: <span class="hljs-built_in">float</span><br>    tax: <span class="hljs-built_in">float</span> = <span class="hljs-number">10.0</span><br>    tags: <span class="hljs-type">List</span>[<span class="hljs-built_in">str</span>] = []<br><br><span class="hljs-comment"># exclude: 排除特定字段</span><br><span class="hljs-comment"># exclude_none: 排除值为 None 的字段</span><br><span class="hljs-comment"># exclude_unset: 只返回设置的字段</span><br><br><span class="hljs-meta">@app.post(<span class="hljs-params"><span class="hljs-string">&quot;/items&quot;</span>, response_model=Item, response_model_exclude_none=<span class="hljs-literal">True</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">create_item</span>(<span class="hljs-params">item: Item</span>):<br>    <span class="hljs-string">&quot;&quot;&quot;排除 None 值的字段&quot;&quot;&quot;</span><br>    <span class="hljs-keyword">return</span> item<br><br><span class="hljs-meta">@app.get(<span class="hljs-params"><span class="hljs-string">&quot;/items/&#123;item_id&#125;&quot;</span>, response_model=Item, response_model_exclude=&#123;<span class="hljs-string">&quot;tax&quot;</span>&#125;</span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">get_item</span>(<span class="hljs-params">item_id: <span class="hljs-built_in">int</span></span>):<br>    <span class="hljs-string">&quot;&quot;&quot;排除 tax 字段&quot;&quot;&quot;</span><br>    <span class="hljs-keyword">return</span> Item(name=<span class="hljs-string">f&quot;Item <span class="hljs-subst">&#123;item_id&#125;</span>&quot;</span>, price=<span class="hljs-number">10.0</span>)<br></code></pre></td></tr></table></figure><h3 id="返回状态码和自定义响应">返回状态码和自定义响应</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI, status, HTTPException<br><br>app = FastAPI()<br><br><span class="hljs-meta">@app.post(<span class="hljs-params"><span class="hljs-string">&quot;/items&quot;</span>, status_code=status.HTTP_201_CREATED</span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">create_item</span>(<span class="hljs-params">name: <span class="hljs-built_in">str</span></span>):<br>    <span class="hljs-string">&quot;&quot;&quot;返回 201 Created 状态码&quot;&quot;&quot;</span><br>    <span class="hljs-keyword">return</span> &#123;<span class="hljs-string">&quot;name&quot;</span>: name&#125;<br><br><span class="hljs-meta">@app.delete(<span class="hljs-params"><span class="hljs-string">&quot;/items/&#123;item_id&#125;&quot;</span>, status_code=<span class="hljs-number">204</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">delete_item</span>(<span class="hljs-params">item_id: <span class="hljs-built_in">int</span></span>):<br>    <span class="hljs-string">&quot;&quot;&quot;返回 204 No Content&quot;&quot;&quot;</span><br>    <span class="hljs-keyword">return</span> <span class="hljs-literal">None</span><br><br><span class="hljs-meta">@app.post(<span class="hljs-params"><span class="hljs-string">&quot;/login&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">login</span>(<span class="hljs-params">username: <span class="hljs-built_in">str</span>, password: <span class="hljs-built_in">str</span></span>):<br>    <span class="hljs-keyword">if</span> username != <span class="hljs-string">&quot;admin&quot;</span> <span class="hljs-keyword">or</span> password != <span class="hljs-string">&quot;secret&quot;</span>:<br>        <span class="hljs-keyword">raise</span> HTTPException(<br>            status_code=<span class="hljs-number">401</span>,<br>            detail=<span class="hljs-string">&quot;用户名或密码错误&quot;</span>,<br>            headers=&#123;<span class="hljs-string">&quot;WWW-Authenticate&quot;</span>: <span class="hljs-string">&quot;Bearer&quot;</span>&#125;<br>        )<br>    <span class="hljs-keyword">return</span> &#123;<span class="hljs-string">&quot;token&quot;</span>: <span class="hljs-string">&quot;fake-token&quot;</span>&#125;<br></code></pre></td></tr></table></figure><hr><h2 id="7-错误处理">7. 错误处理</h2><h3 id="HTTPException">HTTPException</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI, HTTPException, status<br><br>app = FastAPI()<br><br>items = &#123;<span class="hljs-string">&quot;foo&quot;</span>: <span class="hljs-string">&quot;Item Foo&quot;</span>, <span class="hljs-string">&quot;bar&quot;</span>: <span class="hljs-string">&quot;Item Bar&quot;</span>&#125;<br><br><span class="hljs-meta">@app.get(<span class="hljs-params"><span class="hljs-string">&quot;/items/&#123;item_id&#125;&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">read_item</span>(<span class="hljs-params">item_id: <span class="hljs-built_in">str</span></span>):<br>    <span class="hljs-keyword">if</span> item_id <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> items:<br>        <span class="hljs-keyword">raise</span> HTTPException(<br>            status_code=status.HTTP_404_NOT_FOUND,<br>            detail=<span class="hljs-string">f&quot;Item <span class="hljs-subst">&#123;item_id&#125;</span> not found&quot;</span>,<br>            headers=&#123;<span class="hljs-string">&quot;X-Error&quot;</span>: <span class="hljs-string">&quot;NotFound&quot;</span>&#125;  <span class="hljs-comment"># 自定义头</span><br>        )<br>    <span class="hljs-keyword">return</span> &#123;<span class="hljs-string">&quot;item&quot;</span>: items[item_id]&#125;<br></code></pre></td></tr></table></figure><h3 id="自定义异常处理器">自定义异常处理器</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI, Request, status<br><span class="hljs-keyword">from</span> fastapi.responses <span class="hljs-keyword">import</span> JSONResponse<br><span class="hljs-keyword">from</span> fastapi.exceptions <span class="hljs-keyword">import</span> RequestValidationError<br><br>app = FastAPI()<br><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">CustomException</span>(<span class="hljs-title class_ inherited__">Exception</span>):<br>    <span class="hljs-keyword">def</span> <span class="hljs-title function_">__init__</span>(<span class="hljs-params">self, message: <span class="hljs-built_in">str</span>, code: <span class="hljs-built_in">str</span></span>):<br>        <span class="hljs-variable language_">self</span>.message = message<br>        <span class="hljs-variable language_">self</span>.code = code<br><br><span class="hljs-comment"># 注册异常处理器</span><br><span class="hljs-meta">@app.exception_handler(<span class="hljs-params">CustomException</span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">custom_exception_handler</span>(<span class="hljs-params">request: Request, exc: CustomException</span>):<br>    <span class="hljs-keyword">return</span> JSONResponse(<br>        status_code=<span class="hljs-number">400</span>,<br>        content=&#123;<br>            <span class="hljs-string">&quot;error&quot;</span>: <span class="hljs-string">&quot;custom_error&quot;</span>,<br>            <span class="hljs-string">&quot;message&quot;</span>: exc.message,<br>            <span class="hljs-string">&quot;code&quot;</span>: exc.code<br>        &#125;<br>    )<br><br><span class="hljs-comment"># 重写默认验证错误处理</span><br><span class="hljs-meta">@app.exception_handler(<span class="hljs-params">RequestValidationError</span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">validation_exception_handler</span>(<span class="hljs-params">request: Request, exc: RequestValidationError</span>):<br>    <span class="hljs-keyword">return</span> JSONResponse(<br>        status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,<br>        content=&#123;<br>            <span class="hljs-string">&quot;error&quot;</span>: <span class="hljs-string">&quot;validation_error&quot;</span>,<br>            <span class="hljs-string">&quot;details&quot;</span>: exc.errors()<br>        &#125;<br>    )<br><br><span class="hljs-meta">@app.get(<span class="hljs-params"><span class="hljs-string">&quot;/trigger-error&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">trigger_error</span>():<br>    <span class="hljs-keyword">raise</span> CustomException(<span class="hljs-string">&quot;这是一个自定义错误&quot;</span>, <span class="hljs-string">&quot;CUSTOM_001&quot;</span>)<br></code></pre></td></tr></table></figure><h3 id="业务异常封装">业务异常封装</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI, HTTPException, status<br><span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> <span class="hljs-type">Generic</span>, TypeVar, <span class="hljs-type">Optional</span><br><span class="hljs-keyword">from</span> pydantic <span class="hljs-keyword">import</span> BaseModel<br><br>app = FastAPI()<br><br>T = TypeVar(<span class="hljs-string">&#x27;T&#x27;</span>)<br><br><span class="hljs-comment"># 统一响应格式</span><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">APIResponse</span>(BaseModel, <span class="hljs-type">Generic</span>[T]):<br>    success: <span class="hljs-built_in">bool</span><br>    data: <span class="hljs-type">Optional</span>[T] = <span class="hljs-literal">None</span><br>    error: <span class="hljs-type">Optional</span>[<span class="hljs-built_in">str</span>] = <span class="hljs-literal">None</span><br>    message: <span class="hljs-built_in">str</span> = <span class="hljs-string">&quot;操作成功&quot;</span><br><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">BusinessException</span>(<span class="hljs-title class_ inherited__">HTTPException</span>):<br>    <span class="hljs-string">&quot;&quot;&quot;业务异常基类&quot;&quot;&quot;</span><br>    <span class="hljs-keyword">def</span> <span class="hljs-title function_">__init__</span>(<span class="hljs-params">self, message: <span class="hljs-built_in">str</span>, status_code: <span class="hljs-built_in">int</span> = <span class="hljs-number">400</span>, code: <span class="hljs-built_in">str</span> = <span class="hljs-string">&quot;BIZ_ERROR&quot;</span></span>):<br>        <span class="hljs-built_in">super</span>().__init__(<br>            status_code=status_code,<br>            detail=&#123;<span class="hljs-string">&quot;message&quot;</span>: message, <span class="hljs-string">&quot;code&quot;</span>: code&#125;<br>        )<br><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">NotFoundError</span>(<span class="hljs-title class_ inherited__">BusinessException</span>):<br>    <span class="hljs-keyword">def</span> <span class="hljs-title function_">__init__</span>(<span class="hljs-params">self, resource: <span class="hljs-built_in">str</span></span>):<br>        <span class="hljs-built_in">super</span>().__init__(<br>            message=<span class="hljs-string">f&quot;<span class="hljs-subst">&#123;resource&#125;</span> 未找到&quot;</span>,<br>            status_code=<span class="hljs-number">404</span>,<br>            code=<span class="hljs-string">&quot;NOT_FOUND&quot;</span><br>        )<br><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">ValidationError</span>(<span class="hljs-title class_ inherited__">BusinessException</span>):<br>    <span class="hljs-keyword">def</span> <span class="hljs-title function_">__init__</span>(<span class="hljs-params">self, field: <span class="hljs-built_in">str</span>, message: <span class="hljs-built_in">str</span></span>):<br>        <span class="hljs-built_in">super</span>().__init__(<br>            message=<span class="hljs-string">f&quot;<span class="hljs-subst">&#123;field&#125;</span>: <span class="hljs-subst">&#123;message&#125;</span>&quot;</span>,<br>            status_code=<span class="hljs-number">422</span>,<br>            code=<span class="hljs-string">&quot;VALIDATION_ERROR&quot;</span><br>        )<br><br><span class="hljs-meta">@app.get(<span class="hljs-params"><span class="hljs-string">&quot;/users/&#123;user_id&#125;&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">get_user</span>(<span class="hljs-params">user_id: <span class="hljs-built_in">int</span></span>):<br>    <span class="hljs-keyword">if</span> user_id &lt;= <span class="hljs-number">0</span>:<br>        <span class="hljs-keyword">raise</span> ValidationError(<span class="hljs-string">&quot;user_id&quot;</span>, <span class="hljs-string">&quot;必须是正整数&quot;</span>)<br>    <span class="hljs-comment"># 模拟数据库查询</span><br>    <span class="hljs-keyword">if</span> user_id == <span class="hljs-number">999</span>:<br>        <span class="hljs-keyword">raise</span> NotFoundError(<span class="hljs-string">&quot;用户&quot;</span>)<br>    <span class="hljs-keyword">return</span> &#123;<span class="hljs-string">&quot;user_id&quot;</span>: user_id&#125;<br></code></pre></td></tr></table></figure><hr><h2 id="8-依赖注入系统">8. 依赖注入系统</h2><p>依赖注入是 FastAPI 最强大的特性之一。</p><h3 id="基础依赖">基础依赖</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI, Depends, HTTPException<br><span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> <span class="hljs-type">Optional</span><br><br>app = FastAPI()<br><br><span class="hljs-comment"># 可重用的依赖</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">common_parameters</span>(<span class="hljs-params">q: <span class="hljs-type">Optional</span>[<span class="hljs-built_in">str</span>] = <span class="hljs-literal">None</span>, skip: <span class="hljs-built_in">int</span> = <span class="hljs-number">0</span>, limit: <span class="hljs-built_in">int</span> = <span class="hljs-number">100</span></span>):<br>    <span class="hljs-string">&quot;&quot;&quot;返回通用参数&quot;&quot;&quot;</span><br>    <span class="hljs-keyword">return</span> &#123;<span class="hljs-string">&quot;q&quot;</span>: q, <span class="hljs-string">&quot;skip&quot;</span>: skip, <span class="hljs-string">&quot;limit&quot;</span>: limit&#125;<br><br><span class="hljs-meta">@app.get(<span class="hljs-params"><span class="hljs-string">&quot;/items&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">read_items</span>(<span class="hljs-params">commons: <span class="hljs-built_in">dict</span> = Depends(<span class="hljs-params">common_parameters</span>)</span>):<br>    <span class="hljs-string">&quot;&quot;&quot;使用依赖注入&quot;&quot;&quot;</span><br>    <span class="hljs-keyword">return</span> commons<br><br><span class="hljs-meta">@app.get(<span class="hljs-params"><span class="hljs-string">&quot;/users&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">read_users</span>(<span class="hljs-params">commons: <span class="hljs-built_in">dict</span> = Depends(<span class="hljs-params">common_parameters</span>)</span>):<br>    <span class="hljs-string">&quot;&quot;&quot;相同的依赖可以重复使用&quot;&quot;&quot;</span><br>    <span class="hljs-keyword">return</span> &#123;**commons, <span class="hljs-string">&quot;type&quot;</span>: <span class="hljs-string">&quot;users&quot;</span>&#125;<br></code></pre></td></tr></table></figure><h3 id="类依赖">类依赖</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI, Depends<br><br>app = FastAPI()<br><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">Pagination</span>:<br>    <span class="hljs-string">&quot;&quot;&quot;分页依赖&quot;&quot;&quot;</span><br>    <span class="hljs-keyword">def</span> <span class="hljs-title function_">__init__</span>(<span class="hljs-params">self, page: <span class="hljs-built_in">int</span> = <span class="hljs-number">1</span>, page_size: <span class="hljs-built_in">int</span> = <span class="hljs-number">10</span></span>):<br>        <span class="hljs-variable language_">self</span>.page = page<br>        <span class="hljs-variable language_">self</span>.page_size = page_size<br>    <br><span class="hljs-meta">    @property</span><br>    <span class="hljs-keyword">def</span> <span class="hljs-title function_">offset</span>(<span class="hljs-params">self</span>) -&gt; <span class="hljs-built_in">int</span>:<br>        <span class="hljs-keyword">return</span> (<span class="hljs-variable language_">self</span>.page - <span class="hljs-number">1</span>) * <span class="hljs-variable language_">self</span>.page_size<br><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">Sorting</span>:<br>    <span class="hljs-string">&quot;&quot;&quot;排序依赖&quot;&quot;&quot;</span><br>    <span class="hljs-keyword">def</span> <span class="hljs-title function_">__init__</span>(<span class="hljs-params">self, sort_by: <span class="hljs-built_in">str</span> = <span class="hljs-string">&quot;created_at&quot;</span>, order: <span class="hljs-built_in">str</span> = <span class="hljs-string">&quot;desc&quot;</span></span>):<br>        <span class="hljs-variable language_">self</span>.sort_by = sort_by<br>        <span class="hljs-variable language_">self</span>.order = order<br><br><span class="hljs-meta">@app.get(<span class="hljs-params"><span class="hljs-string">&quot;/items&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">read_items</span>(<span class="hljs-params"></span><br><span class="hljs-params">    pagination: Pagination = Depends(<span class="hljs-params"></span>),</span><br><span class="hljs-params">    sorting: Sorting = Depends(<span class="hljs-params"></span>)</span><br><span class="hljs-params"></span>):<br>    <span class="hljs-string">&quot;&quot;&quot;使用类依赖&quot;&quot;&quot;</span><br>    <span class="hljs-keyword">return</span> &#123;<br>        <span class="hljs-string">&quot;page&quot;</span>: pagination.page,<br>        <span class="hljs-string">&quot;page_size&quot;</span>: pagination.page_size,<br>        <span class="hljs-string">&quot;offset&quot;</span>: pagination.offset,<br>        <span class="hljs-string">&quot;sort_by&quot;</span>: sorting.sort_by,<br>        <span class="hljs-string">&quot;order&quot;</span>: sorting.order<br>    &#125;<br></code></pre></td></tr></table></figure><h3 id="数据库会话依赖">数据库会话依赖</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI, Depends<br><span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> Generator<br><br>app = FastAPI()<br><br><span class="hljs-comment"># 模拟数据库连接</span><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">DatabaseSession</span>:<br>    <span class="hljs-keyword">def</span> <span class="hljs-title function_">__init__</span>(<span class="hljs-params">self, url: <span class="hljs-built_in">str</span> = <span class="hljs-string">&quot;sqlite:///:memory:&quot;</span></span>):<br>        <span class="hljs-variable language_">self</span>.url = url<br>        <span class="hljs-variable language_">self</span>.connected = <span class="hljs-literal">False</span><br>    <br>    <span class="hljs-keyword">def</span> <span class="hljs-title function_">connect</span>(<span class="hljs-params">self</span>):<br>        <span class="hljs-variable language_">self</span>.connected = <span class="hljs-literal">True</span><br>        <span class="hljs-built_in">print</span>(<span class="hljs-string">f&quot;连接到数据库：<span class="hljs-subst">&#123;self.url&#125;</span>&quot;</span>)<br>    <br>    <span class="hljs-keyword">def</span> <span class="hljs-title function_">close</span>(<span class="hljs-params">self</span>):<br>        <span class="hljs-variable language_">self</span>.connected = <span class="hljs-literal">False</span><br>        <span class="hljs-built_in">print</span>(<span class="hljs-string">&quot;关闭数据库连接&quot;</span>)<br><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">get_db</span>() -&gt; Generator[DatabaseSession, <span class="hljs-literal">None</span>, <span class="hljs-literal">None</span>]:<br>    <span class="hljs-string">&quot;&quot;&quot;</span><br><span class="hljs-string">    依赖生成器 - 管理数据库会话生命周期</span><br><span class="hljs-string">    </span><br><span class="hljs-string">    yield 之前的代码：请求前执行（连接数据库）</span><br><span class="hljs-string">    yield: 提供会话对象给端点使用</span><br><span class="hljs-string">    yield 之后的代码：请求后执行（关闭连接）</span><br><span class="hljs-string">    &quot;&quot;&quot;</span><br>    db = DatabaseSession()<br>    <span class="hljs-keyword">try</span>:<br>        db.connect()<br>        <span class="hljs-keyword">yield</span> db<br>    <span class="hljs-keyword">finally</span>:<br>        db.close()<br><br><span class="hljs-meta">@app.get(<span class="hljs-params"><span class="hljs-string">&quot;/users&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">read_users</span>(<span class="hljs-params">db: DatabaseSession = Depends(<span class="hljs-params">get_db</span>)</span>):<br>    <span class="hljs-string">&quot;&quot;&quot;自动获取和清理数据库连接&quot;&quot;&quot;</span><br>    <span class="hljs-keyword">assert</span> db.connected, <span class="hljs-string">&quot;数据库应该已连接&quot;</span><br>    <span class="hljs-keyword">return</span> &#123;<span class="hljs-string">&quot;users&quot;</span>: [], <span class="hljs-string">&quot;db_connected&quot;</span>: db.connected&#125;<br></code></pre></td></tr></table></figure><h3 id="依赖嵌套和覆盖">依赖嵌套和覆盖</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI, Depends, HTTPException, status<br><br>app = FastAPI()<br><br><span class="hljs-comment"># 用户认证依赖</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">get_current_user</span>(<span class="hljs-params">token: <span class="hljs-built_in">str</span> = <span class="hljs-literal">None</span></span>):<br>    <span class="hljs-keyword">if</span> token <span class="hljs-keyword">is</span> <span class="hljs-literal">None</span>:<br>        <span class="hljs-keyword">raise</span> HTTPException(status_code=<span class="hljs-number">401</span>, detail=<span class="hljs-string">&quot;缺少 Token&quot;</span>)<br>    <span class="hljs-keyword">if</span> token != <span class="hljs-string">&quot;valid-token&quot;</span>:<br>        <span class="hljs-keyword">raise</span> HTTPException(status_code=<span class="hljs-number">401</span>, detail=<span class="hljs-string">&quot;无效 Token&quot;</span>)<br>    <span class="hljs-keyword">return</span> &#123;<span class="hljs-string">&quot;username&quot;</span>: <span class="hljs-string">&quot;testuser&quot;</span>, <span class="hljs-string">&quot;role&quot;</span>: <span class="hljs-string">&quot;user&quot;</span>&#125;<br><br><span class="hljs-comment"># 依赖上面的依赖</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">get_current_active_user</span>(<span class="hljs-params"></span><br><span class="hljs-params">    current_user: <span class="hljs-built_in">dict</span> = Depends(<span class="hljs-params">get_current_user</span>)</span><br><span class="hljs-params"></span>):<br>    <span class="hljs-keyword">if</span> current_user.get(<span class="hljs-string">&quot;disabled&quot;</span>):<br>        <span class="hljs-keyword">raise</span> HTTPException(status_code=<span class="hljs-number">400</span>, detail=<span class="hljs-string">&quot;用户已禁用&quot;</span>)<br>    <span class="hljs-keyword">return</span> current_user<br><br><span class="hljs-comment"># 进一步依赖</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">get_current_admin_user</span>(<span class="hljs-params"></span><br><span class="hljs-params">    current_user: <span class="hljs-built_in">dict</span> = Depends(<span class="hljs-params">get_current_active_user</span>)</span><br><span class="hljs-params"></span>):<br>    <span class="hljs-keyword">if</span> current_user.get(<span class="hljs-string">&quot;role&quot;</span>) != <span class="hljs-string">&quot;admin&quot;</span>:<br>        <span class="hljs-keyword">raise</span> HTTPException(status_code=<span class="hljs-number">403</span>, detail=<span class="hljs-string">&quot;需要管理员权限&quot;</span>)<br>    <span class="hljs-keyword">return</span> current_user<br><br><span class="hljs-meta">@app.get(<span class="hljs-params"><span class="hljs-string">&quot;/me&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">read_me</span>(<span class="hljs-params">current_user: <span class="hljs-built_in">dict</span> = Depends(<span class="hljs-params">get_current_active_user</span>)</span>):<br>    <span class="hljs-string">&quot;&quot;&quot;需要活跃用户&quot;&quot;&quot;</span><br>    <span class="hljs-keyword">return</span> current_user<br><br><span class="hljs-meta">@app.get(<span class="hljs-params"><span class="hljs-string">&quot;/admin/dashboard&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">admin_dashboard</span>(<span class="hljs-params">current_user: <span class="hljs-built_in">dict</span> = Depends(<span class="hljs-params">get_current_admin_user</span>)</span>):<br>    <span class="hljs-string">&quot;&quot;&quot;需要管理员&quot;&quot;&quot;</span><br>    <span class="hljs-keyword">return</span> &#123;<span class="hljs-string">&quot;message&quot;</span>: <span class="hljs-string">f&quot;欢迎管理员 <span class="hljs-subst">&#123;current_user[<span class="hljs-string">&#x27;username&#x27;</span>]&#125;</span>&quot;</span>&#125;<br></code></pre></td></tr></table></figure><h3 id="依赖覆盖（测试用）">依赖覆盖（测试用）</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI, Depends<br><br>app = FastAPI()<br><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">get_dependency</span>():<br>    <span class="hljs-keyword">return</span> <span class="hljs-string">&quot;production_value&quot;</span><br><br><span class="hljs-meta">@app.get(<span class="hljs-params"><span class="hljs-string">&quot;/&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">read_root</span>(<span class="hljs-params">value: <span class="hljs-built_in">str</span> = Depends(<span class="hljs-params">get_dependency</span>)</span>):<br>    <span class="hljs-keyword">return</span> &#123;<span class="hljs-string">&quot;value&quot;</span>: value&#125;<br><br><span class="hljs-comment"># 测试时覆盖依赖</span><br><span class="hljs-comment"># from .main import app, get_dependency</span><br><span class="hljs-comment"># async def override_dependency():</span><br><span class="hljs-comment">#     return &quot;test_value&quot;</span><br><span class="hljs-comment"># app.dependency_overrides[get_dependency] = override_dependency</span><br></code></pre></td></tr></table></figure><hr><h2 id="9-中间件">9. 中间件</h2><p>中间件在每个请求到达端点之前和响应返回之后执行。</p><h3 id="基础中间件">基础中间件</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI, Request<br><span class="hljs-keyword">from</span> starlette.middleware.base <span class="hljs-keyword">import</span> BaseHTTPMiddleware<br><span class="hljs-keyword">import</span> time<br><br>app = FastAPI()<br><br><span class="hljs-comment"># 添加内置中间件</span><br>app.add_middleware(<br>    CORSMiddleware,  <span class="hljs-comment"># 跨域支持</span><br>    allow_origins=[<span class="hljs-string">&quot;*&quot;</span>],  <span class="hljs-comment"># 允许的来源</span><br>    allow_credentials=<span class="hljs-literal">True</span>,  <span class="hljs-comment"># 允许 cookies</span><br>    allow_methods=[<span class="hljs-string">&quot;*&quot;</span>],  <span class="hljs-comment"># 允许的方法</span><br>    allow_headers=[<span class="hljs-string">&quot;*&quot;</span>],  <span class="hljs-comment"># 允许的头</span><br>)<br><br><span class="hljs-comment"># 自定义中间件</span><br><span class="hljs-meta">@app.middleware(<span class="hljs-params"><span class="hljs-string">&quot;http&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">log_requests</span>(<span class="hljs-params">request: Request, call_next</span>):<br>    <span class="hljs-string">&quot;&quot;&quot;记录请求日志的中间件&quot;&quot;&quot;</span><br>    start_time = time.time()<br>    <br>    <span class="hljs-comment"># 请求前处理</span><br>    <span class="hljs-built_in">print</span>(<span class="hljs-string">f&quot;收到请求：<span class="hljs-subst">&#123;request.method&#125;</span> <span class="hljs-subst">&#123;request.url.path&#125;</span>&quot;</span>)<br>    <br>    <span class="hljs-comment"># 处理请求</span><br>    response = <span class="hljs-keyword">await</span> call_next(request)<br>    <br>    <span class="hljs-comment"># 响应后处理</span><br>    process_time = time.time() - start_time<br>    <span class="hljs-built_in">print</span>(<span class="hljs-string">f&quot;处理时间：<span class="hljs-subst">&#123;process_time:<span class="hljs-number">.3</span>f&#125;</span>s&quot;</span>)<br>    <br>    <span class="hljs-comment"># 添加自定义响应头</span><br>    response.headers[<span class="hljs-string">&quot;X-Process-Time&quot;</span>] = <span class="hljs-built_in">str</span>(process_time)<br>    <br>    <span class="hljs-keyword">return</span> response<br></code></pre></td></tr></table></figure><h3 id="认证中间件">认证中间件</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI, Request, HTTPException<br><span class="hljs-keyword">from</span> starlette.middleware.base <span class="hljs-keyword">import</span> BaseHTTPMiddleware<br><span class="hljs-keyword">from</span> starlette.responses <span class="hljs-keyword">import</span> JSONResponse<br><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">AuthenticationMiddleware</span>(<span class="hljs-title class_ inherited__">BaseHTTPMiddleware</span>):<br>    <span class="hljs-string">&quot;&quot;&quot;JWT 认证中间件&quot;&quot;&quot;</span><br>    <br>    <span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">dispatch</span>(<span class="hljs-params">self, request: Request, call_next</span>):<br>        <span class="hljs-comment"># 跳过不需要认证的路径</span><br>        <span class="hljs-keyword">if</span> request.url.path <span class="hljs-keyword">in</span> [<span class="hljs-string">&quot;/docs&quot;</span>, <span class="hljs-string">&quot;/openapi.json&quot;</span>, <span class="hljs-string">&quot;/login&quot;</span>]:<br>            <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> call_next(request)<br>        <br>        <span class="hljs-comment"># 检查 Authorization 头</span><br>        auth_header = request.headers.get(<span class="hljs-string">&quot;Authorization&quot;</span>)<br>        <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> auth_header <span class="hljs-keyword">or</span> <span class="hljs-keyword">not</span> auth_header.startswith(<span class="hljs-string">&quot;Bearer &quot;</span>):<br>            <span class="hljs-keyword">return</span> JSONResponse(<br>                status_code=<span class="hljs-number">401</span>,<br>                content=&#123;<span class="hljs-string">&quot;detail&quot;</span>: <span class="hljs-string">&quot;缺少认证令牌&quot;</span>&#125;<br>            )<br>        <br>        token = auth_header.split(<span class="hljs-string">&quot; &quot;</span>)[<span class="hljs-number">1</span>]<br>        <br>        <span class="hljs-comment"># 验证 token（这里简化处理）</span><br>        <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> <span class="hljs-variable language_">self</span>.verify_token(token):<br>            <span class="hljs-keyword">return</span> JSONResponse(<br>                status_code=<span class="hljs-number">401</span>,<br>                content=&#123;<span class="hljs-string">&quot;detail&quot;</span>: <span class="hljs-string">&quot;无效的认证令牌&quot;</span>&#125;<br>            )<br>        <br>        <span class="hljs-comment"># 将用户信息添加到请求</span><br>        request.state.user = <span class="hljs-variable language_">self</span>.decode_token(token)<br>        <br>        <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> call_next(request)<br>    <br>    <span class="hljs-keyword">def</span> <span class="hljs-title function_">verify_token</span>(<span class="hljs-params">self, token: <span class="hljs-built_in">str</span></span>) -&gt; <span class="hljs-built_in">bool</span>:<br>        <span class="hljs-comment"># 实际的 token 验证逻辑</span><br>        <span class="hljs-keyword">return</span> token == <span class="hljs-string">&quot;valid-token&quot;</span><br>    <br>    <span class="hljs-keyword">def</span> <span class="hljs-title function_">decode_token</span>(<span class="hljs-params">self, token: <span class="hljs-built_in">str</span></span>) -&gt; <span class="hljs-built_in">dict</span>:<br>        <span class="hljs-comment"># 实际的 token 解码逻辑</span><br>        <span class="hljs-keyword">return</span> &#123;<span class="hljs-string">&quot;sub&quot;</span>: <span class="hljs-string">&quot;user123&quot;</span>, <span class="hljs-string">&quot;exp&quot;</span>: <span class="hljs-number">9999999999</span>&#125;<br><br>app.add_middleware(AuthenticationMiddleware)<br></code></pre></td></tr></table></figure><h3 id="速率限制中间件">速率限制中间件</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI, Request<br><span class="hljs-keyword">from</span> starlette.middleware.base <span class="hljs-keyword">import</span> BaseHTTPMiddleware<br><span class="hljs-keyword">from</span> starlette.responses <span class="hljs-keyword">import</span> JSONResponse<br><span class="hljs-keyword">from</span> collections <span class="hljs-keyword">import</span> defaultdict<br><span class="hljs-keyword">import</span> time<br><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">RateLimitMiddleware</span>(<span class="hljs-title class_ inherited__">BaseHTTPMiddleware</span>):<br>    <span class="hljs-string">&quot;&quot;&quot;简单的速率限制&quot;&quot;&quot;</span><br>    <br>    <span class="hljs-keyword">def</span> <span class="hljs-title function_">__init__</span>(<span class="hljs-params">self, app, requests_per_minute: <span class="hljs-built_in">int</span> = <span class="hljs-number">60</span></span>):<br>        <span class="hljs-built_in">super</span>().__init__(app)<br>        <span class="hljs-variable language_">self</span>.requests_per_minute = requests_per_minute<br>        <span class="hljs-variable language_">self</span>.request_counts = defaultdict(<span class="hljs-built_in">list</span>)<br>    <br>    <span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">dispatch</span>(<span class="hljs-params">self, request: Request, call_next</span>):<br>        client_ip = request.client.host<br>        current_time = time.time()<br>        window_start = current_time - <span class="hljs-number">60</span><br>        <br>        <span class="hljs-comment"># 清理旧请求</span><br>        <span class="hljs-variable language_">self</span>.request_counts[client_ip] = [<br>            t <span class="hljs-keyword">for</span> t <span class="hljs-keyword">in</span> <span class="hljs-variable language_">self</span>.request_counts[client_ip] <span class="hljs-keyword">if</span> t &gt; window_start<br>        ]<br>        <br>        <span class="hljs-comment"># 检查是否超出限制</span><br>        <span class="hljs-keyword">if</span> <span class="hljs-built_in">len</span>(<span class="hljs-variable language_">self</span>.request_counts[client_ip]) &gt;= <span class="hljs-variable language_">self</span>.requests_per_minute:<br>            <span class="hljs-keyword">return</span> JSONResponse(<br>                status_code=<span class="hljs-number">429</span>,<br>                content=&#123;<span class="hljs-string">&quot;detail&quot;</span>: <span class="hljs-string">&quot;请求过于频繁，请稍后再试&quot;</span>&#125;<br>            )<br>        <br>        <span class="hljs-comment"># 记录请求</span><br>        <span class="hljs-variable language_">self</span>.request_counts[client_ip].append(current_time)<br>        <br>        <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> call_next(request)<br><br>app.add_middleware(RateLimitMiddleware, requests_per_minute=<span class="hljs-number">100</span>)<br></code></pre></td></tr></table></figure><hr><h2 id="10-WebSocket-支持">10. WebSocket 支持</h2><h3 id="基础-WebSocket">基础 WebSocket</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI, WebSocket<br><span class="hljs-keyword">from</span> fastapi.responses <span class="hljs-keyword">import</span> HTMLResponse<br><br>app = FastAPI()<br><br><span class="hljs-meta">@app.websocket(<span class="hljs-params"><span class="hljs-string">&quot;/ws&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">websocket_endpoint</span>(<span class="hljs-params">websocket: WebSocket</span>):<br>    <span class="hljs-string">&quot;&quot;&quot;WebSocket 端点&quot;&quot;&quot;</span><br>    <span class="hljs-keyword">await</span> websocket.accept()<br>    <br>    <span class="hljs-keyword">while</span> <span class="hljs-literal">True</span>:<br>        <span class="hljs-comment"># 接收消息</span><br>        data = <span class="hljs-keyword">await</span> websocket.receive_text()<br>        <span class="hljs-comment"># 处理并返回</span><br>        response = <span class="hljs-string">f&quot;收到：<span class="hljs-subst">&#123;data&#125;</span>&quot;</span><br>        <span class="hljs-keyword">await</span> websocket.send_text(response)<br></code></pre></td></tr></table></figure><h3 id="WebSocket-依赖注入">WebSocket + 依赖注入</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI, WebSocket, Depends, HTTPException<br><span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> <span class="hljs-type">Optional</span><br><br>app = FastAPI()<br><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">get_token</span>(<span class="hljs-params">websocket: WebSocket</span>) -&gt; <span class="hljs-built_in">str</span>:<br>    <span class="hljs-string">&quot;&quot;&quot;从 WebSocket 连接中获取 token&quot;&quot;&quot;</span><br>    token = websocket.query_params.get(<span class="hljs-string">&quot;token&quot;</span>)<br>    <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> token <span class="hljs-keyword">or</span> token != <span class="hljs-string">&quot;secret-token&quot;</span>:<br>        <span class="hljs-keyword">await</span> websocket.close(code=<span class="hljs-number">4001</span>)<br>        <span class="hljs-keyword">raise</span> HTTPException(status_code=<span class="hljs-number">400</span>, detail=<span class="hljs-string">&quot;无效的 Token&quot;</span>)<br>    <span class="hljs-keyword">return</span> token<br><br><span class="hljs-meta">@app.websocket(<span class="hljs-params"><span class="hljs-string">&quot;/ws/auth&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">authenticated_websocket</span>(<span class="hljs-params"></span><br><span class="hljs-params">    websocket: WebSocket,</span><br><span class="hljs-params">    token: <span class="hljs-built_in">str</span> = Depends(<span class="hljs-params">get_token</span>)</span><br><span class="hljs-params"></span>):<br>    <span class="hljs-string">&quot;&quot;&quot;需要认证的 WebSocket&quot;&quot;&quot;</span><br>    <span class="hljs-keyword">await</span> websocket.accept()<br>    <span class="hljs-keyword">await</span> websocket.send_text(<span class="hljs-string">f&quot;欢迎！Token: <span class="hljs-subst">&#123;token&#125;</span>&quot;</span>)<br>    <br>    <span class="hljs-keyword">while</span> <span class="hljs-literal">True</span>:<br>        data = <span class="hljs-keyword">await</span> websocket.receive_text()<br>        <span class="hljs-keyword">await</span> websocket.send_text(<span class="hljs-string">f&quot;Echo: <span class="hljs-subst">&#123;data&#125;</span>&quot;</span>)<br></code></pre></td></tr></table></figure><h3 id="WebSocket-聊天室示例">WebSocket 聊天室示例</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI, WebSocket, WebSocketDisconnect<br><span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> <span class="hljs-type">List</span><br><span class="hljs-keyword">import</span> json<br><br>app = FastAPI()<br><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">ConnectionManager</span>:<br>    <span class="hljs-string">&quot;&quot;&quot;管理 WebSocket 连接&quot;&quot;&quot;</span><br>    <br>    <span class="hljs-keyword">def</span> <span class="hljs-title function_">__init__</span>(<span class="hljs-params">self</span>):<br>        <span class="hljs-variable language_">self</span>.active_connections: <span class="hljs-type">List</span>[WebSocket] = []<br>    <br>    <span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">connect</span>(<span class="hljs-params">self, websocket: WebSocket</span>):<br>        <span class="hljs-keyword">await</span> websocket.accept()<br>        <span class="hljs-variable language_">self</span>.active_connections.append(websocket)<br>    <br>    <span class="hljs-keyword">def</span> <span class="hljs-title function_">disconnect</span>(<span class="hljs-params">self, websocket: WebSocket</span>):<br>        <span class="hljs-variable language_">self</span>.active_connections.remove(websocket)<br>    <br>    <span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">broadcast</span>(<span class="hljs-params">self, message: <span class="hljs-built_in">str</span></span>):<br>        <span class="hljs-string">&quot;&quot;&quot;广播消息给所有连接&quot;&quot;&quot;</span><br>        <span class="hljs-keyword">for</span> connection <span class="hljs-keyword">in</span> <span class="hljs-variable language_">self</span>.active_connections:<br>            <span class="hljs-keyword">await</span> connection.send_text(message)<br>    <br>    <span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">send_personal</span>(<span class="hljs-params">self, message: <span class="hljs-built_in">str</span>, websocket: WebSocket</span>):<br>        <span class="hljs-string">&quot;&quot;&quot;发送个人消息&quot;&quot;&quot;</span><br>        <span class="hljs-keyword">await</span> websocket.send_text(message)<br><br>manager = ConnectionManager()<br><br><span class="hljs-meta">@app.websocket(<span class="hljs-params"><span class="hljs-string">&quot;/ws/chat&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">chat_websocket</span>(<span class="hljs-params">websocket: WebSocket</span>):<br>    <span class="hljs-keyword">await</span> manager.connect(websocket)<br>    <span class="hljs-keyword">try</span>:<br>        <span class="hljs-keyword">await</span> manager.broadcast(<span class="hljs-string">f&quot;新用户加入聊天室&quot;</span>)<br>        <span class="hljs-keyword">while</span> <span class="hljs-literal">True</span>:<br>            data = <span class="hljs-keyword">await</span> websocket.receive_text()<br>            message = json.loads(data)<br>            <span class="hljs-keyword">await</span> manager.broadcast(<br>                json.dumps(&#123;<br>                    <span class="hljs-string">&quot;user&quot;</span>: message.get(<span class="hljs-string">&quot;user&quot;</span>, <span class="hljs-string">&quot;匿名&quot;</span>),<br>                    <span class="hljs-string">&quot;message&quot;</span>: message.get(<span class="hljs-string">&quot;text&quot;</span>, <span class="hljs-string">&quot;&quot;</span>)<br>                &#125;)<br>            )<br>    <span class="hljs-keyword">except</span> WebSocketDisconnect:<br>        manager.disconnect(websocket)<br>        <span class="hljs-keyword">await</span> manager.broadcast(<span class="hljs-string">&quot;用户离开了聊天室&quot;</span>)<br></code></pre></td></tr></table></figure><hr><h2 id="11-后台任务">11. 后台任务</h2><h3 id="基础后台任务">基础后台任务</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI, BackgroundTasks<br><span class="hljs-keyword">import</span> time<br><br>app = FastAPI()<br><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">send_email</span>(<span class="hljs-params">email: <span class="hljs-built_in">str</span>, message: <span class="hljs-built_in">str</span></span>):<br>    <span class="hljs-string">&quot;&quot;&quot;模拟发送邮件&quot;&quot;&quot;</span><br>    <span class="hljs-built_in">print</span>(<span class="hljs-string">f&quot;发送邮件到 <span class="hljs-subst">&#123;email&#125;</span>: <span class="hljs-subst">&#123;message&#125;</span>&quot;</span>)<br>    time.sleep(<span class="hljs-number">2</span>)  <span class="hljs-comment"># 模拟耗时操作</span><br>    <span class="hljs-built_in">print</span>(<span class="hljs-string">&quot;邮件发送完成&quot;</span>)<br><br><span class="hljs-meta">@app.post(<span class="hljs-params"><span class="hljs-string">&quot;/send-email&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">send_email_endpoint</span>(<span class="hljs-params"></span><br><span class="hljs-params">    email: <span class="hljs-built_in">str</span>,</span><br><span class="hljs-params">    background_tasks: BackgroundTasks</span><br><span class="hljs-params"></span>):<br>    <span class="hljs-string">&quot;&quot;&quot;</span><br><span class="hljs-string">    后台任务会在响应返回后异步执行</span><br><span class="hljs-string">    用户会立即收到响应，而不用等待邮件发送完成</span><br><span class="hljs-string">    &quot;&quot;&quot;</span><br>    background_tasks.add_task(send_email, email, <span class="hljs-string">&quot;欢迎注册！&quot;</span>)<br>    <span class="hljs-keyword">return</span> &#123;<span class="hljs-string">&quot;message&quot;</span>: <span class="hljs-string">&quot;邮件正在发送中&quot;</span>, <span class="hljs-string">&quot;email&quot;</span>: email&#125;<br><br><span class="hljs-meta">@app.post(<span class="hljs-params"><span class="hljs-string">&quot;/multiple-tasks&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">multiple_background_tasks</span>(<span class="hljs-params"></span><br><span class="hljs-params">    background_tasks: BackgroundTasks</span><br><span class="hljs-params"></span>):<br>    <span class="hljs-string">&quot;&quot;&quot;添加多个后台任务&quot;&quot;&quot;</span><br>    background_tasks.add_task(send_email, <span class="hljs-string">&quot;user1@example.com&quot;</span>, <span class="hljs-string">&quot;通知 1&quot;</span>)<br>    background_tasks.add_task(send_email, <span class="hljs-string">&quot;user2@example.com&quot;</span>, <span class="hljs-string">&quot;通知 2&quot;</span>)<br>    background_tasks.add_task(send_email, <span class="hljs-string">&quot;user3@example.com&quot;</span>, <span class="hljs-string">&quot;通知 3&quot;</span>)<br>    <span class="hljs-keyword">return</span> &#123;<span class="hljs-string">&quot;message&quot;</span>: <span class="hljs-string">&quot;3 个邮件正在发送&quot;</span>&#125;<br></code></pre></td></tr></table></figure><h3 id="使用-Celery-处理重型任务">使用 Celery 处理重型任务</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-comment"># 重型任务建议使用 Celery</span><br><span class="hljs-comment"># pip install celery[redis]</span><br><br><span class="hljs-keyword">from</span> celery <span class="hljs-keyword">import</span> Celery<br><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI, BackgroundTasks<br><br>app = FastAPI()<br><br><span class="hljs-comment"># Celery 配置</span><br>celery_app = Celery(<br>    <span class="hljs-string">&#x27;tasks&#x27;</span>,<br>    broker=<span class="hljs-string">&#x27;redis://localhost:6379/0&#x27;</span>,<br>    backend=<span class="hljs-string">&#x27;redis://localhost:6379/0&#x27;</span><br>)<br><br><span class="hljs-meta">@celery_app.task</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">process_large_file</span>(<span class="hljs-params">file_path: <span class="hljs-built_in">str</span></span>):<br>    <span class="hljs-string">&quot;&quot;&quot;处理大文件的 Celery 任务&quot;&quot;&quot;</span><br>    <span class="hljs-comment"># 耗时操作</span><br>    time.sleep(<span class="hljs-number">10</span>)<br>    <span class="hljs-keyword">return</span> &#123;<span class="hljs-string">&quot;processed&quot;</span>: file_path&#125;<br><br><span class="hljs-meta">@app.post(<span class="hljs-params"><span class="hljs-string">&quot;/process-file&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">process_file</span>(<span class="hljs-params">file_path: <span class="hljs-built_in">str</span></span>):<br>    <span class="hljs-string">&quot;&quot;&quot;</span><br><span class="hljs-string">    返回任务 ID，用户可以稍后查询结果</span><br><span class="hljs-string">    &quot;&quot;&quot;</span><br>    task = process_large_file.delay(file_path)<br>    <span class="hljs-keyword">return</span> &#123;<span class="hljs-string">&quot;task_id&quot;</span>: task.<span class="hljs-built_in">id</span>, <span class="hljs-string">&quot;status&quot;</span>: <span class="hljs-string">&quot;processing&quot;</span>&#125;<br><br><span class="hljs-meta">@app.get(<span class="hljs-params"><span class="hljs-string">&quot;/task-status/&#123;task_id&#125;&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">get_task_status</span>(<span class="hljs-params">task_id: <span class="hljs-built_in">str</span></span>):<br>    <span class="hljs-keyword">from</span> celery.result <span class="hljs-keyword">import</span> AsyncResult<br>    task_result = AsyncResult(task_id, app=celery_app)<br>    <span class="hljs-keyword">return</span> &#123;<br>        <span class="hljs-string">&quot;task_id&quot;</span>: task_id,<br>        <span class="hljs-string">&quot;status&quot;</span>: task_result.status,<br>        <span class="hljs-string">&quot;result&quot;</span>: task_result.result <span class="hljs-keyword">if</span> task_result.ready() <span class="hljs-keyword">else</span> <span class="hljs-literal">None</span><br>    &#125;<br></code></pre></td></tr></table></figure><hr><h2 id="12-数据库集成">12. 数据库集成</h2><h3 id="SQLAlchemy-集成">SQLAlchemy 集成</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI, Depends, HTTPException<br><span class="hljs-keyword">from</span> sqlalchemy <span class="hljs-keyword">import</span> create_engine, Column, Integer, String<br><span class="hljs-keyword">from</span> sqlalchemy.ext.declarative <span class="hljs-keyword">import</span> declarative_base<br><span class="hljs-keyword">from</span> sqlalchemy.orm <span class="hljs-keyword">import</span> sessionmaker, Session<br><span class="hljs-keyword">from</span> pydantic <span class="hljs-keyword">import</span> BaseModel<br><span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> <span class="hljs-type">Optional</span>, <span class="hljs-type">List</span><br><br>app = FastAPI()<br><br><span class="hljs-comment"># 数据库配置</span><br>DATABASE_URL = <span class="hljs-string">&quot;sqlite:///./test.db&quot;</span><br>engine = create_engine(DATABASE_URL, connect_args=&#123;<span class="hljs-string">&quot;check_same_thread&quot;</span>: <span class="hljs-literal">False</span>&#125;)<br>SessionLocal = sessionmaker(autocommit=<span class="hljs-literal">False</span>, autoflush=<span class="hljs-literal">False</span>, bind=engine)<br>Base = declarative_base()<br><br><span class="hljs-comment"># 模型定义</span><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">User</span>(<span class="hljs-title class_ inherited__">Base</span>):<br>    __tablename__ = <span class="hljs-string">&quot;users&quot;</span><br>    <br>    <span class="hljs-built_in">id</span> = Column(Integer, primary_key=<span class="hljs-literal">True</span>, index=<span class="hljs-literal">True</span>)<br>    username = Column(String, unique=<span class="hljs-literal">True</span>, index=<span class="hljs-literal">True</span>)<br>    email = Column(String, unique=<span class="hljs-literal">True</span>, index=<span class="hljs-literal">True</span>)<br>    hashed_password = Column(String)<br><br><span class="hljs-comment"># 创建表</span><br>Base.metadata.create_all(bind=engine)<br><br><span class="hljs-comment"># Pydantic 模型</span><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">UserBase</span>(<span class="hljs-title class_ inherited__">BaseModel</span>):<br>    username: <span class="hljs-built_in">str</span><br>    email: <span class="hljs-built_in">str</span><br><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">UserCreate</span>(<span class="hljs-title class_ inherited__">UserBase</span>):<br>    password: <span class="hljs-built_in">str</span><br><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">UserResponse</span>(<span class="hljs-title class_ inherited__">UserBase</span>):<br>    <span class="hljs-built_in">id</span>: <span class="hljs-built_in">int</span><br>    <br>    <span class="hljs-keyword">class</span> <span class="hljs-title class_">Config</span>:<br>        from_attributes = <span class="hljs-literal">True</span><br><br><span class="hljs-comment"># 依赖项</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">get_db</span>():<br>    db = SessionLocal()<br>    <span class="hljs-keyword">try</span>:<br>        <span class="hljs-keyword">yield</span> db<br>    <span class="hljs-keyword">finally</span>:<br>        db.close()<br><br><span class="hljs-comment"># CRUD 端点</span><br><span class="hljs-meta">@app.post(<span class="hljs-params"><span class="hljs-string">&quot;/users&quot;</span>, response_model=UserResponse</span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">create_user</span>(<span class="hljs-params">user: UserCreate, db: Session = Depends(<span class="hljs-params">get_db</span>)</span>):<br>    db_user = User(<br>        username=user.username,<br>        email=user.email,<br>        hashed_password=user.password  <span class="hljs-comment"># 实际应使用 bcrypt 加密</span><br>    )<br>    db.add(db_user)<br>    db.commit()<br>    db.refresh(db_user)<br>    <span class="hljs-keyword">return</span> db_user<br><br><span class="hljs-meta">@app.get(<span class="hljs-params"><span class="hljs-string">&quot;/users&quot;</span>, response_model=<span class="hljs-type">List</span>[UserResponse]</span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">read_users</span>(<span class="hljs-params">skip: <span class="hljs-built_in">int</span> = <span class="hljs-number">0</span>, limit: <span class="hljs-built_in">int</span> = <span class="hljs-number">100</span>, db: Session = Depends(<span class="hljs-params">get_db</span>)</span>):<br>    users = db.query(User).offset(skip).limit(limit).<span class="hljs-built_in">all</span>()<br>    <span class="hljs-keyword">return</span> users<br><br><span class="hljs-meta">@app.get(<span class="hljs-params"><span class="hljs-string">&quot;/users/&#123;user_id&#125;&quot;</span>, response_model=UserResponse</span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">read_user</span>(<span class="hljs-params">user_id: <span class="hljs-built_in">int</span>, db: Session = Depends(<span class="hljs-params">get_db</span>)</span>):<br>    user = db.query(User).<span class="hljs-built_in">filter</span>(User.<span class="hljs-built_in">id</span> == user_id).first()<br>    <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> user:<br>        <span class="hljs-keyword">raise</span> HTTPException(status_code=<span class="hljs-number">404</span>, detail=<span class="hljs-string">&quot;用户不存在&quot;</span>)<br>    <span class="hljs-keyword">return</span> user<br></code></pre></td></tr></table></figure><h3 id="Tortoise-ORM（异步）">Tortoise ORM（异步）</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI<br><span class="hljs-keyword">from</span> tortoise <span class="hljs-keyword">import</span> fields, models<br><span class="hljs-keyword">from</span> tortoise.contrib.fastapi <span class="hljs-keyword">import</span> register_tortoise<br><br>app = FastAPI()<br><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">User</span>(models.Model):<br>    <span class="hljs-built_in">id</span> = fields.IntField(pk=<span class="hljs-literal">True</span>)<br>    username = fields.CharField(max_length=<span class="hljs-number">50</span>, unique=<span class="hljs-literal">True</span>)<br>    email = fields.CharField(max_length=<span class="hljs-number">100</span>)<br>    created_at = fields.DatetimeField(auto_now_add=<span class="hljs-literal">True</span>)<br>    <br>    <span class="hljs-keyword">class</span> <span class="hljs-title class_">Meta</span>:<br>        table = <span class="hljs-string">&quot;users&quot;</span><br><br><span class="hljs-meta">@app.get(<span class="hljs-params"><span class="hljs-string">&quot;/users&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">get_users</span>():<br>    users = <span class="hljs-keyword">await</span> User.<span class="hljs-built_in">all</span>()<br>    <span class="hljs-keyword">return</span> [&#123;<span class="hljs-string">&quot;id&quot;</span>: u.<span class="hljs-built_in">id</span>, <span class="hljs-string">&quot;username&quot;</span>: u.username&#125; <span class="hljs-keyword">for</span> u <span class="hljs-keyword">in</span> users]<br><br><span class="hljs-meta">@app.post(<span class="hljs-params"><span class="hljs-string">&quot;/users&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">create_user</span>(<span class="hljs-params">username: <span class="hljs-built_in">str</span>, email: <span class="hljs-built_in">str</span></span>):<br>    user = <span class="hljs-keyword">await</span> User.create(username=username, email=email)<br>    <span class="hljs-keyword">return</span> &#123;<span class="hljs-string">&quot;id&quot;</span>: user.<span class="hljs-built_in">id</span>, <span class="hljs-string">&quot;username&quot;</span>: user.username&#125;<br><br><span class="hljs-comment"># 注册 Tortoise</span><br>register_tortoise(<br>    app,<br>    db_url=<span class="hljs-string">&quot;sqlite://:memory:&quot;</span>,<br>    modules=&#123;<span class="hljs-string">&quot;models&quot;</span>: [<span class="hljs-string">&quot;__main__&quot;</span>]&#125;,<br>    generate_schemas=<span class="hljs-literal">True</span>,<br>    add_exception_handlers=<span class="hljs-literal">True</span><br>)<br></code></pre></td></tr></table></figure><hr><h2 id="13-认证与授权">13. 认证与授权</h2><h3 id="JWT-认证">JWT 认证</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI, Depends, HTTPException, status<br><span class="hljs-keyword">from</span> fastapi.security <span class="hljs-keyword">import</span> OAuth2PasswordBearer, OAuth2PasswordRequestForm<br><span class="hljs-keyword">from</span> pydantic <span class="hljs-keyword">import</span> BaseModel<br><span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> <span class="hljs-type">Optional</span><br><span class="hljs-keyword">from</span> datetime <span class="hljs-keyword">import</span> datetime, timedelta<br><span class="hljs-keyword">from</span> jose <span class="hljs-keyword">import</span> JWTError, jwt<br><br>app = FastAPI()<br><br><span class="hljs-comment"># 配置</span><br>SECRET_KEY = <span class="hljs-string">&quot;your-secret-key&quot;</span><br>ALGORITHM = <span class="hljs-string">&quot;HS256&quot;</span><br>ACCESS_TOKEN_EXPIRE_MINUTES = <span class="hljs-number">30</span><br><br><span class="hljs-comment"># 模拟用户数据库</span><br>fake_users_db = &#123;<br>    <span class="hljs-string">&quot;admin&quot;</span>: &#123;<br>        <span class="hljs-string">&quot;username&quot;</span>: <span class="hljs-string">&quot;admin&quot;</span>,<br>        <span class="hljs-string">&quot;password&quot;</span>: <span class="hljs-string">&quot;secret&quot;</span>,  <span class="hljs-comment"># 实际应使用 bcrypt</span><br>        <span class="hljs-string">&quot;disabled&quot;</span>: <span class="hljs-literal">False</span><br>    &#125;<br>&#125;<br><br><span class="hljs-comment"># OAuth2 scheme</span><br>oauth2_scheme = OAuth2PasswordBearer(tokenUrl=<span class="hljs-string">&quot;token&quot;</span>)<br><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">Token</span>(<span class="hljs-title class_ inherited__">BaseModel</span>):<br>    access_token: <span class="hljs-built_in">str</span><br>    token_type: <span class="hljs-built_in">str</span><br><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">User</span>(<span class="hljs-title class_ inherited__">BaseModel</span>):<br>    username: <span class="hljs-built_in">str</span><br>    disabled: <span class="hljs-type">Optional</span>[<span class="hljs-built_in">bool</span>] = <span class="hljs-literal">None</span><br><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">create_access_token</span>(<span class="hljs-params">data: <span class="hljs-built_in">dict</span>, expires_delta: <span class="hljs-type">Optional</span>[timedelta] = <span class="hljs-literal">None</span></span>):<br>    to_encode = data.copy()<br>    expire = datetime.utcnow() + (expires_delta <span class="hljs-keyword">or</span> timedelta(minutes=<span class="hljs-number">15</span>))<br>    to_encode.update(&#123;<span class="hljs-string">&quot;exp&quot;</span>: expire&#125;)<br>    <span class="hljs-keyword">return</span> jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)<br><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">get_current_user</span>(<span class="hljs-params">token: <span class="hljs-built_in">str</span> = Depends(<span class="hljs-params">oauth2_scheme</span>)</span>):<br>    credentials_exception = HTTPException(<br>        status_code=status.HTTP_401_UNAUTHORIZED,<br>        detail=<span class="hljs-string">&quot;无法验证凭据&quot;</span>,<br>        headers=&#123;<span class="hljs-string">&quot;WWW-Authenticate&quot;</span>: <span class="hljs-string">&quot;Bearer&quot;</span>&#125;,<br>    )<br>    <span class="hljs-keyword">try</span>:<br>        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])<br>        username: <span class="hljs-built_in">str</span> = payload.get(<span class="hljs-string">&quot;sub&quot;</span>)<br>        <span class="hljs-keyword">if</span> username <span class="hljs-keyword">is</span> <span class="hljs-literal">None</span>:<br>            <span class="hljs-keyword">raise</span> credentials_exception<br>    <span class="hljs-keyword">except</span> JWTError:<br>        <span class="hljs-keyword">raise</span> credentials_exception<br>    <br>    user = fake_users_db.get(username)<br>    <span class="hljs-keyword">if</span> user <span class="hljs-keyword">is</span> <span class="hljs-literal">None</span>:<br>        <span class="hljs-keyword">raise</span> credentials_exception<br>    <span class="hljs-keyword">return</span> User(**user)<br><br><span class="hljs-meta">@app.post(<span class="hljs-params"><span class="hljs-string">&quot;/token&quot;</span>, response_model=Token</span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">login</span>(<span class="hljs-params">form_data: OAuth2PasswordRequestForm = Depends(<span class="hljs-params"></span>)</span>):<br>    user = fake_users_db.get(form_data.username)<br>    <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> user <span class="hljs-keyword">or</span> user[<span class="hljs-string">&quot;password&quot;</span>] != form_data.password:<br>        <span class="hljs-keyword">raise</span> HTTPException(<br>            status_code=status.HTTP_401_UNAUTHORIZED,<br>            detail=<span class="hljs-string">&quot;用户名或密码错误&quot;</span>,<br>            headers=&#123;<span class="hljs-string">&quot;WWW-Authenticate&quot;</span>: <span class="hljs-string">&quot;Bearer&quot;</span>&#125;,<br>        )<br>    <br>    access_token = create_access_token(<br>        data=&#123;<span class="hljs-string">&quot;sub&quot;</span>: user[<span class="hljs-string">&quot;username&quot;</span>]&#125;,<br>        expires_delta=timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)<br>    )<br>    <span class="hljs-keyword">return</span> &#123;<span class="hljs-string">&quot;access_token&quot;</span>: access_token, <span class="hljs-string">&quot;token_type&quot;</span>: <span class="hljs-string">&quot;bearer&quot;</span>&#125;<br><br><span class="hljs-meta">@app.get(<span class="hljs-params"><span class="hljs-string">&quot;/users/me&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">read_users_me</span>(<span class="hljs-params">current_user: User = Depends(<span class="hljs-params">get_current_user</span>)</span>):<br>    <span class="hljs-keyword">return</span> current_user<br></code></pre></td></tr></table></figure><h3 id="权限系统">权限系统</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI, Depends, HTTPException<br><span class="hljs-keyword">from</span> fastapi.security <span class="hljs-keyword">import</span> OAuth2PasswordBearer<br><span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> <span class="hljs-type">List</span><br><br>app = FastAPI()<br>oauth2_scheme = OAuth2PasswordBearer(tokenUrl=<span class="hljs-string">&quot;token&quot;</span>)<br><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">Permissions</span>:<br>    ADMIN = <span class="hljs-string">&quot;admin&quot;</span><br>    USER_READ = <span class="hljs-string">&quot;user:read&quot;</span><br>    USER_WRITE = <span class="hljs-string">&quot;user:write&quot;</span><br><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">require_permissions</span>(<span class="hljs-params">required_permissions: <span class="hljs-type">List</span>[<span class="hljs-built_in">str</span>]</span>):<br>    <span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">permission_checker</span>(<span class="hljs-params">current_user: <span class="hljs-built_in">dict</span> = Depends(<span class="hljs-params">get_current_user</span>)</span>):<br>        user_permissions = current_user.get(<span class="hljs-string">&quot;permissions&quot;</span>, [])<br>        <br>        <span class="hljs-comment"># 检查是否有所有必需的权限</span><br>        <span class="hljs-keyword">for</span> perm <span class="hljs-keyword">in</span> required_permissions:<br>            <span class="hljs-keyword">if</span> perm <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> user_permissions <span class="hljs-keyword">and</span> Permissions.ADMIN <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> user_permissions:<br>                <span class="hljs-keyword">raise</span> HTTPException(<br>                    status_code=<span class="hljs-number">403</span>,<br>                    detail=<span class="hljs-string">f&quot;缺少权限：<span class="hljs-subst">&#123;perm&#125;</span>&quot;</span><br>                )<br>        <span class="hljs-keyword">return</span> current_user<br>    <span class="hljs-keyword">return</span> permission_checker<br><br><span class="hljs-meta">@app.get(<span class="hljs-params"><span class="hljs-string">&quot;/admin/users&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">list_all_users</span>(<span class="hljs-params"></span><br><span class="hljs-params">    current_user: <span class="hljs-built_in">dict</span> = Depends(<span class="hljs-params">require_permissions(<span class="hljs-params">[Permissions.USER_READ]</span>)</span>)</span><br><span class="hljs-params"></span>):<br>    <span class="hljs-keyword">return</span> &#123;<span class="hljs-string">&quot;message&quot;</span>: <span class="hljs-string">&quot;用户列表&quot;</span>, <span class="hljs-string">&quot;user&quot;</span>: current_user[<span class="hljs-string">&quot;username&quot;</span>]&#125;<br><br><span class="hljs-meta">@app.post(<span class="hljs-params"><span class="hljs-string">&quot;/admin/users&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">create_user_endpoint</span>(<span class="hljs-params"></span><br><span class="hljs-params">    current_user: <span class="hljs-built_in">dict</span> = Depends(<span class="hljs-params">require_permissions(<span class="hljs-params">[Permissions.USER_WRITE]</span>)</span>)</span><br><span class="hljs-params"></span>):<br>    <span class="hljs-keyword">return</span> &#123;<span class="hljs-string">&quot;message&quot;</span>: <span class="hljs-string">&quot;创建用户&quot;</span>, <span class="hljs-string">&quot;user&quot;</span>: current_user[<span class="hljs-string">&quot;username&quot;</span>]&#125;<br></code></pre></td></tr></table></figure><hr><h2 id="14-测试">14. 测试</h2><h3 id="使用-TestClient">使用 TestClient</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> fastapi.testclient <span class="hljs-keyword">import</span> TestClient<br><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI<br><br>app = FastAPI()<br><br><span class="hljs-meta">@app.get(<span class="hljs-params"><span class="hljs-string">&quot;/&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">root</span>():<br>    <span class="hljs-keyword">return</span> &#123;<span class="hljs-string">&quot;message&quot;</span>: <span class="hljs-string">&quot;Hello World&quot;</span>&#125;<br><br><span class="hljs-meta">@app.post(<span class="hljs-params"><span class="hljs-string">&quot;/items&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">create_item</span>(<span class="hljs-params">name: <span class="hljs-built_in">str</span>, price: <span class="hljs-built_in">float</span></span>):<br>    <span class="hljs-keyword">return</span> &#123;<span class="hljs-string">&quot;name&quot;</span>: name, <span class="hljs-string">&quot;price&quot;</span>: price&#125;<br><br><span class="hljs-comment"># 测试</span><br>client = TestClient(app)<br><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">test_root</span>():<br>    response = client.get(<span class="hljs-string">&quot;/&quot;</span>)<br>    <span class="hljs-keyword">assert</span> response.status_code == <span class="hljs-number">200</span><br>    <span class="hljs-keyword">assert</span> response.json() == &#123;<span class="hljs-string">&quot;message&quot;</span>: <span class="hljs-string">&quot;Hello World&quot;</span>&#125;<br><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">test_create_item</span>():<br>    response = client.post(<span class="hljs-string">&quot;/items?name=Apple&amp;price=1.5&quot;</span>)<br>    <span class="hljs-keyword">assert</span> response.status_code == <span class="hljs-number">200</span><br>    data = response.json()<br>    <span class="hljs-keyword">assert</span> data[<span class="hljs-string">&quot;name&quot;</span>] == <span class="hljs-string">&quot;Apple&quot;</span><br>    <span class="hljs-keyword">assert</span> data[<span class="hljs-string">&quot;price&quot;</span>] == <span class="hljs-number">1.5</span><br><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">test_validation_error</span>():<br>    response = client.post(<span class="hljs-string">&quot;/items?name=Test&quot;</span>)<br>    <span class="hljs-keyword">assert</span> response.status_code == <span class="hljs-number">422</span><br></code></pre></td></tr></table></figure><h3 id="测试带依赖的端点">测试带依赖的端点</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI, Depends<br><span class="hljs-keyword">from</span> fastapi.testclient <span class="hljs-keyword">import</span> TestClient<br><span class="hljs-keyword">from</span> unittest.mock <span class="hljs-keyword">import</span> Mock<br><br>app = FastAPI()<br><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">get_db</span>():<br>    <span class="hljs-keyword">return</span> &#123;<span class="hljs-string">&quot;connection&quot;</span>: <span class="hljs-string">&quot;real_db&quot;</span>&#125;<br><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">get_current_user</span>(<span class="hljs-params">token: <span class="hljs-built_in">str</span></span>):<br>    <span class="hljs-keyword">return</span> &#123;<span class="hljs-string">&quot;username&quot;</span>: <span class="hljs-string">&quot;testuser&quot;</span>&#125;<br><br><span class="hljs-meta">@app.get(<span class="hljs-params"><span class="hljs-string">&quot;/protected&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">protected_route</span>(<span class="hljs-params">user: <span class="hljs-built_in">dict</span> = Depends(<span class="hljs-params">get_current_user</span>)</span>):<br>    <span class="hljs-keyword">return</span> &#123;<span class="hljs-string">&quot;user&quot;</span>: user&#125;<br><br><span class="hljs-comment"># 覆盖依赖进行测试</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">test_protected_route</span>():<br>    app.dependency_overrides[get_current_user] = <span class="hljs-keyword">lambda</span>: &#123;<span class="hljs-string">&quot;username&quot;</span>: <span class="hljs-string">&quot;test_user&quot;</span>&#125;<br>    <br>    client = TestClient(app)<br>    response = client.get(<span class="hljs-string">&quot;/protected&quot;</span>)<br>    <br>    <span class="hljs-keyword">assert</span> response.status_code == <span class="hljs-number">200</span><br>    <span class="hljs-keyword">assert</span> response.json()[<span class="hljs-string">&quot;user&quot;</span>][<span class="hljs-string">&quot;username&quot;</span>] == <span class="hljs-string">&quot;test_user&quot;</span><br>    <br>    <span class="hljs-comment"># 清理覆盖</span><br>    app.dependency_overrides.clear()<br></code></pre></td></tr></table></figure><h3 id="Pytest-集成">Pytest 集成</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-comment"># test_main.py</span><br><span class="hljs-keyword">import</span> pytest<br><span class="hljs-keyword">from</span> fastapi.testclient <span class="hljs-keyword">import</span> TestClient<br><span class="hljs-keyword">from</span> main <span class="hljs-keyword">import</span> app<br><br><span class="hljs-meta">@pytest.fixture</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">client</span>():<br>    <span class="hljs-keyword">return</span> TestClient(app)<br><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">test_read_main</span>(<span class="hljs-params">client</span>):<br>    response = client.get(<span class="hljs-string">&quot;/&quot;</span>)<br>    <span class="hljs-keyword">assert</span> response.status_code == <span class="hljs-number">200</span><br><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">test_read_item</span>(<span class="hljs-params">client</span>):<br>    response = client.get(<span class="hljs-string">&quot;/items/1&quot;</span>)<br>    <span class="hljs-keyword">assert</span> response.status_code == <span class="hljs-number">200</span><br></code></pre></td></tr></table></figure><hr><h2 id="15-项目结构最佳实践">15. 项目结构最佳实践</h2><h3 id="推荐的项目结构">推荐的项目结构</h3><figure class="highlight nix"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><code class="hljs nix">my_project<span class="hljs-symbol">/</span><br>├── app<span class="hljs-symbol">/</span><br>│   ├── __init__.py<br>│   ├── main.py              <span class="hljs-comment"># FastAPI 应用实例</span><br>│   ├── config.py            <span class="hljs-comment"># 配置管理</span><br>│   ├── dependencies.py      <span class="hljs-comment"># 依赖注入</span><br>│   │<br>│   ├── models<span class="hljs-symbol">/</span>              <span class="hljs-comment"># 数据库模型</span><br>│   │   ├── __init__.py<br>│   │   └── user.py<br>│   │<br>│   ├── schemas<span class="hljs-symbol">/</span>             <span class="hljs-comment"># Pydantic 模型</span><br>│   │   ├── __init__.py<br>│   │   └── user.py<br>│   │<br>│   ├── routers<span class="hljs-symbol">/</span>             <span class="hljs-comment"># 路由模块</span><br>│   │   ├── __init__.py<br>│   │   ├── users.py<br>│   │   └── items.py<br>│   │<br>│   ├── services<span class="hljs-symbol">/</span>            <span class="hljs-comment"># 业务逻辑</span><br>│   │   ├── __init__.py<br>│   │   └── user_service.py<br>│   │<br>│   ├── middleware<span class="hljs-symbol">/</span>          <span class="hljs-comment"># 中间件</span><br>│   │   └── auth.py<br>│   │<br>│   └── utils<span class="hljs-symbol">/</span>               <span class="hljs-comment"># 工具函数</span><br>│       └── security.py<br>│<br>├── tests<span class="hljs-symbol">/</span>                   <span class="hljs-comment"># 测试</span><br>│   ├── __init__.py<br>│   └── test_users.py<br>│<br>├── .env                     <span class="hljs-comment"># 环境变量</span><br>├── requirements.txt         <span class="hljs-comment"># 依赖</span><br>└── pytest.ini              <span class="hljs-comment"># Pytest 配置</span><br></code></pre></td></tr></table></figure><h3 id="模块化路由">模块化路由</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-comment"># app/routers/users.py</span><br><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> APIRouter, Depends, HTTPException<br><br>router = APIRouter(prefix=<span class="hljs-string">&quot;/users&quot;</span>, tags=[<span class="hljs-string">&quot;用户&quot;</span>])<br><br><span class="hljs-meta">@router.get(<span class="hljs-params"><span class="hljs-string">&quot;/&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">list_users</span>():<br>    <span class="hljs-keyword">return</span> [&#123;<span class="hljs-string">&quot;id&quot;</span>: <span class="hljs-number">1</span>, <span class="hljs-string">&quot;name&quot;</span>: <span class="hljs-string">&quot;Alice&quot;</span>&#125;]<br><br><span class="hljs-meta">@router.get(<span class="hljs-params"><span class="hljs-string">&quot;/&#123;user_id&#125;&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">get_user</span>(<span class="hljs-params">user_id: <span class="hljs-built_in">int</span></span>):<br>    <span class="hljs-keyword">return</span> &#123;<span class="hljs-string">&quot;id&quot;</span>: user_id, <span class="hljs-string">&quot;name&quot;</span>: <span class="hljs-string">f&quot;User <span class="hljs-subst">&#123;user_id&#125;</span>&quot;</span>&#125;<br><br><span class="hljs-comment"># app/main.py</span><br><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI<br><span class="hljs-keyword">from</span> .routers <span class="hljs-keyword">import</span> users, items<br><br>app = FastAPI(title=<span class="hljs-string">&quot;My API&quot;</span>)<br><br>app.include_router(users.router)<br>app.include_router(items.router, prefix=<span class="hljs-string">&quot;/api/v1&quot;</span>)<br></code></pre></td></tr></table></figure><hr><h2 id="16-性能优化">16. 性能优化</h2><h3 id="异步优化">异步优化</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI<br><span class="hljs-keyword">import</span> asyncio<br><span class="hljs-keyword">import</span> aiohttp<br><br>app = FastAPI()<br><br><span class="hljs-comment"># 好的异步实践</span><br><span class="hljs-meta">@app.get(<span class="hljs-params"><span class="hljs-string">&quot;/fetch-data&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">fetch_data</span>():<br>    <span class="hljs-keyword">async</span> <span class="hljs-keyword">with</span> aiohttp.ClientSession() <span class="hljs-keyword">as</span> session:<br>        <span class="hljs-comment"># 并发请求</span><br>        urls = [<br>            <span class="hljs-string">&quot;https://api.example.com/users&quot;</span>,<br>            <span class="hljs-string">&quot;https://api.example.com/posts&quot;</span>,<br>            <span class="hljs-string">&quot;https://api.example.com/comments&quot;</span><br>        ]<br>        tasks = [session.get(url) <span class="hljs-keyword">for</span> url <span class="hljs-keyword">in</span> urls]<br>        responses = <span class="hljs-keyword">await</span> asyncio.gather(*tasks)<br>        <br>        <span class="hljs-comment"># 并发解析</span><br>        data = <span class="hljs-keyword">await</span> asyncio.gather(*[r.json() <span class="hljs-keyword">for</span> r <span class="hljs-keyword">in</span> responses])<br>        <br>    <span class="hljs-keyword">return</span> &#123;<br>        <span class="hljs-string">&quot;users&quot;</span>: data[<span class="hljs-number">0</span>],<br>        <span class="hljs-string">&quot;posts&quot;</span>: data[<span class="hljs-number">1</span>],<br>        <span class="hljs-string">&quot;comments&quot;</span>: data[<span class="hljs-number">2</span>]<br>    &#125;<br></code></pre></td></tr></table></figure><h3 id="缓存策略">缓存策略</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI, Response<br><span class="hljs-keyword">from</span> functools <span class="hljs-keyword">import</span> lru_cache<br><span class="hljs-keyword">import</span> hashlib<br><span class="hljs-keyword">import</span> json<br><br>app = FastAPI()<br><br><span class="hljs-comment"># 内存缓存</span><br>cache = &#123;&#125;<br><br><span class="hljs-meta">@lru_cache(<span class="hljs-params">maxsize=<span class="hljs-number">128</span></span>)</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">compute_expensive</span>(<span class="hljs-params">value: <span class="hljs-built_in">str</span></span>) -&gt; <span class="hljs-built_in">str</span>:<br>    <span class="hljs-string">&quot;&quot;&quot;CPU 密集型计算的缓存&quot;&quot;&quot;</span><br>    <span class="hljs-keyword">return</span> hashlib.sha256(value.encode()).hexdigest()<br><br><span class="hljs-meta">@app.get(<span class="hljs-params"><span class="hljs-string">&quot;/compute/&#123;value&#125;&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">compute_endpoint</span>(<span class="hljs-params">value: <span class="hljs-built_in">str</span></span>):<br>    result = compute_expensive(value)<br>    <span class="hljs-keyword">return</span> &#123;<span class="hljs-string">&quot;result&quot;</span>: result&#125;<br><br><span class="hljs-comment"># HTTP 缓存头</span><br><span class="hljs-meta">@app.get(<span class="hljs-params"><span class="hljs-string">&quot;/static-data&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">static_data</span>(<span class="hljs-params">response: Response</span>):<br>    response.headers[<span class="hljs-string">&quot;Cache-Control&quot;</span>] = <span class="hljs-string">&quot;public, max-age=3600&quot;</span><br>    <span class="hljs-keyword">return</span> &#123;<span class="hljs-string">&quot;data&quot;</span>: <span class="hljs-string">&quot;static&quot;</span>&#125;<br></code></pre></td></tr></table></figure><h3 id="响应压缩">响应压缩</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI<br><span class="hljs-keyword">from</span> fastapi.middleware.gzip <span class="hljs-keyword">import</span> GZipMiddleware<br><br>app = FastAPI()<br>app.add_middleware(GZipMiddleware, minimum_size=<span class="hljs-number">1000</span>)<br><br><span class="hljs-meta">@app.get(<span class="hljs-params"><span class="hljs-string">&quot;/large-response&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">large_response</span>():<br>    <span class="hljs-keyword">return</span> &#123;<span class="hljs-string">&quot;data&quot;</span>: <span class="hljs-string">&quot;x&quot;</span> * <span class="hljs-number">10000</span>&#125;  <span class="hljs-comment"># 大于 1KB 会自动压缩</span><br></code></pre></td></tr></table></figure><hr><h2 id="附录：常用命令">附录：常用命令</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-comment"># 安装</span><br>pip install fastapi uvicorn[standard]<br>pip install python-jose[cryptography]  <span class="hljs-comment"># JWT</span><br>pip install passlib[bcrypt]           <span class="hljs-comment"># 密码加密</span><br>pip install sqlalchemy                <span class="hljs-comment"># 数据库</span><br>pip install <span class="hljs-string">&quot;uvicorn[standard]&quot;</span>       <span class="hljs-comment"># 完整 uvicorn</span><br><br><span class="hljs-comment"># 运行</span><br>uvicorn main:app --reload             <span class="hljs-comment"># 开发</span><br>uvicorn main:app --workers 4          <span class="hljs-comment"># 生产</span><br><br><span class="hljs-comment"># 测试</span><br>pytest<br>pytest --cov=app                      <span class="hljs-comment"># 覆盖率</span><br><br><span class="hljs-comment"># 代码检查</span><br>mypy app                              <span class="hljs-comment"># 类型检查</span><br>black app                             <span class="hljs-comment"># 格式化</span><br>flake8 app                            <span class="hljs-comment"># 代码规范</span><br></code></pre></td></tr></table></figure><hr><h2 id="学习资源">学习资源</h2><ul><li>官方文档：<a href="https://fastapi.tiangolo.com/">https://fastapi.tiangolo.com/</a></li><li>GitHub: <a href="https://github.com/tiangolo/fastapi">https://github.com/tiangolo/fastapi</a></li><li>中文社区：<a href="https://fastapi-chinese.netlify.app/">https://fastapi-chinese.netlify.app/</a></li></ul><hr>]]>
    </content>
    <id>https://www.furinaluna.top/posts/e8eb82c9.html</id>
    <link href="https://www.furinaluna.top/posts/e8eb82c9.html"/>
    <published>2026-04-04T15:23:15.000Z</published>
    <summary>
      <![CDATA[<h1>FastAPI 精通教程</h1>
<blockquote>
<p><strong>从入门到精通，自我总结，可能有缺，持续更新。。。</strong></p>
</blockquote>
<hr>
<h2 id="目录">目录</h2>
<ol>
<li><a href=]]>
    </summary>
    <title>FastAPI快速入门自我总结</title>
    <updated>2026-09-11T13:48:57.130Z</updated>
  </entry>
  <entry>
    <author>
      <name>Furina</name>
    </author>
    <category term="GEO优化" scheme="https://www.furinaluna.top/categories/GEO%E4%BC%98%E5%8C%96/"/>
    <category term="GEO" scheme="https://www.furinaluna.top/tags/GEO/"/>
    <content>
      <![CDATA[<h1>GEO 优化入门指南</h1><blockquote><p>GEO = Generative Engine Optimization（生成式引擎优化）</p></blockquote><h2 id="一句话解释">一句话解释</h2><p>SEO 是让你的网站出现在 Google 搜索结果里；<strong>GEO 是让你的内容被 ChatGPT、Gemini、Perplexity 这些 AI 引用和推荐</strong>。</p><p>用户越来越多地直接问 AI 而不是搜索引擎，如果 AI 回答里没提到你，你就丢了客户。</p><hr><h2 id="核心技巧（6-招）">核心技巧（6 招）</h2><h3 id="1-结论先行，别废话">1. 结论先行，别废话</h3><p>AI 提取内容时偏爱<strong>前 100 字就给出答案</strong>的文章。</p><ul><li>别写&quot;众所周知…随着时代发展…&quot;这种开头</li><li>直接说结论，再展开解释</li><li>段落不超过 150 字，一段一个观点</li></ul><h3 id="2-结构化写作">2. 结构化写作</h3><p>AI 读内容跟人不一样，它喜欢<strong>格式清晰、层级分明</strong>的内容。</p><table><thead><tr><th>推荐格式</th><th>举例</th></tr></thead><tbody><tr><td>问答（FAQ）</td><td>“什么是 GEO？答：……”</td></tr><tr><td>列表/步骤</td><td>“第一步…第二步…”</td></tr><tr><td>对比表格</td><td>“A vs B 对比”</td></tr><tr><td>带层级的标题</td><td>H1 &gt; H2 &gt; H3 有序嵌套</td></tr></tbody></table><p>技术层面：给网页加 <strong>Schema 标记</strong>（FAQ Schema、HowTo Schema），让 AI 更容易理解你的内容结构。</p><h3 id="3-让别人提到你">3. 让别人提到你</h3><p>AI 判断该引用谁，核心看<strong>权威性</strong>——你被多少可信来源提到过。</p><ul><li>去知乎、公众号、行业媒体发优质内容</li><li>争取被权威网站引用或客座发文</li><li>在 Reddit、YouTube 等平台保持活跃</li><li>被第三方权威提及后，AI 引用率可提升 3 倍以上</li></ul><h3 id="4-多模态内容">4. 多模态内容</h3><p>AI 不只看文字，图片、视频、信息图都会被索引。</p><ul><li>图片：写好 Alt 描述文字</li><li>视频：提供字幕和时间戳章节</li><li>信息图：配一份纯文本版本</li></ul><h3 id="5-保持内容新鲜">5. 保持内容新鲜</h3><p>AI 偏爱<strong>有明确时间标记的新内容</strong>。</p><ul><li>标注&quot;更新于 2026 年 X 月&quot;</li><li>每季度更新数据和案例</li><li>过时内容及时修订或标记</li></ul><h3 id="6-技术基础别拉胯">6. 技术基础别拉胯</h3><ul><li><code>robots.txt</code> 允许 AI 爬虫访问（GPTBot、Google-Extended 等）</li><li>有 SSL 证书（https）</li><li>有 XML Sitemap</li><li>页面能被正常抓取，别全靠 JS 渲染</li></ul><hr><h2 id="怎么检验效果？">怎么检验效果？</h2><p>不看排名，看<strong>AI 有没有引用你</strong>：</p><ol><li>拿你的核心业务问题去问 ChatGPT、Gemini、Perplexity</li><li>看回答里有没有提到你的品牌/网站</li><li>定期跟踪，记录变化</li></ol><hr><h2 id="一张图总结">一张图总结</h2><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs">传统 SEO：写内容 → 优化关键词 → 争排名<br>GEO：写好内容 → 结构化 → 建权威 → 被 AI 引用<br></code></pre></td></tr></table></figure><p><strong>最重要的一句话：</strong> 写内容的时候，想象你在给 AI 喂一份它能直接拿来回答用户问题的参考资料。清晰、准确、有结构、有来源——AI 自然会选你。</p>]]>
    </content>
    <id>https://www.furinaluna.top/posts/8ea135ce.html</id>
    <link href="https://www.furinaluna.top/posts/8ea135ce.html"/>
    <published>2026-04-04T10:22:14.000Z</published>
    <summary>
      <![CDATA[<h1>GEO 优化入门指南</h1>
<blockquote>
<p>GEO = Generative Engine Optimization（生成式引擎优化）</p>
</blockquote>
<h2 id="一句话解释">一句话解释</h2>
<p>SEO 是让你的网站出]]>
    </summary>
    <title>GEO优化</title>
    <updated>2026-09-11T13:48:57.147Z</updated>
  </entry>
  <entry>
    <author>
      <name>Furina</name>
    </author>
    <category term="SEO优化" scheme="https://www.furinaluna.top/categories/SEO%E4%BC%98%E5%8C%96/"/>
    <category term="SEO" scheme="https://www.furinaluna.top/tags/SEO/"/>
    <content>
      <![CDATA[<p>SEO（Search Engine Optimization）优化是指通过一系列技术和策略，提高网站在搜索引擎中的排名，从而增加网站的曝光率和访问量。以下是一些常见的SEO优化方法：</p><blockquote><p>学习一下鱼皮大佬的SEO优化方法：</p></blockquote><p><strong><font style="color:#DF2A3F;">请在做任何 SEO 优化工作前，先完整阅读本文的内容！</font></strong></p><h2 id="一、确定产品的通用默认-TDK">一、确定产品的通用默认 TDK</h2><h3 id="1-1-标题（Title）规范">1.1 标题（Title）规范</h3><p><strong>结构模板</strong></p><p>建议采用三段式结构：页面名称 - 产品名称 | 产品核心介绍</p><p><strong>分隔符选择</strong></p><p>经过 SEO 实践验证，推荐使用以下分隔符组合：</p><ul><li>短横线（-）：用于连接相关词汇，如页面名称与产品名称之间</li><li>竖线（|）：用于分隔不同层级的信息，如产品名称与核心介绍之间</li><li>避免使用：下划线（_）、斜杠（/）、逗号（,）等不利于 SEO 的符号</li></ul><p>这种组合方式有助于搜索引擎更好地识别标题的层次结构，提升关键词权重分配的准确性。</p><p><strong>长度控制</strong></p><ul><li>最佳长度：50-60 个字符（包括空格）</li><li>最大长度：不超过 70 个字符，避免在搜索结果中被截断</li><li>百度特殊性：百度会显示约 30 个汉字，建议核心关键词前置</li></ul><p><strong>内容要求</strong></p><ul><li>必须包含页面主关键词，建议出现在标题前半部分</li><li><strong>每个页面标题必须独一无二，避免重复</strong></li><li>语句通顺自然，符合用户搜索习惯</li><li>避免关键词堆砌，保持可读性</li></ul><h3 id="1-2-描述（Description）规范">1.2 描述（Description）规范</h3><p><strong>功能定位</strong></p><p>描述是网页在搜索结果中的&quot;广告文案&quot;，需要在有限字数内吸引用户点击，同时覆盖核心关键词。</p><p><strong>长度控制</strong></p><ul><li><strong>Google 最佳长度</strong>：150-160 个字符（包括空格）</li><li><strong>百度最佳长度</strong>：约 200 个字符</li><li><strong>建议策略</strong>：前 150 字符放置最核心信息，后续内容作为补充</li></ul><p><strong>内容要求</strong></p><ul><li>自然融入多个核心关键词及相关长尾词</li><li>突出产品核心价值和差异化优势</li><li>包含行动号召（如&quot;立即使用&quot;、&quot;免费体验&quot;等）</li><li><strong>每个页面描述必须独特，不可复制粘贴</strong></li><li>语句流畅，避免单纯罗列关键词</li></ul><p><strong>覆盖策略</strong></p><p>应尽可能覆盖以下维度的关键词：</p><ul><li>产品核心功能关键词</li><li>目标用户群体关键词</li><li>使用场景关键词</li><li>行业/领域关键词</li><li>品牌词和竞品对比词</li></ul><h3 id="1-3-关键词（Keywords）规范">1.3 关键词（Keywords）规范</h3><p><strong>数量控制</strong></p><ul><li>最大数量：不超过 10 个，避免分散权重</li><li>优先级排序：按重要性从高到低排列</li></ul><p><strong>长度控制</strong></p><ul><li>单个关键词：2-8 个汉字为宜</li><li>长尾关键词：不超过 15 个汉字</li><li>避免使用过长的短语</li></ul><p><strong>选择原则</strong></p><ul><li>优先选择搜索量大、竞争度适中的词</li><li>结合页面实际内容，确保相关性</li><li>包含 1-2 个品牌词或产品名称</li><li>适当加入长尾关键词，提升精准流量</li></ul><p>搜索引擎兼容性说明</p><ul><li>Google：已基本不使用 Keywords meta 标签进行排名</li><li>百度：仍会参考 Keywords 标签，建议保留</li><li>其他国内搜索引擎（360、搜狗等）：仍有一定参考价值</li><li>建议：虽然权重降低，但设置正确的关键词不会有负面影响，建议保留</li></ul><h2 id="二、为每个页面模板提供定制的-TDK">二、为每个页面模板提供定制的 TDK</h2><h3 id="2-1-页面模板分类">2.1 页面模板分类</h3><p>页面模板是指具有相同结构和功能的一类页面。常见的页面模板包括：</p><ul><li><strong>首页</strong>：网站门户，展示核心价值</li><li><strong>列表页/分类页</strong>：内容聚合页面（如文章列表、产品分类）</li><li><strong>详情页</strong>：具体内容页面（如文章详情、产品详情）</li><li><strong>功能页</strong>：特定功能页面（如搜索页、个人中心）</li><li><strong>静态页</strong>：关于我们、帮助中心等</li></ul><h3 id="2-2-TDK-定制原则">2.2 TDK 定制原则</h3><p><strong>首页 TDK</strong></p><ul><li>标题：包含品牌词和核心业务关键词</li><li>描述：全面介绍产品定位、核心功能、目标用户</li><li>关键词：选择最核心的 10 个业务关键词</li></ul><p><strong>列表页/分类页 TDK</strong></p><ul><li>标题：分类名称 + 品牌词，体现内容范围</li><li>描述：说明该分类下的内容特点和价值</li><li>关键词：该分类相关的精准关键词</li></ul><p><strong>详情页 TDK</strong></p><ul><li>标题：内容标题 + 分类 + 品牌词</li><li>描述：内容摘要或核心观点提炼（建议自动提取前 150 字并优化）</li><li>关键词：从内容中提取的核心主题词（可通过算法自动提取）</li></ul><p><strong>功能页 TDK</strong></p><ul><li>标题：功能名称 + 功能价值 + 品牌词</li><li>描述：功能介绍和使用场景</li><li>关键词：功能相关的操作词和需求词</li></ul><h3 id="2-3-TDK-优化管理表格">2.3 TDK 优化管理表格</h3><p>建议创建一份 TDK 规范表格，包含以下字段：</p><p><strong>表格必需字段</strong></p><ul><li>页面模板名称</li><li>页面路径规则</li><li>标题模板（含变量占位符）</li><li>描述模板（含变量占位符）</li><li>关键词模板（含变量占位符）</li><li>动态变量说明（如文章标题、分类名称等）</li><li>示例展示</li><li>负责人</li><li>更新时间</li></ul><p><strong>使用场景</strong></p><ul><li>新增页面类型时，开发同学可查表确定 TDK 规则</li><li>产品/运营同学可定期审核和优化 TDK 模板</li><li>便于批量检查和优化现有页面的 TDK 质量</li></ul><h2 id="三、必需的-SEO-Meta-标签清单">三、必需的 SEO Meta 标签清单</h2><h3 id="3-1-基础-Meta-标签（必需）">3.1 基础 Meta 标签（必需）</h3><p><strong>标题标签</strong></p><ul><li>Title 标签：网页最重要的 SEO 元素，必须独一无二</li></ul><p><strong>描述和关键词标签</strong></p><ul><li>Description meta 标签：影响点击率的核心因素</li><li>Keywords meta 标签：虽然权重降低，但建议保留（尤其针对百度）</li></ul><h3 id="3-2-字符编码与视口设置（必需）">3.2 字符编码与视口设置（必需）</h3><p><strong>字符编码</strong></p><ul><li>使用 UTF-8 编码，确保中文正确显示</li></ul><p><strong>移动端适配</strong></p><ul><li>Viewport meta 标签：确保移动端正常显示，对移动搜索排名有影响</li></ul><h3 id="3-3-Open-Graph-标签（强烈推荐）">3.3 Open Graph 标签（强烈推荐）</h3><p><strong>用途说明</strong></p><p>Open Graph 标签主要用于社交媒体分享（如微信、Facebook、Twitter 等），当用户分享链接时，会展示设置的标题、描述和图片。</p><p><strong>必需的 OG 标签</strong></p><ul><li>og:title - 分享标题（可与页面 Title 一致或略有差异）</li><li>og:description - 分享描述</li><li>og:image - 分享图片（建议尺寸：1200x630 像素）</li><li>og:type - 页面类型（网站首页用 website，文章用 article）</li><li>og:url - 页面完整 URL（建议使用规范链接）</li><li>og:locale - 语言地区（如 zh_CN）</li><li>og:site_name - 网站名称</li></ul><p><strong>图片要求</strong></p><ul><li>格式：JPG 或 PNG</li><li>尺寸：推荐 1200x630px（比例 1.91:1）</li><li>大小：不超过 5MB</li><li>必须使用绝对路径（完整 URL）</li></ul><h3 id="3-4-Twitter-Card-标签（可选，面向国际化）">3.4 Twitter Card 标签（可选，面向国际化）</h3><p>如果产品面向海外用户，建议添加：</p><ul><li>twitter:card - 卡片类型（如 summary_large_image）</li><li>twitter:title - 标题</li><li>twitter:description - 描述</li><li>twitter:image - 图片</li></ul><h3 id="3-5-Schema-org-结构化数据标签（推荐）">3.5 <a href="http://Schema.org">Schema.org</a> 结构化数据标签（推荐）</h3><p><strong>用途说明</strong></p><p>帮助搜索引擎更好地理解页面内容，可能获得富文本摘要（Rich Snippets）展示。</p><p><strong>常用 itemprop 标签</strong></p><ul><li>itemprop=“name” - 内容名称</li><li>itemprop=“description” - 内容描述</li><li>itemprop=“image” - 内容图片</li></ul><p><strong>更高级的结构化数据</strong></p><p>建议使用 JSON-LD 格式添加结构化数据（在 head 或 body 中添加），常见类型：</p><ul><li>文章类（Article）：标题、作者、发布时间、图片等</li><li>组织类（Organization）：公司信息、Logo、联系方式等</li><li>面包屑（BreadcrumbList）：导航路径</li><li>评分类（AggregateRating）：用户评分信息</li><li>问答类（FAQPage）：常见问题页面</li></ul><h3 id="3-6-其他重要-Meta-标签">3.6 其他重要 Meta 标签</h3><p><strong>搜索引擎爬虫控制</strong></p><ul><li>robots meta 标签：控制页面是否可被索引和抓取（如 index,follow）</li><li>canonical 标签：指定页面的规范版本，避免重复内容问题</li></ul><p><strong>移动端优化</strong></p><ul><li>format-detection meta 标签：控制手机号、邮箱等的自动识别</li></ul><p><strong>浏览器兼容性</strong></p><ul><li>X-UA-Compatible meta 标签：指定 IE 浏览器渲染模式</li></ul><p><strong>国际化支持</strong></p><ul><li>hreflang 标签：多语言网站指定不同语言版本（link 标签）</li></ul><h3 id="3-7-标签完整性检查清单">3.7 标签完整性检查清单</h3><p>开发完成后，使用以下清单进行检查：</p><p><strong>基础必需项（100%页面必须有）</strong></p><ul><li>✓ Title 标签（唯一、符合长度要求）</li><li>✓ Description meta 标签（唯一、符合长度要求）</li><li>✓ Keywords meta 标签</li><li>✓ UTF-8 编码声明</li><li>✓ Viewport 设置</li></ul><p><strong>社交分享优化（面向用户分享的页面必须有）</strong></p><ul><li>✓ OG:title</li><li>✓ OG:description</li><li>✓ OG:image（使用绝对路径）</li><li>✓ OG:type</li><li>✓ OG:url</li></ul><p><strong>结构化数据（重要页面推荐有）</strong></p><ul><li>✓ <a href="http://Schema.org">Schema.org</a> 标记（itemprop 或 JSON-LD）</li><li>✓ 面包屑导航结构化数据</li><li>✓ 文章/产品结构化数据</li></ul><p><strong>爬虫控制（根据需要）</strong></p><ul><li>✓ Robots meta 标签（需要控制的页面）</li><li>✓ Canonical 标签（有重复内容风险的页面）</li></ul><h3 id="3-8-推荐的标签-HTML-示例代码【重要】">3.8 推荐的标签 HTML 示例代码【重要】</h3><p>以下是一个完整的 SEO Meta 标签示例，涵盖了国内外搜索引擎优化的核心标签：</p><p><strong>完整的 Head 标签示例（综合版本）</strong></p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><code class="hljs html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-keyword">html</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">&quot;zh-CN&quot;</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span><br>  <span class="hljs-comment">&lt;!-- ========== 基础必需标签 ========== --&gt;</span><br>  <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">&quot;UTF-8&quot;</span>&gt;</span><br>  <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">&quot;viewport&quot;</span> <span class="hljs-attr">content</span>=<span class="hljs-string">&quot;width=device-width, initial-scale=1.0&quot;</span>&gt;</span><br>  <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">http-equiv</span>=<span class="hljs-string">&quot;X-UA-Compatible&quot;</span> <span class="hljs-attr">content</span>=<span class="hljs-string">&quot;IE=edge&quot;</span>&gt;</span><br>  <br>  <span class="hljs-comment">&lt;!-- ========== 核心 SEO 标签 ========== --&gt;</span><br>  <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Java面试题大全 - 面试鸭 | 程序员求职面试刷题神器<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span><br>  <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">&quot;description&quot;</span> <span class="hljs-attr">content</span>=<span class="hljs-string">&quot;面试鸭提供海量Java面试题及优质题解，涵盖Java基础、集合框架、多线程、JVM、Spring等核心知识点，助你快速通关校招社招技术面试，找到理想工作。&quot;</span>&gt;</span><br>  <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">&quot;keywords&quot;</span> <span class="hljs-attr">content</span>=<span class="hljs-string">&quot;Java面试题,Java刷题,程序员面试,技术面试,面试鸭&quot;</span>&gt;</span><br>  <br>  <span class="hljs-comment">&lt;!-- ========== Open Graph 标签 ========== --&gt;</span><br>  <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">property</span>=<span class="hljs-string">&quot;og:title&quot;</span> <span class="hljs-attr">content</span>=<span class="hljs-string">&quot;Java面试题大全 - 面试鸭&quot;</span>&gt;</span><br>  <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">property</span>=<span class="hljs-string">&quot;og:description&quot;</span> <span class="hljs-attr">content</span>=<span class="hljs-string">&quot;面试鸭提供海量Java面试题及优质题解，助你快速通关技术面试&quot;</span>&gt;</span><br>  <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">property</span>=<span class="hljs-string">&quot;og:image&quot;</span> <span class="hljs-attr">content</span>=<span class="hljs-string">&quot;https://www.mianshiya.com/images/java-interview-share.jpg&quot;</span>&gt;</span><br>  <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">property</span>=<span class="hljs-string">&quot;og:type&quot;</span> <span class="hljs-attr">content</span>=<span class="hljs-string">&quot;article&quot;</span>&gt;</span><br>  <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">property</span>=<span class="hljs-string">&quot;og:url&quot;</span> <span class="hljs-attr">content</span>=<span class="hljs-string">&quot;https://www.mianshiya.com/bank/java&quot;</span>&gt;</span><br>  <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">property</span>=<span class="hljs-string">&quot;og:locale&quot;</span> <span class="hljs-attr">content</span>=<span class="hljs-string">&quot;zh_CN&quot;</span>&gt;</span><br>  <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">property</span>=<span class="hljs-string">&quot;og:site_name&quot;</span> <span class="hljs-attr">content</span>=<span class="hljs-string">&quot;面试鸭&quot;</span>&gt;</span><br>  <br>  <span class="hljs-comment">&lt;!-- ========== Schema.org 标签 ========== --&gt;</span><br>  <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">itemprop</span>=<span class="hljs-string">&quot;name&quot;</span> <span class="hljs-attr">content</span>=<span class="hljs-string">&quot;Java面试题大全&quot;</span>&gt;</span><br>  <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">itemprop</span>=<span class="hljs-string">&quot;description&quot;</span> <span class="hljs-attr">content</span>=<span class="hljs-string">&quot;面试鸭提供海量Java面试题及优质题解&quot;</span>&gt;</span><br>  <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">itemprop</span>=<span class="hljs-string">&quot;image&quot;</span> <span class="hljs-attr">content</span>=<span class="hljs-string">&quot;https://www.mianshiya.com/images/java-interview.jpg&quot;</span>&gt;</span><br>  <br>  <span class="hljs-comment">&lt;!-- ========== 爬虫控制 ========== --&gt;</span><br>  <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">&quot;robots&quot;</span> <span class="hljs-attr">content</span>=<span class="hljs-string">&quot;index, follow&quot;</span>&gt;</span><br>  <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">&quot;canonical&quot;</span> <span class="hljs-attr">href</span>=<span class="hljs-string">&quot;https://www.mianshiya.com/bank/java&quot;</span>&gt;</span><br>  <br>  <span class="hljs-comment">&lt;!-- ========== 其他优化标签 ========== --&gt;</span><br>  <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">&quot;format-detection&quot;</span> <span class="hljs-attr">content</span>=<span class="hljs-string">&quot;telephone=no&quot;</span>&gt;</span><br>  <br>  <span class="hljs-comment">&lt;!-- 网站图标 --&gt;</span><br>  <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">&quot;icon&quot;</span> <span class="hljs-attr">href</span>=<span class="hljs-string">&quot;/favicon.ico&quot;</span>&gt;</span><br><span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span><br>  <span class="hljs-comment">&lt;!-- 页面内容 --&gt;</span><br><span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span><br><span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span><br><br></code></pre></td></tr></table></figure><p><strong>使用建议</strong></p><ol><li><strong>基础标签</strong>：所有页面都必须包含（charset、viewport、title、description）</li><li><strong>OG 标签</strong>：面向用户分享的页面必须包含，提升社交媒体分享效果</li><li><strong><a href="http://Schema.org">Schema.org</a> 标签</strong>：重要页面建议使用 JSON-LD 格式，有机会获得富文本摘要</li><li><strong>Twitter Card</strong>：国际化产品建议添加</li><li><strong>JSON-LD</strong>：首页、文章页、产品页等重要页面强烈推荐使用</li></ol><p><strong>动态页面处理</strong></p><p>对于使用框架开发的动态页面（如 React、Vue），建议：</p><ul><li>使用服务端渲染（SSR）确保这些标签在 HTML 源代码中</li><li>或使用预渲染工具为搜索引擎生成静态 HTML</li><li>每个路由/页面都应该有独立的 TDK 和 Meta 标签</li></ul><h2 id="四、页面结构化标签优化规范">四、页面结构化标签优化规范</h2><h3 id="4-1-标题标签（H1-H6）优化规范">4.1 标题标签（H1-H6）优化规范</h3><p><strong>H1 标签规范</strong></p><ul><li>唯一性原则：每个页面有且仅有一个 H1 标签</li><li>位置要求：H1 应该是页面主标题，通常在顶部显著位置</li><li>内容要求：<ul><li>包含页面核心关键词</li><li>与 Title 标签内容相关但不完全相同</li><li>长度控制在 20-50 个字符</li><li>语义清晰，描述页面主题</li></ul></li><li>常见错误：Logo 使用 H1、一个页面多个 H1、H1 内容与页面主题无关</li></ul><p><strong>H2-H6 标签规范</strong></p><ul><li>层级结构：严格按照 H1 &gt; H2 &gt; H3 的层级关系使用，不可跳级</li><li>H2 标签：用于页面主要章节标题，一个页面可有多个</li><li>H3-H6 标签：用于子章节和次级内容分类</li><li>SEO 价值：H2-H3 对 SEO 仍有价值，H4-H6 价值较低</li><li>关键词分布：H2-H3 标签中适当融入相关关键词，但避免堆砌</li></ul><p><strong>最佳实践示例</strong></p><p>页面结构建议：</p><ul><li>H1：页面主标题（1 个）</li><li>H2：主要内容板块标题（3-5 个）</li><li>H3：板块内的小节标题（若干个）</li><li>H4-H6：更细分的层级（按需使用）</li></ul><h3 id="4-2-链接标签（A）优化规范">4.2 链接标签（A）优化规范</h3><p><strong>锚文本优化</strong></p><ul><li>描述性原则：锚文本应清晰描述目标页面内容</li><li>避免模糊词（忽略）：禁止使用&quot;点击这里&quot;、“查看更多”、&quot;详情&quot;等无意义文本</li><li>关键词融入：锚文本中自然包含目标页面的关键词</li><li>长度适中：锚文本建议 2-8 个字，不宜过长</li></ul><p><strong>链接属性设置</strong></p><ul><li>title 属性：为链接添加 title 属性，提供额外信息（鼠标悬停时显示）</li><li>rel 属性：<ul><li>外部链接使用 rel=“nofollow”（不传递权重的链接）</li><li>广告/付费链接使用 rel=“sponsored”</li><li>用户生成内容中的链接使用 rel=“ugc”</li><li>同时打开新窗口时使用 rel=“noopener noreferrer”（安全性）</li></ul></li><li>target 属性：站外链接建议使用 target=“_blank” 新窗口打开</li></ul><p><strong>内链策略</strong></p><ul><li>相关性原则：链接到相关性高的内部页面</li><li>合理密度：每 300-500 字添加 1-2 个内链</li><li>避免过度：同一页面不要重复链接到同一目标页面</li><li>重要页面优先：多链接到希望搜索引擎重点抓取的页面</li></ul><p><strong>外链策略</strong></p><ul><li>权威性原则：优先链接到高权重、权威性强的外部网站</li><li>相关性原则：链接到与内容相关的外部资源</li><li>nofollow 使用：对于不信任的外链、广告链接、用户生成内容中的链接，使用 nofollow</li><li>定期检查：避免死链，定期检查外链的有效性</li></ul><h3 id="4-3-图片优化规范">4.3 图片优化规范</h3><p><strong>Alt 属性（必需）</strong></p><ul><li>功能价值：<ul><li>图片无法加载时显示的替代文本</li><li>帮助搜索引擎理解图片内容（图片 SEO 的核心）</li><li>提升网站可访问性（屏幕阅读器会读取 alt 文本）</li></ul></li><li>内容要求：<ul><li>准确描述图片内容，5-15 个字为宜</li><li>自然融入相关关键词（避免堆砌）</li><li>每张图片的 alt 应该独特</li><li>装饰性图片可使用空 alt（alt=“”），但不要省略 alt 属性</li></ul></li><li>常见错误：使用图片文件名作为 alt、所有图片用相同的 alt、完全省略 alt 属性</li></ul><p><strong>Title 属性（推荐）</strong></p><ul><li>鼠标悬停时显示的提示文本</li><li>可提供比 alt 更详细的描述信息</li><li>不是 SEO 的核心因素，但可提升用户体验</li></ul><p><strong>图片文件名优化</strong></p><ul><li>使用描述性文件名，而非随机字符</li><li>使用英文和连字符，如 seo-workflow-guide.jpg</li><li>避免使用中文、空格、特殊字符</li></ul><p><strong>图片格式和大小</strong></p><ul><li>格式选择：<ul><li>照片：使用 JPEG（压缩后质量好）</li><li>图标/Logo：使用 PNG（支持透明）或 SVG（矢量图）</li><li>动画：使用 WebP 或现代格式</li></ul></li><li>尺寸优化：<ul><li>根据显示尺寸提供合适大小的图片</li><li>使用响应式图片（srcset 属性）</li><li>单张图片大小尽量控制在 200KB 以内</li></ul></li><li>懒加载：对非首屏图片使用懒加载（loading=“lazy” 属性），提升首屏加载速度</li></ul><p><strong>图片结构化数据</strong></p><ul><li>对于重要图片（如产品图、文章配图），可添加 ImageObject 结构化数据</li><li>包含图片 URL、描述、作者、版权信息等</li></ul><h2 id="五、搜索引擎内容可见性优化">五、搜索引擎内容可见性优化</h2><h3 id="5-1-内容开放策略">5.1 内容开放策略</h3><p><strong>核心原则：免费内容对搜索引擎完全开放</strong></p><ul><li>价值内容开放：将对用户有价值的核心内容（如免费题目、免费教程、文章全文）完全开放给搜索引擎</li><li>避免登录墙：不要要求搜索引擎爬虫登录才能访问内容</li><li>避免付费墙：免费内容不要设置付费限制（付费内容可设置，但需要正确标记）</li></ul><p><strong>实施策略</strong></p><ul><li>题库类产品：免费题目的题干、题解、答案完全展示给搜索引擎</li><li>教程类产品：免费教程的完整内容开放给搜索引擎</li><li>内容平台：文章、问答等 UGC 内容对搜索引擎可见</li><li>会员内容：可在页面中标注&quot;会员专享&quot;，但仍在 HTML 中渲染内容（使用 CSS 遮罩），或使用结构化数据标记付费内容</li></ul><h3 id="5-2-JavaScript-渲染问题">5.2 JavaScript 渲染问题</h3><p><strong>问题说明</strong></p><p>虽然 Google 已支持 JavaScript 渲染，但抓取成本更高，可能影响收录速度和质量。百度等国内搜索引擎对 JS 渲染的支持仍然有限。</p><p><strong>解决方案</strong></p><ul><li><strong>服务端渲染（SSR）：使用 Next.js、Nuxt.js 等框架实现 SSR</strong></li><li>静态生成（SSG）：对于内容固定的页面，生成静态 HTML</li><li>预渲染：使用 <a href="http://Prerender.io">Prerender.io</a> 等工具为搜索引擎提供预渲染的 HTML</li><li>渐进增强：确保 HTML 中包含主要内容，JavaScript 仅用于增强交互</li></ul><p><strong>检查方法</strong></p><ul><li>禁用浏览器 JavaScript，查看页面内容是否正常显示</li><li>使用 Google Search Console 的 URL 检查工具查看 Google 抓取的内容</li><li>查看网页源代码（右键-查看源代码），检查核心内容是否在 HTML 中</li></ul><h3 id="5-3-爬虫友好性设置">5.3 爬虫友好性设置</h3><p><strong>Robots.txt 文件【重要】</strong></p><ul><li>位置：网站根目录（如 <a href="https://example.com/robots.txt">https://example.com/robots.txt</a> ）</li><li>功能：告诉搜索引擎哪些页面可以抓取，哪些不可以</li><li>注意事项：<ul><li>不要误拦截重要页面</li><li>可拦截后台管理页面、用户隐私页面、重复内容页面</li><li>允许抓取 CSS 和 JS 文件（不要拦截）</li></ul></li><li>定期检查：确保 robots.txt 设置正确，未误拦截</li></ul><p><strong>Sitemap 网站地图【重要】</strong></p><ul><li>XML Sitemap：<ul><li>列出网站所有重要页面的 URL</li><li>包含最后更新时间、更新频率、优先级</li><li>建议按内容类型分多个 sitemap（文章、产品、分类等）</li><li>提交到 Google Search Console、百度站长平台等</li></ul></li><li>HTML Sitemap：<ul><li>为用户提供的网站结构导航页面</li><li>有助于用户和搜索引擎了解网站结构</li></ul></li></ul><p>爬虫抓取速度</p><ul><li>确保服务器稳定，响应速度快（建议 &lt; 200ms）</li><li>合理设置 CDN 加速</li><li>避免对爬虫 IP 进行限流（可在日志中识别爬虫 User-Agent）</li></ul><h3 id="5-4-移动端优化">5.4 移动端优化</h3><p><strong>响应式设计 vs 独立移动站</strong></p><ul><li><strong>响应式设计（推荐）</strong>：一套代码适配所有设备，URL 统一，利于 SEO</li><li>独立移动站：需要设置 canonical 和 alternate 标签，指明 PC 和移动版本的对应关系</li></ul><p><strong>移动友好性要求</strong></p><ul><li>文字大小适中，无需缩放即可阅读</li><li>点击元素间距足够（至少 48x48 像素）</li><li>避免使用 Flash 等移动端不支持的技术</li><li>页面加载速度快（建议 &lt; 3 秒）</li></ul><p><strong>AMP（加速移动页面）</strong></p><ul><li>Google 推出的移动端加速技术</li><li>国内使用较少，国际化产品可考虑</li><li>可在移动搜索结果中获得闪电标识</li></ul><h2 id="六、内容质量和用户体验优化">六、内容质量和用户体验优化</h2><h3 id="6-1-内容原创性和质量">6.1 内容原创性和质量</h3><p><strong>原创内容的重要性</strong></p><ul><li>搜索引擎优先展示原创、高质量内容</li><li>避免大量采集或复制其他网站内容</li><li>对于必须引用的内容，注明出处并添加 canonical 标签</li></ul><p><strong>内容质量标准</strong></p><ul><li><strong>深度</strong>：内容详实，提供真实价值，不是浅尝辄止</li><li><strong>专业性</strong>：展示专业知识和权威性（E-A-T 原则）</li><li><strong>时效性</strong>：及时更新过时内容，保持信息准确</li><li><strong>可读性</strong>：排版清晰、逻辑清楚、配图合理</li><li><strong>长度</strong>：根据主题深度决定，一般不少于 500 字</li></ul><h3 id="6-2-避免-TDK-重复">6.2 避免 TDK 重复</h3><p><strong>重复问题的危害</strong></p><ul><li>搜索引擎无法判断哪个页面更重要</li><li>可能被判定为低质量内容</li><li>降低整站的 SEO 表现</li></ul><p><strong>检查和解决方法</strong></p><ul><li><strong>定期检查</strong>：使用 Screaming Frog、Sitebulb 等工具检查全站 TDK 重复情况</li><li><strong>URL 参数处理</strong>：对于筛选、排序等 URL 参数生成的页面，使用 canonical 标签指向标准版本</li><li><strong>分页处理</strong>：列表页的分页使用 rel=“prev” 和 rel=“next” 标签（或设置 canonical 到第一页）</li><li><strong>动态生成</strong>：详情页的 TDK 应从内容中动态提取，确保每页唯一</li></ul><h3 id="6-3-用户体验信号">6.3 用户体验信号</h3><p><strong>影响 SEO 的用户行为指标</strong></p><ul><li><strong>点击率（CTR）</strong>：搜索结果的点击率高，说明标题描述吸引人</li><li><strong>停留时间</strong>：用户在页面停留时间长，说明内容有价值</li><li><strong>跳出率</strong>：跳出率低，说明用户找到了所需内容</li><li><strong>回访率</strong>：用户多次访问，说明网站有价值</li></ul><p><strong>优化策略</strong></p><ul><li>优化 TDK，提升搜索结果点击率</li><li>提供高质量内容，延长用户停留时间</li><li>改善页面加载速度，降低跳出率</li><li>增加内链，引导用户浏览更多页面</li><li>提供订阅、收藏等功能，提升回访率</li></ul><h2 id="七、技术实施检查清单">七、技术实施检查清单</h2><h3 id="7-1-开发阶段检查项">7.1 开发阶段检查项</h3><p><strong>页面开发时必须完成</strong></p><ul><li>[ ] 每个页面都有唯一的 Title 标签</li><li>[ ] 每个页面都有唯一的 Description meta 标签</li><li>[ ] 每个页面都有合适的 Keywords meta 标签</li><li>[ ] 每个页面都有唯一的 H1 标签</li><li>[ ] H2-H6 标签使用符合层级结构</li><li>[ ] 所有图片都有 alt 属性</li><li>[ ] 图片文件名使用描述性命名</li><li>[ ] 链接使用描述性锚文本，避免&quot;点击这里&quot;</li><li>[ ] 外部链接添加适当的 rel 属性</li><li>[ ] 添加 OG 标签（至少包含 title、description、image）</li><li>[ ] 移动端适配正确（响应式或独立移动站）</li><li>[ ] 页面加载速度优化（Core Web Vitals 达标）</li></ul><p><strong>技术配置必须完成</strong></p><ul><li>[ ] 设置正确的 robots.txt 文件</li><li>[ ] 生成并提交 XML Sitemap</li><li>[ ] 设置 canonical 标签（有重复内容风险的页面）</li><li>[ ] 配置 404 错误页面</li><li>[ ] 启用 HTTPS（SSL 证书）</li><li>[ ] 配置 301 重定向（旧页面迁移）</li><li>[ ] 设置合理的 URL 结构（简短、描述性、包含关键词）</li></ul><h3 id="7-2-上线前检查项">7.2 上线前检查项</h3><p><strong>SEO 基础检查</strong></p><ul><li>[ ] 使用 Google Search Console URL 检查工具测试抓取效果</li><li>[ ] 使用 Screaming Frog 等工具检查全站 TDK 重复情况</li><li>[ ] 检查是否有死链（404 页面）</li><li>[ ] 检查 robots.txt 是否误拦截重要页面</li><li>[ ] 检查 sitemap.xml 是否正确生成</li></ul><p><strong>技术检查</strong></p><ul><li>[ ] 移动端友好性测试（Google Mobile-Friendly Test）</li><li>[ ] 页面速度测试（PageSpeed Insights）</li><li>[ ] 结构化数据测试（Google Rich Results Test）</li><li>[ ] OG 标签测试（Facebook Sharing Debugger、微信开发者工具）</li></ul><h3 id="7-3-上线后持续优化">7.3 上线后持续优化</h3><p><strong>提交到搜索引擎</strong></p><ul><li>[ ] 提交 sitemap 到 Google Search Console</li><li>[ ] 提交 sitemap 到百度站长平台</li><li>[ ] 提交 sitemap 到其他搜索引擎（360、搜狗、必应等）</li></ul><p><strong>监控和分析</strong></p><ul><li>[ ] 设置 Google Analytics 或百度统计</li><li>[ ] 定期查看 Search Console 收录情况</li><li>[ ] 监控关键词排名变化</li><li>[ ] 分析用户搜索词，优化内容策略</li><li>[ ] 定期检查和修复爬虫错误</li></ul><p><strong>内容优化</strong></p><ul><li>[ ] 定期更新高价值内容</li><li>[ ] 根据搜索数据优化现有页面的 TDK</li><li>[ ] 创建更多高质量原创内容</li><li>[ ] 建立内链体系，提升重要页面权重</li></ul><h2 id="八、常见问题和注意事项">八、常见问题和注意事项</h2><h3 id="8-1-TDK-常见错误">8.1 TDK 常见错误</h3><p><strong>错误示例和解决方案</strong></p><ol><li><strong>标题太短或太长</strong><ul><li>错误：标题只有品牌名&quot;编程导航&quot;</li><li>正确：编程导航 - 程序员的成长社区 | 优质编程学习资源大全</li></ul></li><li><strong>描述中堆砌关键词</strong><ul><li>错误：Java、Java 教程、Java 面试、Java 学习、Java 资源、Java 刷题…</li><li>正确：自然地将关键词融入通顺的句子中</li></ul></li><li><strong>关键词设置过多</strong><ul><li>错误：20 个关键词罗列</li><li>正确：3-5 个核心关键词</li></ul></li><li><strong>TDK 与页面内容不符</strong><ul><li>错误：标题是&quot;Java 教程&quot;，页面内容是 Python 教程</li><li>正确：确保 TDK 与页面实际内容高度相关</li></ul></li></ol><h3 id="8-2-结构化标签常见错误">8.2 结构化标签常见错误</h3><ol><li><strong>多个 H1 标签</strong><ul><li>一个页面只能有一个 H1</li></ul></li><li><strong>标题层级跳跃</strong><ul><li>不要从 H1 直接跳到 H3</li></ul></li><li><strong>图片缺少 alt</strong><ul><li>所有图片都应该有 alt 属性</li></ul></li><li><strong>锚文本不明确</strong><ul><li>避免使用&quot;点击这里&quot;、&quot;更多&quot;等模糊词汇</li></ul></li></ol><h3 id="8-3-技术实施注意事项">8.3 技术实施注意事项</h3><p><strong>单页应用（SPA）的 SEO</strong></p><ul><li>使用服务端渲染（SSR）或预渲染</li><li>确保搜索引擎能抓取到完整内容</li><li>每个路由都有独立的 TDK</li></ul><p><strong>多语言网站 SEO</strong></p><ul><li>使用 hreflang 标签指定语言版本</li><li>每个语言版本都有独立的 TDK</li><li>URL 结构清晰（如 /en/, /zh/）</li></ul><p><strong>国际化 SEO</strong></p><ul><li>根据目标国家/地区选择合适的域名（.com, .cn 等）</li><li>设置 Google Search Console 的目标国家</li><li>考虑服务器地理位置（使用 CDN）</li></ul><h2 id="九、附录：推荐工具和资源">九、附录：推荐工具和资源</h2><h3 id="9-1-SEO-检测工具">9.1 SEO 检测工具</h3><p><strong>综合检测</strong></p><ul><li>Google Search Console（必备）</li><li>百度站长平台（必备）</li><li>Screaming Frog SEO Spider（全站扫描）</li><li>Sitebulb（可视化 SEO 审计）</li></ul><p><strong>页面速度</strong></p><ul><li>Google PageSpeed Insights</li><li>GTmetrix</li><li>WebPageTest</li></ul><p><strong>移动端</strong></p><ul><li>Google Mobile-Friendly Test</li><li>百度移动友好度检测</li></ul><p><strong>结构化数据</strong></p><ul><li>Google Rich Results Test</li><li><a href="http://Schema.org">Schema.org</a> Validator</li></ul><p><strong>社交分享</strong></p><ul><li>Facebook Sharing Debugger</li><li>Twitter Card Validator</li><li>微信开发者工具</li></ul><h3 id="9-2-关键词研究工具">9.2 关键词研究工具</h3><ul><li>Google Keyword Planner</li><li>百度关键词规划师</li><li>Ahrefs Keyword Explorer</li><li>SEMrush</li><li>5118（国内关键词工具）</li></ul><h3 id="9-3-学习资源">9.3 学习资源</h3><p><strong>官方文档</strong></p><ul><li>Google 搜索中心文档</li><li>百度搜索资源平台</li><li><a href="http://Schema.org">Schema.org</a> 官网</li></ul><p><strong>行业博客</strong></p><ul><li>Search Engine Journal</li><li>Moz Blog</li><li>百度搜索资源平台博客</li></ul><h2 id="十、总结">十、总结</h2><p>本 SEO 工作流指南涵盖了从 TDK 设置、Meta 标签配置、页面结构优化到技术实施的完整流程。开发团队在实施时应遵循以下核心原则：</p><p><strong>三大核心原则</strong></p><ol><li>用户第一：优先考虑用户体验，SEO 优化应服务于用户需求</li><li>内容为王：高质量、原创、有价值的内容是 SEO 的基础</li><li>技术支撑：正确的技术实施确保搜索引擎能够发现、抓取和理解网站内容</li></ol><p><strong>实施优先级</strong></p><ul><li>第一优先级：TDK 设置、H1 标签、图片 alt、移动端适配</li><li>第二优先级：OG 标签、结构化数据、页面速度优化</li><li>第三优先级：高级优化（AMP、国际化、多语言等）</li></ul><p><strong>持续优化</strong></p><p>SEO 不是一次性工作，需要持续监控、分析和优化。建议每月进行一次 SEO 审计，根据数据反馈调整策略。</p><p>通过遵循本指南，开发团队能够系统化地完成网站 SEO 优化，提升网站在搜索引擎中的可见性和排名，最终带来更多高质量的自然流量。</p>]]>
    </content>
    <id>https://www.furinaluna.top/posts/7f8d26b3.html</id>
    <link href="https://www.furinaluna.top/posts/7f8d26b3.html"/>
    <published>2026-04-04T10:17:02.000Z</published>
    <summary>
      <![CDATA[<p>SEO（Search Engine Optimization）优化是指通过一系列技术和策略，提高网站在搜索引擎中的排名，从而增加网站的曝光率和访问量。以下是一些常见的SEO优化方法：</p>
<blockquote>
<p>学习一下鱼皮大佬的SEO优化方法：</p>
</b]]>
    </summary>
    <title>SEO优化</title>
    <updated>2026-09-11T13:48:57.151Z</updated>
  </entry>
  <entry>
    <author>
      <name>Furina</name>
    </author>
    <category term="python" scheme="https://www.furinaluna.top/categories/python/"/>
    <category term="Python" scheme="https://www.furinaluna.top/tags/Python/"/>
    <content>
      <![CDATA[<h1>FastAPI框架</h1><style>.wiopbmiodoiu{}</style><p><code>fastapi</code>，一个用于构建 API 的现代、快速（高性能）的web框架。</p><p><code>fastapi</code>是建立在Starlette和Pydantic基础上的，Pydantic是一个基于Python类型提示来定义数据验证、序列化和文档的库。Starlette是一种轻量级的ASGI框架/工具包，是构建高性能Asyncio服务的理性选择。</p><blockquote><ul><li>快速：可与 NodeJS 和 Go 比肩的<code>极高性能</code>（归功于 <code>Starlette</code> 和 <code>Pydantic</code>），是最快的 Python web 框架之一。</li><li>高效编码：提高功能开发速度约 200％ 至 300％。</li><li>更少bug：减少约 40％ 的人为（开发者）导致错误。</li><li>智能：极佳的编辑器支持。处处皆可自动补全，减少调试时间。</li><li>简单：设计的<code>易于使用和学习</code>，阅读文档的时间更短。</li><li>简短：使代码重复最小化。通过不同的参数声明实现丰富功能。</li><li>健壮：生产可用级别的代码。还有<code>自动生成的交互式文档</code>。</li></ul></blockquote><p>依赖：Python 3.6 及更高版本，FastAPI 站在以下巨人的肩膀之上</p><style>.jhmmowweibih{}</style><img src="/posts/fastapi-framework/starlette.webp" class="jhmmowweibih" alt="小明星"><blockquote><p><a href="https://www.starlette.io/">Starlette</a> 负责 web 部分(Asyncio)</p><p><a href="https://pydantic-docs.helpmanual.io/">Pydantic</a> 负责数据部分(类型提示)</p></blockquote><p>FastApi是站在前人肩膀上，集成了多种框架的优点的新秀框架。它出现的比较晚，2018年底才发布在github上。广泛应用于当前各种前后端分离的项目开发，测试运维自动化以及微服务的场景中。</p><h2 id="一、预备知识点">一、预备知识点</h2><h3 id="1-1、http协议">1.1、http协议</h3><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs text">1. 什么是请求头请求体，响应头响应体<br>2. URL地址包括什么<br>3. get请求和post请求到底是什么<br>4. Content-Type是什么<br></code></pre></td></tr></table></figure><h4 id="一、简介">一、简介</h4><p>HTTP协议是Hyper Text Transfer Protocol（超文本传输协议）的缩写,是用于万维网（WWW:World Wide Web ）服务器与本地浏览器之间传输超文本的传送协议。HTTP是一个属于应用层的面向对象的协议，由于其简捷、快速的方式，适用于分布式超媒体信息系统。它于1990年提出，经过几年的使用与发展，得到不断地完善和扩展。HTTP协议工作于客户端-服务端架构为上。浏览器作为HTTP客户端通过URL向HTTP服务端即WEB服务器发送所有请求。Web服务器根据接收到的请求后，向客户端发送响应信息。</p><style>.zjcmvsmmexzn{}</style><h4 id="二、-http协议特性">二、 http协议特性</h4> <figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><code class="hljs text"> (1) 基于TCP/IP协议<br><br>http协议是基于TCP/IP协议之上的应用层协议。<br><br>(2) 基于请求－响应模式<br><br>HTTP协议规定,请求从客户端发出,最后服务器端响应该请求并 返回。换句话说,肯定是先从客户端开始建立通信的,服务器端在没有 接收到请求之前不会发送响应<br><br> (3) 无状态保存<br><br>HTTP是一种不保存状态,即无状态(stateless)协议。HTTP协议 自身不对请求和响应之间的通信状态进行保存。也就是说在HTTP这个 级别,协议对于发送过的请求或响应都不做持久化处理。<br><br>使用HTTP协议,每当有新的请求发送时,就会有对应的新响应产 生。协议本身并不保留之前一切的请求或响应报文的信息。这是为了更快地处理大量事务,确保协议的可伸缩性,而特意把HTTP协议设计成 如此简单的。<br><br> (4) 短连接<br><br>HTTP1.0默认使用的是短连接。浏览器和服务器每进行一次HTTP操作，就建立一次连接，任务结束就中断连接。<br>HTTP/1.1起，默认使用长连接。要使用长连接，客户端和服务器的HTTP首部的Connection都要设置为keep-alive，才能支持长连接。<br>HTTP长连接，指的是复用TCP连接。多个HTTP请求可以复用同一个TCP连接，这就节省了TCP连接建立和断开的消耗。<br></code></pre></td></tr></table></figure><h4 id="三、http请求协议与响应协议">三、http请求协议与响应协议</h4><style>.nflguukuijbo{}</style><img src="/posts/fastapi-framework/image-20220901214542004.png" class="nflguukuijbo" alt="image-20220901214542004"><p>http协议包含由浏览器发送数据到服务器需要遵循的请求协议与服务器发送数据到浏览器需要遵循的请求协议。用于HTTP协议交互的信被为HTTP报文。请求端(客户端)的HTTP报文 做请求报文,响应端(服务器端)的 做响应报文。HTTP报文本身是由多行数据构成的字文本。</p><style>.svskfnstewll{}</style><img src="/posts/fastapi-framework/http%E5%8D%8F%E8%AE%AE.png" class="svskfnstewll" alt="http协议"><blockquote><p>一个完整的URL包括：协议、ip、端口、路径、参数</p><p>例如： <a href="https://www.baidu.com/s?wd=yuan">https://www.baidu.com/s?wd=yuan</a>     其中https是协议，<a href="http://www.baidu.com">www.baidu.com</a> 是IP，端口默认80，/s是路径，参数是wd=yuan</p><p>请求方式: get与post请求</p><ul><li>GET提交的数据会放在URL之后，以?分割URL和传输数据，参数之间以&amp;相连，如EditBook?name=test1&amp;id=123456. POST方法是把提交的数据放在HTTP包的请求体中.</li><li>GET提交的数据大小有限制（因为浏览器对URL的长度有限制），而POST方法提交的数据没有限制</li></ul><p>响应状态码：状态码的职 是当客户端向服务器端发送请求时, 返回的请求 结果。借助状态码,用户可以知道服务器端是正常 理了请求,还是出 现了 。状态码如200 OK,以3位数字和原因 组成。</p></blockquote><h3 id="1-2、api接口">1.2、api接口</h3><p>在开发Web应用中，有两种应用模式：</p><ol><li>前后端不分离[客户端看到的内容和所有界面效果都是由服务端提供出来的。]</li></ol><style>.qlzysxbtbjfj{}</style><img src="/posts/fastapi-framework/depended_frontend_backend.png" class="qlzysxbtbjfj" alt="前后端不分离"><ol start="2"><li>前后端分离【把前端的界面效果(html，css，js分离到另一个服务端，python服务端只需要返回数据即可)】</li></ol><p>前端形成一个独立的网站，服务端构成一个独立的网站</p><style>.coftyqtgjkeo{}</style><img src="/posts/fastapi-framework/indepent_frontend_backend-16318493319127.png" class="coftyqtgjkeo" alt="前后端分离"><p>应用程序编程接口（Application Programming Interface，API接口），就是应用程序对外提供了一个操作数据的入口，这个入口可以是一个函数或类方法，也可以是一个url地址或者一个网络地址。当客户端调用这个入口，应用程序则会执行对应代码操作，给客户端完成相对应的功能。</p><p>当然，api接口在工作中是比较常见的开发内容，有时候，我们会调用其他人编写的api接口，有时候，我们也需要提供api接口给其他人操作。由此就会带来一个问题，api接口往往都是一个函数、类方法、或者url或其他网络地址，不断是哪一种，当api接口编写过程中，我们都要考虑一个问题就是这个接口应该怎么编写？接口怎么写的更加容易维护和清晰，这就需要大家在调用或者编写api接口的时候要有一个明确的编写规范！！！</p><p>为了在团队内部形成共识、防止个人习惯差异引起的混乱，我们都需要找到一种大家都觉得很好的接口实现规范，而且这种规范能够让后端写的接口，用途一目了然，减少客户端和服务端双方之间的合作成本。</p><p>目前市面上大部分公司开发人员使用的接口实现规范主要有：restful、RPC。</p><p>REST全称是Representational State Transfer，中文意思是表述（编者注：通常译为表征）性状态转移。 它首次出现在2000年Roy Fielding的博士论文中。</p><p>RESTful是一种专门为Web 开发而定义API接口的设计风格，尤其适用于前后端分离的应用模式中。</p><p>关键：面向资源开发</p><p>这种风格的理念认为后端开发任务就是提供数据的，对外提供的是数据资源的访问接口，所以在定义接口时，客户端访问的URL路径就表示这种要操作的数据资源。</p><p>而对于数据资源分别使用POST、DELETE、GET、UPDATE等请求动作来表达对数据的增删查改。</p><table><thead><tr><th style="text-align:left">请求方法</th><th style="text-align:left">请求地址</th><th style="text-align:left">后端操作</th></tr></thead><tbody><tr><td style="text-align:left">POST</td><td style="text-align:left">/student/</td><td style="text-align:left">增加学生</td></tr><tr><td style="text-align:left">GET</td><td style="text-align:left">/student/</td><td style="text-align:left">获取所有学生</td></tr><tr><td style="text-align:left">GET</td><td style="text-align:left">/student/1</td><td style="text-align:left">获取id为1的学生</td></tr><tr><td style="text-align:left">PUT</td><td style="text-align:left">/student/1</td><td style="text-align:left">修改id为1的学生</td></tr><tr><td style="text-align:left">DELETE</td><td style="text-align:left">/student/1</td><td style="text-align:left">删除id为1的学生</td></tr></tbody></table><p>restful规范是一种通用的规范，不限制语言和开发框架的使用。事实上，我们可以使用任何一门语言，任何一个框架都可以实现符合restful规范的API接口。</p><h2 id="二、quick-start">二、quick start</h2><h3 id="简单案例">简单案例</h3><p><strong>安装</strong></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs python">pip install fastapi<br></code></pre></td></tr></table></figure><p>你还会需要一个 ASGI 服务器，生产环境可以使用 <a href="https://www.uvicorn.org/">Uvicorn</a></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs python">pip install uvicorn<br></code></pre></td></tr></table></figure><p><strong>代码</strong></p> <figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI  <span class="hljs-comment"># FastAPI 是一个为你的 API 提供了所有功能的 Python 类。</span><br><br>app = FastAPI()  <span class="hljs-comment"># 这个实例将是创建你所有 API 的主要交互对象。这个 app 同样在如下命令中被 uvicorn 所引用</span><br><br><span class="hljs-meta">@app.get(<span class="hljs-params"><span class="hljs-string">&quot;/&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">root</span>():<br>    <span class="hljs-keyword">return</span> &#123;<span class="hljs-string">&quot;message&quot;</span>: <span class="hljs-string">&quot;Hello yuan&quot;</span>&#125;<br></code></pre></td></tr></table></figure><p>通过以下命令运行服务器：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs python">uvicorn main:app --reload<br></code></pre></td></tr></table></figure><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs python">INFO:     Uvicorn running on http://<span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">8000</span> (Press CTRL+C to quit)<br>INFO:     Started reloader process [<span class="hljs-number">73408</span>]<br>INFO:     Started server process [<span class="hljs-number">73408</span>]<br>INFO:     Waiting <span class="hljs-keyword">for</span> application startup<br>INFO:     Application startup complete<br></code></pre></td></tr></table></figure><p>也可以直接运行：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">&#x27;__main__&#x27;</span>:<br>    <span class="hljs-keyword">import</span> uvicorn<br><br>    uvicorn.run(<span class="hljs-string">&quot;main:app&quot;</span>, host=<span class="hljs-string">&quot;127.0.0.1&quot;</span>, port=<span class="hljs-number">8080</span>, debug=<span class="hljs-literal">True</span>, reload=<span class="hljs-literal">True</span>)<br><br></code></pre></td></tr></table></figure><blockquote><p>（1）导入 FastAPI。<br>（2）创建一个 app 实例。<br>（3）编写一个路径操作装饰器（如 @app.get(“/”)）。<br>（4）编写一个路径操作函数（如上面的 def root(): …）<br>（5）定义返回值<br>（6）运行开发服务器（如 uvicorn main:app --reload）</p></blockquote><p>此外，fastapi有着<strong>非常棒的交互式 API 文档</strong>，这一点很吸引人。</p><p>跳转到 <a href="https://link.zhihu.com/?target=http%3A//127.0.0.1%3A8000/docs">http://127.0.0.1:8000/docs</a>。你将会看到<strong>自动生成</strong>的交互式 API 文档。</p><style>.hqcsofsdsdbn{}</style><img src="/posts/fastapi-framework/image-20220812090755216-0266479.png" class="hqcsofsdsdbn" alt="image-20220812090755216"><h2 id="三、路径操作">三、路径操作</h2><h3 id="路径操作装饰器">路径操作装饰器</h3><p>fastapi支持各种请求方式：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-meta">@app.get()</span><br><span class="hljs-meta">@app.post()</span><br><span class="hljs-meta">@app.put()</span><br><span class="hljs-meta">@app.patch()</span><br><span class="hljs-meta">@app.delete()</span><br><span class="hljs-meta">@app.options()</span><br><span class="hljs-meta">@app.head()</span><br><span class="hljs-meta">@app.trace()</span><br></code></pre></td></tr></table></figure><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> <span class="hljs-type">Union</span><br><br><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI<br><br>app = FastAPI()<br><br><br><span class="hljs-meta">@app.get(<span class="hljs-params"><span class="hljs-string">&quot;/get&quot;</span></span>)</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">get_test</span>():<br>    <span class="hljs-keyword">return</span> &#123;<span class="hljs-string">&quot;method&quot;</span>: <span class="hljs-string">&quot;get方法&quot;</span>&#125;<br><br><br><span class="hljs-meta">@app.post(<span class="hljs-params"><span class="hljs-string">&quot;/post&quot;</span></span>)</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">post_test</span>():<br>    <span class="hljs-keyword">return</span> &#123;<span class="hljs-string">&quot;method&quot;</span>: <span class="hljs-string">&quot;post方法&quot;</span>&#125;<br><br><br><span class="hljs-meta">@app.put(<span class="hljs-params"><span class="hljs-string">&quot;/put&quot;</span></span>)</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">put_test</span>():<br>    <span class="hljs-keyword">return</span> &#123;<span class="hljs-string">&quot;method&quot;</span>: <span class="hljs-string">&quot;put方法&quot;</span>&#125;<br><br><br><span class="hljs-meta">@app.delete(<span class="hljs-params"><span class="hljs-string">&quot;/delete&quot;</span></span>)</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">delete_test</span>():<br>    <span class="hljs-keyword">return</span> &#123;<span class="hljs-string">&quot;method&quot;</span>: <span class="hljs-string">&quot;delete方法&quot;</span>&#125;<br><br></code></pre></td></tr></table></figure><style>.ztxegdldsgkb{}</style><img src="/posts/fastapi-framework/image-20220818163654321.png" class="ztxegdldsgkb" alt="image-20220818163654321"><p>路径操作装饰器参数：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-meta">@app.post(<span class="hljs-params"></span></span><br><span class="hljs-params"><span class="hljs-meta">    <span class="hljs-string">&quot;/items/&#123;item_id&#125;&quot;</span>,</span></span><br><span class="hljs-params"><span class="hljs-meta">    response_model=Item,</span></span><br><span class="hljs-params"><span class="hljs-meta">    status_code=status.HTTP_200_OK,</span></span><br><span class="hljs-params"><span class="hljs-meta">    tags=[<span class="hljs-string">&quot;AAA&quot;</span>],</span></span><br><span class="hljs-params"><span class="hljs-meta">    summary=<span class="hljs-string">&quot;this is summary&quot;</span>,</span></span><br><span class="hljs-params"><span class="hljs-meta">    description=<span class="hljs-string">&quot;this is description&quot;</span>,</span></span><br><span class="hljs-params"><span class="hljs-meta">    response_description= <span class="hljs-string">&quot;this is response_description&quot;</span>,</span></span><br><span class="hljs-params"><span class="hljs-meta">    deprecated=<span class="hljs-literal">False</span>,</span></span><br><span class="hljs-params"><span class="hljs-meta"></span>)</span><br></code></pre></td></tr></table></figure><h3 id="include-router">include_router</h3><p><a href="http://main.py">main.py</a></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> <span class="hljs-type">Union</span><br><br><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI<br><span class="hljs-keyword">import</span> uvicorn<br><br><span class="hljs-keyword">from</span> apps <span class="hljs-keyword">import</span> app01, app02<br><br>app = FastAPI()<br><br>app.include_router(app01, prefix=<span class="hljs-string">&quot;/app01&quot;</span>, tags=[<span class="hljs-string">&quot;第一章节：商城接口&quot;</span>, ])<br>app.include_router(app02, prefix=<span class="hljs-string">&quot;/app02&quot;</span>, tags=[<span class="hljs-string">&quot;第二章节：用户中心接口&quot;</span>, ])<br><br><span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">&#x27;__main__&#x27;</span>:<br>    uvicorn.run(<span class="hljs-string">&quot;main:app&quot;</span>, host=<span class="hljs-string">&quot;127.0.0.1&quot;</span>, port=<span class="hljs-number">8080</span>, debug=<span class="hljs-literal">True</span>, reload=<span class="hljs-literal">True</span>)<br><br></code></pre></td></tr></table></figure><p>与main.py同级目录apps：</p><style>.fhurwbkpeovg{}</style><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-comment"># __init__.py</span><br><span class="hljs-keyword">from</span> .app01 <span class="hljs-keyword">import</span> app01<br><span class="hljs-keyword">from</span> .app02 <span class="hljs-keyword">import</span> app02<br></code></pre></td></tr></table></figure><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> APIRouter<br><br>app01 = APIRouter()<br><br><br><span class="hljs-meta">@app01.get(<span class="hljs-params"><span class="hljs-string">&quot;/shop/food&quot;</span></span>)</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">shop_food</span>():<br>    <span class="hljs-keyword">return</span> &#123;<span class="hljs-string">&quot;shop&quot;</span>: <span class="hljs-string">&quot;food&quot;</span>&#125;<br><br><br><span class="hljs-meta">@app01.get(<span class="hljs-params"><span class="hljs-string">&quot;/shop/bed&quot;</span></span>)</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">shop_food</span>():<br>    <span class="hljs-keyword">return</span> &#123;<span class="hljs-string">&quot;shop&quot;</span>: <span class="hljs-string">&quot;bed&quot;</span>&#125;<br></code></pre></td></tr></table></figure><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> APIRouter<br><br>app02 = APIRouter()<br><br><br><span class="hljs-meta">@app02.post(<span class="hljs-params"><span class="hljs-string">&quot;/user/login&quot;</span></span>)</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">user_login</span>():<br>    <span class="hljs-keyword">return</span> &#123;<span class="hljs-string">&quot;user&quot;</span>: <span class="hljs-string">&quot;login&quot;</span>&#125;<br><br><br><span class="hljs-meta">@app02.post(<span class="hljs-params"><span class="hljs-string">&quot;/user/reg&quot;</span></span>)</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">user_reg</span>():<br>    <span class="hljs-keyword">return</span> &#123;<span class="hljs-string">&quot;user&quot;</span>: <span class="hljs-string">&quot;reg&quot;</span>&#125;<br><br></code></pre></td></tr></table></figure><style>.apzgpnbwwfox{}</style><img src="/posts/fastapi-framework/image-20220818121552383.png" class="apzgpnbwwfox" alt="image-20220818121552383"><h2 id="四、请求与响应">四、请求与响应</h2><h3 id="4-1、路径参数">4.1、路径参数</h3><h4 id="（1）基本用法">（1）基本用法</h4><p>以使用与 Python 格式化字符串相同的语法来声明路径&quot;参数&quot;或&quot;变量&quot;：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-meta">@app.get(<span class="hljs-params"><span class="hljs-string">&quot;/user/&#123;user_id&#125;&quot;</span></span>)</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">get_user</span>(<span class="hljs-params">user_id</span>):<br>    <span class="hljs-built_in">print</span>(user_id, <span class="hljs-built_in">type</span>(user_id))<br>    <span class="hljs-keyword">return</span> &#123;<span class="hljs-string">&quot;user_id&quot;</span>: user_id&#125;<br></code></pre></td></tr></table></figure><p>路径参数 <code>user_id</code> 的值将作为参数 <code>user_id</code> 传递给你的函数。</p><h4 id="（2）有类型的路径参数">（2）有类型的路径参数</h4><p>你可以使用标准的 Python 类型标注为函数中的路径参数声明类型。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-meta">@app.get(<span class="hljs-params"><span class="hljs-string">&quot;/user/&#123;user_id&#125;&quot;</span></span>)</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">get_user</span>(<span class="hljs-params">user_id: <span class="hljs-built_in">int</span></span>):<br>    <span class="hljs-built_in">print</span>(user_id, <span class="hljs-built_in">type</span>(user_id))<br>    <span class="hljs-keyword">return</span> &#123;<span class="hljs-string">&quot;user_id&quot;</span>: user_id&#125;<br></code></pre></td></tr></table></figure><p>在这个例子中，<code>user_id</code> 被声明为 <code>int</code> 类型。</p><blockquote><p>这将为你的函数提供编辑器支持，包括错误检查、代码补全等等。</p></blockquote><h4 id="（3）注意顺序">（3）注意顺序</h4><p>在创建<em>路径操作</em>时，你会发现有些情况下路径是固定的。</p><p>比如 <code>/users/me</code>，我们假设它用来获取关于当前用户的数据.</p><p>然后，你还可以使用路径 <code>/user/&#123;username&#125;</code> 来通过用户名 获取关于特定用户的数据。</p><p>由于<em>路径操作</em>是按顺序依次运行的，你需要确保路径 <code>/user/me</code> 声明在路径 <code>/user/&#123;username&#125;</code>之前：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-meta">@app.get(<span class="hljs-params"><span class="hljs-string">&quot;/user/me&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">read_user_me</span>():<br>    <span class="hljs-keyword">return</span> &#123;<span class="hljs-string">&quot;username&quot;</span>: <span class="hljs-string">&quot;the current user&quot;</span>&#125;<br><br><span class="hljs-meta">@app.get(<span class="hljs-params"><span class="hljs-string">&quot;/user/&#123;username&#125;&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">read_user</span>(<span class="hljs-params">username: <span class="hljs-built_in">str</span></span>):<br>    <span class="hljs-keyword">return</span> &#123;<span class="hljs-string">&quot;username&quot;</span>: username&#125;<br></code></pre></td></tr></table></figure><p>否则，<code>/user/&#123;username&#125;</code> 的路径还将与 <code>/user/me</code> 相匹配，&quot;认为&quot;自己正在接收一个值为 <code>&quot;me&quot;</code> 的 <code>username</code> 参数。</p><h3 id="4-2、查询参数（请求参数）">4.2、查询参数（请求参数）</h3><p>路径函数中声明不属于路径参数的其他函数参数时，它们将被自动解释为&quot;查询字符串&quot;参数，就是 url? 之后用<code>&amp;</code>分割的 key-value 键值对。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-meta">@app.get(<span class="hljs-params"><span class="hljs-string">&quot;/jobs/&#123;kd&#125;&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">search_jobs</span>(<span class="hljs-params">kd: <span class="hljs-built_in">str</span>, city: <span class="hljs-type">Union</span>[<span class="hljs-built_in">str</span>, <span class="hljs-literal">None</span>] = <span class="hljs-literal">None</span>, xl: <span class="hljs-type">Union</span>[<span class="hljs-built_in">str</span>, <span class="hljs-literal">None</span>] = <span class="hljs-literal">None</span></span>):  <span class="hljs-comment"># 有默认值即可选，否则必选</span><br>    <span class="hljs-keyword">if</span> city <span class="hljs-keyword">or</span> xl:<br>        <span class="hljs-keyword">return</span> &#123;<span class="hljs-string">&quot;kd&quot;</span>: kd, <span class="hljs-string">&quot;city&quot;</span>: city, <span class="hljs-string">&quot;xl&quot;</span>: xl&#125;<br>    <span class="hljs-keyword">return</span> &#123;<span class="hljs-string">&quot;kd&quot;</span>: kd&#125;<br></code></pre></td></tr></table></figure><style>.vbtkdmsrbmiv{}</style><img src="/posts/fastapi-framework/image-20220929202111451.png" class="vbtkdmsrbmiv" alt="image-20220929202111451"><p>在这个例子中，函数参数 <code>city</code>和<code>xl</code> 是可选的，并且默认值为 <code>None</code>。</p><p>自python3.5开始，PEP484为python引入了类型注解(type hints)，typing的主要作用有：</p><blockquote><ol><li>类型检查，防止运行时出现参数、返回值类型不符。</li><li>作为开发文档附加说明，方便使用者调用时传入和返回参数类型。</li><li>模块加入不会影响程序的运行不会报正式的错误，pycharm支持typing检查错误时会出现黄色警告。</li></ol></blockquote><p><code>type hints</code>主要是要指示函数的输入和输出的数据类型，数据类型在typing 包中，基本类型有str list dict等等，</p><p>Union 是当有多种可能的数据类型时使用，比如函数有可能根据不同情况有时返回str或返回list，那么就可以写成Union[list, str]<br>Optional 是Union的一个简化， 当 数据类型中有可能是None时，比如有可能是str也有可能是None，则Optional[str], 相当于Union[str, None]</p><h3 id="4-3、请求体数据">4.3、请求体数据</h3><p>当你需要将数据从客户端（例如浏览器）发送给 API 时，你将其作为「请求体」发送。<strong>请求</strong>体是客户端发送给 API 的数据。<strong>响应</strong>体是 API 发送给客户端的数据。</p><p>FastAPI 基于 <code>Pydantic</code> ，<code>Pydantic</code> 主要用来做类型强制检查（校验数据）。不符合类型要求就会抛出异常。</p><p>对于 API 服务，支持类型检查非常有用，会让服务更加健壮，也会加快开发速度，因为开发者再也不用自己写一行一行的做类型检查。</p><p>安装上手<code>pip install pydantic</code></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> <span class="hljs-type">Union</span>, <span class="hljs-type">List</span>, <span class="hljs-type">Optional</span><br><br><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI<br><span class="hljs-keyword">from</span> pydantic <span class="hljs-keyword">import</span> BaseModel, Field, ValidationError, validator<br><span class="hljs-keyword">import</span> uvicorn<br><span class="hljs-keyword">from</span> datetime <span class="hljs-keyword">import</span> date<br><br><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">Addr</span>(<span class="hljs-title class_ inherited__">BaseModel</span>):<br>    province: <span class="hljs-built_in">str</span><br>    city: <span class="hljs-built_in">str</span><br><br><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">User</span>(<span class="hljs-title class_ inherited__">BaseModel</span>):<br>    name = <span class="hljs-string">&#x27;root&#x27;</span><br>    age: <span class="hljs-built_in">int</span> = Field(default=<span class="hljs-number">0</span>, lt=<span class="hljs-number">100</span>, gt=<span class="hljs-number">0</span>)<br>    birth: <span class="hljs-type">Optional</span>[date] = <span class="hljs-literal">None</span><br>    friends: <span class="hljs-type">List</span>[<span class="hljs-built_in">int</span>] = []<br>    description: <span class="hljs-type">Union</span>[<span class="hljs-built_in">str</span>, <span class="hljs-literal">None</span>] = <span class="hljs-literal">None</span><br><br>    <span class="hljs-comment"># addr: Union[Addr, None] = None  # 类型嵌套</span><br><br><span class="hljs-meta">    @validator(<span class="hljs-params"><span class="hljs-string">&#x27;name&#x27;</span></span>)</span><br>    <span class="hljs-keyword">def</span> <span class="hljs-title function_">name_must_alpha</span>(<span class="hljs-params">cls, v</span>):<br>        <span class="hljs-keyword">assert</span> v.isalpha(), <span class="hljs-string">&#x27;name must be alpha&#x27;</span><br>        <span class="hljs-keyword">return</span> v<br><br><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">Data</span>(<span class="hljs-title class_ inherited__">BaseModel</span>):  <span class="hljs-comment"># 类型嵌套</span><br>    users: <span class="hljs-type">List</span>[User]<br><br><br>app = FastAPI()<br><br><br><span class="hljs-meta">@app.post(<span class="hljs-params"><span class="hljs-string">&quot;/data/&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">create_data</span>(<span class="hljs-params">data: Data</span>):<br>    <span class="hljs-comment"># 添加数据库</span><br>    <span class="hljs-keyword">return</span> data<br><br><br><span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">&#x27;__main__&#x27;</span>:<br>    <span class="hljs-keyword">try</span>:<br>        User(name=<span class="hljs-string">&quot;&quot;</span>,...)<br>    <span class="hljs-keyword">except</span> ValidationError <span class="hljs-keyword">as</span> e:<br>        <span class="hljs-built_in">print</span>(e.json())  <br>    <br><br></code></pre></td></tr></table></figure><p>测试：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-punctuation">&#123;</span><br>  <span class="hljs-attr">&quot;name&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;rain&quot;</span><span class="hljs-punctuation">,</span><br>  <span class="hljs-attr">&quot;age&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-number">32</span><span class="hljs-punctuation">,</span><br>  <span class="hljs-attr">&quot;birth&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;2022-09-29&quot;</span><span class="hljs-punctuation">,</span><br>  <span class="hljs-attr">&quot;friends&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><span class="hljs-punctuation">]</span><span class="hljs-punctuation">,</span><br>  <span class="hljs-attr">&quot;description&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;最帅的讲fastapi的老师&quot;</span><br><span class="hljs-punctuation">&#125;</span><br></code></pre></td></tr></table></figure><p>和声明查询参数时一样，当一个模型属性具有默认值时，它不是必需的。否则它是一个必需属性。将默认值设为 <code>None</code> 可使其成为可选属性。</p><p>FastAPI 会自动将定义的模型类转化为<code>JSON Schema</code>，Schema 成为 OpenAPI 生成模式的一部分，并显示在 API 交互文档中，查看 API 交互文档如下，该接口将接收<code>application/json</code>类型的参数。</p><p>FastAPI 支持同时定义 Path 参数、Query 参数和请求体参数，FastAPI 将会正确识别并获取数据。</p><blockquote><p>参数在 url 中也声明了，它将被解释为 path 参数</p><p>参数是单一类型（例如int、float、str、bool等），它将被解释为 query 参数</p><p>参数类型为继承 Pydantic 模块的<code>BaseModel</code>类的数据模型类，则它将被解释为请求体参数</p></blockquote><h3 id="4-4、form表单数据">4.4、form表单数据</h3><p>在 OAuth2 规范的一种使用方式（密码流）中，需要将用户名、密码作为表单字段发送，而不是 JSON。</p><p>FastAPI 可以使用<strong>Form</strong>组件来接收表单数据，需要先使用<code>pip install python-multipart</code>命令进行安装。</p><blockquote><p>pip install python-multipart</p></blockquote><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI, Form<br><br>app = FastAPI()<br><br><span class="hljs-meta">@app.post(<span class="hljs-params"><span class="hljs-string">&quot;/regin&quot;</span></span>)</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">regin</span>(<span class="hljs-params">username: <span class="hljs-built_in">str</span> = Form(<span class="hljs-params">..., max_length=<span class="hljs-number">16</span>, min_length=<span class="hljs-number">8</span>, regex=<span class="hljs-string">&#x27;[a-zA-Z]&#x27;</span></span>),</span><br><span class="hljs-params">          password: <span class="hljs-built_in">str</span> = Form(<span class="hljs-params">..., max_length=<span class="hljs-number">16</span>, min_length=<span class="hljs-number">8</span>, regex=<span class="hljs-string">&#x27;[0-9]&#x27;</span></span>)</span>):<br>    <span class="hljs-built_in">print</span>(<span class="hljs-string">f&quot;username:<span class="hljs-subst">&#123;username&#125;</span>,password:<span class="hljs-subst">&#123;password&#125;</span>&quot;</span>)<br>    <span class="hljs-keyword">return</span> &#123;<span class="hljs-string">&quot;username&quot;</span>: username&#125;<br><br></code></pre></td></tr></table></figure><style>.ufpmlirgfuql{}</style><img src="/posts/fastapi-framework/image-20220929200207500-4452928.png" class="ufpmlirgfuql" alt="image-20220929200207500"><h3 id="4-5、文件上传">4.5、文件上传</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI, File, UploadFile<br><span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> <span class="hljs-type">List</span><br><br>app = FastAPI()<br><br><span class="hljs-comment"># file: bytes = File()：适合小文件上传</span><br><span class="hljs-meta">@app.post(<span class="hljs-params"><span class="hljs-string">&quot;/files/&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">create_file</span>(<span class="hljs-params">file: <span class="hljs-built_in">bytes</span> = File(<span class="hljs-params"></span>)</span>):<br>    <span class="hljs-built_in">print</span>(<span class="hljs-string">&quot;file:&quot;</span>, file)<br>    <span class="hljs-keyword">return</span> &#123;<span class="hljs-string">&quot;file_size&quot;</span>: <span class="hljs-built_in">len</span>(file)&#125;<br><br><br><span class="hljs-meta">@app.post(<span class="hljs-params"><span class="hljs-string">&quot;/multiFiles/&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">create_files</span>(<span class="hljs-params">files: <span class="hljs-type">List</span>[<span class="hljs-built_in">bytes</span>] = File(<span class="hljs-params"></span>)</span>):<br>    <span class="hljs-keyword">return</span> &#123;<span class="hljs-string">&quot;file_sizes&quot;</span>: [<span class="hljs-built_in">len</span>(file) <span class="hljs-keyword">for</span> file <span class="hljs-keyword">in</span> files]&#125;<br><br><br><span class="hljs-comment"># file: UploadFile：适合大文件上传</span><br><br><span class="hljs-meta">@app.post(<span class="hljs-params"><span class="hljs-string">&quot;/uploadFile/&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">create_upload_file</span>(<span class="hljs-params">file: UploadFile</span>):<br>    <span class="hljs-keyword">with</span> <span class="hljs-built_in">open</span>(<span class="hljs-string">f&quot;<span class="hljs-subst">&#123;file.filename&#125;</span>&quot;</span>, <span class="hljs-string">&#x27;wb&#x27;</span>) <span class="hljs-keyword">as</span> f:<br>        <span class="hljs-keyword">for</span> chunk <span class="hljs-keyword">in</span> <span class="hljs-built_in">iter</span>(<span class="hljs-keyword">lambda</span>: file.file.read(<span class="hljs-number">1024</span>), <span class="hljs-string">b&#x27;&#x27;</span>):<br>            f.write(chunk)<br><br>    <span class="hljs-keyword">return</span> &#123;<span class="hljs-string">&quot;filename&quot;</span>: file.filename&#125;<br><br><br><span class="hljs-meta">@app.post(<span class="hljs-params"><span class="hljs-string">&quot;/multiUploadFiles/&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">create_upload_files</span>(<span class="hljs-params">files: <span class="hljs-type">List</span>[UploadFile]</span>):<br>    <span class="hljs-keyword">return</span> &#123;<span class="hljs-string">&quot;filenames&quot;</span>: [file.filename <span class="hljs-keyword">for</span> file <span class="hljs-keyword">in</span> files]&#125;<br><br></code></pre></td></tr></table></figure><h3 id="4-6、Reqeust对象">4.6、Reqeust对象</h3><p>有些情况下我们希望能直接访问<em>Request</em>对象。例如我们在路径操作函数中想获取客户端的IP地址，需要在函数中声明<strong>Request</strong>类型的参数，FastAPI 就会自动传递 Request 对象给这个参数，我们就可以获取到 Request 对象及其属性信息，例如 header、url、cookie、session 等。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs python"><br><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> Request<br><br><br><span class="hljs-meta">@app.get(<span class="hljs-params"><span class="hljs-string">&quot;/items&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">items</span>(<span class="hljs-params">request: Request</span>):<br>    <span class="hljs-keyword">return</span> &#123;<br>        <span class="hljs-string">&quot;请求URL：&quot;</span>: request.url,<br>        <span class="hljs-string">&quot;请求ip：&quot;</span>: request.client.host,<br>        <span class="hljs-string">&quot;请求宿主：&quot;</span>: request.headers.get(<span class="hljs-string">&quot;user-agent&quot;</span>),<br>        <span class="hljs-string">&quot;cookies&quot;</span>: request.cookies,<br>    &#125;<br>    <br></code></pre></td></tr></table></figure><h3 id="4-7、请求静态文件">4.7、请求静态文件</h3><p>在 Web 开发中，需要请求很多静态资源文件（不是由服务器生成的文件），如 css/js 和图片文件等。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> fastapi.staticfiles <span class="hljs-keyword">import</span> StaticFiles<br><br>app = FastAPI()<br>app.mount(<span class="hljs-string">&quot;/static&quot;</span>,StaticFiles(directory=<span class="hljs-string">&quot;static&quot;</span>))<br><br></code></pre></td></tr></table></figure><h3 id="4-8、响应模型相关参数">4.8、响应模型相关参数</h3><h4 id="（1）response-model">（1）response_model</h4><p>前面写的这么多路径函数最终 return 的都是自定义结构的字典，FastAPI 提供了 response_model 参数，声明 return 响应体的模型</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs javascript"># 路径操作<br>@app.<span class="hljs-title function_">post</span>(<span class="hljs-string">&quot;/items/&quot;</span>, response_model=<span class="hljs-title class_">Item</span>)<br># 路径函数<br><span class="hljs-keyword">async</span> def <span class="hljs-title function_">create_item</span>(<span class="hljs-attr">item</span>: <span class="hljs-title class_">Item</span>):<br>    ...<br></code></pre></td></tr></table></figure><blockquote><p>response_model 是路径操作的参数，并不是路径函数的参数哦</p></blockquote><p>FastAPI将使用<code>response_model</code>进行以下操作：</p><ul><li>将输出数据转换为response_model中声明的数据类型。</li><li>验证数据结构和类型</li><li>将输出数据限制为该model定义的</li><li>添加到OpenAPI中</li><li>在自动文档系统中使用。</li></ul><p>你可以在任意的路径操作中使用 <code>response_model</code> 参数来声明用于响应的模型</p><p>案例：</p><ul><li>注册功能</li><li>输入账号、密码、昵称、邮箱，注册成功后返回个人信息</li></ul><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> <span class="hljs-type">Union</span><br><br><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI<br><span class="hljs-keyword">from</span> pydantic <span class="hljs-keyword">import</span> BaseModel, EmailStr<br><br>app = FastAPI()<br><br><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">UserIn</span>(<span class="hljs-title class_ inherited__">BaseModel</span>):<br>    username: <span class="hljs-built_in">str</span><br>    password: <span class="hljs-built_in">str</span><br>    email: EmailStr<br>    full_name: <span class="hljs-type">Union</span>[<span class="hljs-built_in">str</span>, <span class="hljs-literal">None</span>] = <span class="hljs-literal">None</span><br><br><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">UserOut</span>(<span class="hljs-title class_ inherited__">BaseModel</span>):<br>    username: <span class="hljs-built_in">str</span><br>    email: EmailStr<br>    full_name: <span class="hljs-type">Union</span>[<span class="hljs-built_in">str</span>, <span class="hljs-literal">None</span>] = <span class="hljs-literal">None</span><br><br><br><span class="hljs-meta">@app.post(<span class="hljs-params"><span class="hljs-string">&quot;/user/&quot;</span>, response_model=UserOut</span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">create_user</span>(<span class="hljs-params">user: UserIn</span>):<br>    <span class="hljs-keyword">return</span> user<br><br></code></pre></td></tr></table></figure><style>.lltwuspekjkh{}</style><img src="/posts/fastapi-framework/image-20220816105323504.png" class="lltwuspekjkh" alt="image-20220816105323504"><h4 id="（2）response-model-exclude-unset">（2）response_model_exclude_unset</h4><p>通过上面的例子，我们学到了如何用response_model控制响应体结构，但是如果它们实际上没有存储，则可能要从结果中忽略它们。例如，如果model在NoSQL数据库中具有很多可选属性，但是不想发送很长的JSON响应，其中包含默认值。</p><p>案例：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> <span class="hljs-type">List</span>, <span class="hljs-type">Union</span><br><br><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI<br><span class="hljs-keyword">from</span> pydantic <span class="hljs-keyword">import</span> BaseModel<br><br>app = FastAPI()<br><br><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">Item</span>(<span class="hljs-title class_ inherited__">BaseModel</span>):<br>    name: <span class="hljs-built_in">str</span><br>    description: <span class="hljs-type">Union</span>[<span class="hljs-built_in">str</span>, <span class="hljs-literal">None</span>] = <span class="hljs-literal">None</span><br>    price: <span class="hljs-built_in">float</span><br>    tax: <span class="hljs-built_in">float</span> = <span class="hljs-number">10.5</span><br>    tags: <span class="hljs-type">List</span>[<span class="hljs-built_in">str</span>] = []<br><br><br>items = &#123;<br>    <span class="hljs-string">&quot;foo&quot;</span>: &#123;<span class="hljs-string">&quot;name&quot;</span>: <span class="hljs-string">&quot;Foo&quot;</span>, <span class="hljs-string">&quot;price&quot;</span>: <span class="hljs-number">50.2</span>&#125;,<br>    <span class="hljs-string">&quot;bar&quot;</span>: &#123;<span class="hljs-string">&quot;name&quot;</span>: <span class="hljs-string">&quot;Bar&quot;</span>, <span class="hljs-string">&quot;description&quot;</span>: <span class="hljs-string">&quot;The bartenders&quot;</span>, <span class="hljs-string">&quot;price&quot;</span>: <span class="hljs-number">62</span>, <span class="hljs-string">&quot;tax&quot;</span>: <span class="hljs-number">20.2</span>&#125;,<br>    <span class="hljs-string">&quot;baz&quot;</span>: &#123;<span class="hljs-string">&quot;name&quot;</span>: <span class="hljs-string">&quot;Baz&quot;</span>, <span class="hljs-string">&quot;description&quot;</span>: <span class="hljs-literal">None</span>, <span class="hljs-string">&quot;price&quot;</span>: <span class="hljs-number">50.2</span>, <span class="hljs-string">&quot;tax&quot;</span>: <span class="hljs-number">10.5</span>, <span class="hljs-string">&quot;tags&quot;</span>: []&#125;,<br>&#125;<br><br><br><span class="hljs-meta">@app.get(<span class="hljs-params"><span class="hljs-string">&quot;/items/&#123;item_id&#125;&quot;</span>, response_model=Item, response_model_exclude_unset=<span class="hljs-literal">True</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">read_item</span>(<span class="hljs-params">item_id: <span class="hljs-built_in">str</span></span>):<br>    <span class="hljs-keyword">return</span> items[item_id]<br><br></code></pre></td></tr></table></figure><p>请求：<a href="http://127.0.0.1:8080/items/foo">http://127.0.0.1:8080/items/foo</a></p><p>不设置unset参数：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-punctuation">&#123;</span><br>  <span class="hljs-attr">&quot;name&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;Foo&quot;</span><span class="hljs-punctuation">,</span><br>  <span class="hljs-attr">&quot;description&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">null</span></span><span class="hljs-punctuation">,</span><br>  <span class="hljs-attr">&quot;price&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-number">50.2</span><span class="hljs-punctuation">,</span><br>  <span class="hljs-attr">&quot;tax&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-number">10.5</span><span class="hljs-punctuation">,</span><br>  <span class="hljs-attr">&quot;tags&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><span class="hljs-punctuation">]</span><br><span class="hljs-punctuation">&#125;</span><br></code></pre></td></tr></table></figure><p>设置unset参数：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-punctuation">&#123;</span><br>  <span class="hljs-attr">&quot;name&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;Foo&quot;</span><span class="hljs-punctuation">,</span><br>  <span class="hljs-attr">&quot;price&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-number">50.2</span><br><span class="hljs-punctuation">&#125;</span><br></code></pre></td></tr></table></figure><p>使用路径操作装饰器的 <code>response_model</code> 参数来定义响应模型，特别是确保私有数据被过滤掉。使用 <code>response_model_exclude_unset</code> 来仅返回显式设定的值。<br>除了<code>response_model_exclude_unset</code>以外，还有<code>response_model_exclude_defaults</code>和<code>response_model_exclude_none</code>，我们可以很直观的了解到他们的意思，不返回是默认值的字段和不返回是None的字段。</p><h4 id="（3）INCLUDE和EXCLUDE">（3）INCLUDE和EXCLUDE</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-comment"># response_model_exclude</span><br><span class="hljs-meta">@app.get(<span class="hljs-params"><span class="hljs-string">&quot;/items/&#123;item_id&#125;&quot;</span>, response_model=Item, response_model_exclude=&#123;<span class="hljs-string">&quot;description&quot;</span>&#125;, </span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">read_item</span>(<span class="hljs-params">item_id: <span class="hljs-built_in">str</span></span>):<br>    <span class="hljs-keyword">return</span> items[item_id]<br><br><span class="hljs-comment"># response_model_include  </span><br><span class="hljs-meta">@app.get(<span class="hljs-params"><span class="hljs-string">&quot;/items/&#123;item_id&#125;&quot;</span>, response_model=Item, response_model_include=&#123;<span class="hljs-string">&quot;name&quot;</span>, <span class="hljs-string">&quot;price&quot;</span>&#125;, </span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">read_item</span>(<span class="hljs-params">item_id: <span class="hljs-built_in">str</span></span>):<br>    <span class="hljs-keyword">return</span> items[item_id]<br></code></pre></td></tr></table></figure><h3 id="五、api接口与restful规范">五、api接口与restful规范</h3><h4 id="5-1api接口">5.1api接口</h4><p>应用程序编程接口（Application Programming Interface，API接口），就是应用程序对外提供了一个操作数据的入口，这个入口可以是一个函数或类方法，也可以是一个url地址或者一个网络地址。当客户端调用这个入口，应用程序则会执行对应代码操作，给客户端完成相对应的功能。</p><style>.wpkrfrfnalyl{}</style><img src="/posts/fastapi-framework/image-20221013160754152-5648475.png" class="wpkrfrfnalyl" alt="image-20221013160754152"><p>当然，api接口在工作中是比较常见的开发内容，有时候，我们会调用其他人编写的api接口，有时候，我们也需要提供api接口给其他人操作。由此就会带来一个问题，api接口往往都是一个函数、类方法、或者url或其他网络地址，不断是哪一种，当api接口编写过程中，我们都要考虑一个问题就是这个接口应该怎么编写？接口怎么写的更加容易维护和清晰，这就需要大家在调用或者编写api接口的时候要有一个明确的编写规范！！！</p><h4 id="5-2-restful规范">5.2 restful规范</h4><p>为了在团队内部形成共识、防止个人习惯差异引起的混乱，我们都需要找到一种大家都觉得很好的接口实现规范，而且这种规范能够让后端写的接口，用途一目了然，减少客户端和服务端双方之间的合作成本。</p><p>目前市面上大部分公司开发人员使用的接口实现规范主要有：restful、RPC。</p><p>REST与技术无关，代表的是一种软件架构风格，REST是Representational State Transfer的简称，中文翻译为“表征状态转移”或“表现层状态转化”。</p><p>简单来说，REST的含义就是客户端与Web服务器之间进行交互的时候，使用HTTP协议中的4个请求方法代表不同的动作。</p><blockquote><p>GET用来获取资源</p><p>POST用来新建资源</p><p>PUT用来更新资源</p><p>DELETE用来删除资源。</p></blockquote><p>只要API程序遵循了REST风格，那就可以称其为RESTful API。目前在前后端分离的架构中，前后端基本都是通过RESTful API来进行交互。</p><p>例如，我们现在要编写一个选课系统的接口，我们可以查询对一个学生进行查询、创建、更新和删除等操作，我们在编写程序的时候就要设计客户端浏览器与我们Web服务端交互的方式和路径。</p><p>而对于数据资源分别使用POST、DELETE、GET、UPDATE等请求动作来表达对数据的增删查改。</p><table><thead><tr><th style="text-align:left">请求方法</th><th style="text-align:left">请求地址</th><th style="text-align:left">后端操作</th></tr></thead><tbody><tr><td style="text-align:left">GET</td><td style="text-align:left">/students</td><td style="text-align:left">获取所有学生</td></tr><tr><td style="text-align:left">POST</td><td style="text-align:left">/students</td><td style="text-align:left">增加学生</td></tr><tr><td style="text-align:left">GET</td><td style="text-align:left">/students/1</td><td style="text-align:left">获取编号为1的学生</td></tr><tr><td style="text-align:left">PUT</td><td style="text-align:left">/students/1</td><td style="text-align:left">修改编号为1的学生</td></tr><tr><td style="text-align:left">DELETE</td><td style="text-align:left">/students/1</td><td style="text-align:left">删除编号为1的学生</td></tr></tbody></table><h4 id="5-3、选课系统接口开发">5.3、选课系统接口开发</h4><p><code>api/student.py</code></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> fastapi.exceptions <span class="hljs-keyword">import</span> HTTPException<br><br><span class="hljs-keyword">from</span> models <span class="hljs-keyword">import</span> *<br><br><span class="hljs-keyword">from</span> pydantic <span class="hljs-keyword">import</span> BaseModel<br><span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> <span class="hljs-type">List</span>, <span class="hljs-type">Union</span><br><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> APIRouter<br><br>api_student = APIRouter()<br><br><br><span class="hljs-meta">@api_student.get(<span class="hljs-params"><span class="hljs-string">&quot;/student&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">getAllStudent</span>():<br>    students = <span class="hljs-keyword">await</span> Student.<span class="hljs-built_in">all</span>().values(<span class="hljs-string">&quot;name&quot;</span>, <span class="hljs-string">&quot;clas__name&quot;</span>)<br>    <span class="hljs-comment"># students = await Student.filter(name__icontains=&#x27;a&#x27;).values(&quot;name&quot;, &quot;clas__name&quot;)</span><br>    <span class="hljs-comment"># print(&quot;students&quot;, students)</span><br>    <span class="hljs-comment"># for i in students:</span><br>    <span class="hljs-comment">#     print(i)</span><br>    <span class="hljs-comment">#</span><br>    <span class="hljs-comment"># rain = await Student.get(name=&#x27;rain&#x27;)</span><br>    <span class="hljs-comment"># print(rain, type(rain))</span><br>    <span class="hljs-comment"># print(rain.sno)</span><br><br>    <span class="hljs-keyword">return</span> students<br><br><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">StudentModel</span>(<span class="hljs-title class_ inherited__">BaseModel</span>):<br>    name: <span class="hljs-built_in">str</span><br>    pwd: <span class="hljs-built_in">str</span><br>    sno: <span class="hljs-built_in">int</span><br>    clas_id: <span class="hljs-type">Union</span>[<span class="hljs-built_in">int</span>, <span class="hljs-literal">None</span>] = <span class="hljs-literal">None</span><br>    courses: <span class="hljs-type">List</span>[<span class="hljs-built_in">int</span>] = []<br><br><br><span class="hljs-meta">@api_student.post(<span class="hljs-params"><span class="hljs-string">&quot;/student&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">addStudent</span>(<span class="hljs-params">stu: StudentModel</span>):<br>    <span class="hljs-comment"># 添加数据库操作</span><br>    <span class="hljs-comment"># 方式1</span><br>    <span class="hljs-comment"># student = Student(name=stu.name, pwd=stu.pwd, sno=stu.sno, clas_id=stu.clas)</span><br>    <span class="hljs-comment"># await student.save()</span><br>    <span class="hljs-comment"># 方式2</span><br>    student = <span class="hljs-keyword">await</span> Student.create(name=stu.name, pwd=stu.pwd, sno=stu.sno, clas_id=stu.clas_id)<br>    <span class="hljs-built_in">print</span>(student, <span class="hljs-built_in">dir</span>(student))<br><br>    <span class="hljs-comment"># 添加多对多关系记录</span><br>    courses = <span class="hljs-keyword">await</span> Course.<span class="hljs-built_in">filter</span>(id__in=stu.courses)<br>    <span class="hljs-built_in">print</span>(<span class="hljs-string">&quot;courses&quot;</span>, courses)<br>    <span class="hljs-keyword">await</span> student.courses.add(*courses)<br>    <span class="hljs-built_in">print</span>(<span class="hljs-string">&quot;student&quot;</span>, student.courses)<br><br>    <span class="hljs-keyword">return</span> student<br><br><br><span class="hljs-meta">@api_student.put(<span class="hljs-params"><span class="hljs-string">&quot;/student/&#123;student_id&#125;&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">update_student</span>(<span class="hljs-params">student_id: <span class="hljs-built_in">int</span>, student: StudentModel</span>):<br>    data = student.<span class="hljs-built_in">dict</span>(exclude_unset=<span class="hljs-literal">True</span>)<br>    courses = data.pop(<span class="hljs-string">&quot;courses&quot;</span>)<br>    <span class="hljs-built_in">print</span>(data, courses)<br>    <span class="hljs-keyword">await</span> Student.<span class="hljs-built_in">filter</span>(<span class="hljs-built_in">id</span>=student_id).update(**data)<br><br>    courses = <span class="hljs-keyword">await</span> Course.<span class="hljs-built_in">filter</span>(id__in=student.courses)<br>    edit_student = <span class="hljs-keyword">await</span> Student.get(<span class="hljs-built_in">id</span>=student_id)<br>    <span class="hljs-keyword">await</span> edit_student.courses.clear()<br>    <span class="hljs-keyword">await</span> edit_student.courses.add(*courses)<br><br>    <span class="hljs-keyword">return</span> student<br><br><br><span class="hljs-meta">@api_student.delete(<span class="hljs-params"><span class="hljs-string">&quot;/student/&#123;student_id&#125;&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">delete_student</span>(<span class="hljs-params">student_id: <span class="hljs-built_in">int</span></span>):<br>    deleted_count = <span class="hljs-keyword">await</span> Student.<span class="hljs-built_in">filter</span>(<span class="hljs-built_in">id</span>=student_id).delete()  <span class="hljs-comment"># 条件删除</span><br>    <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> deleted_count:<br>        <span class="hljs-keyword">raise</span> HTTPException(status_code=<span class="hljs-number">404</span>, detail=<span class="hljs-string">f&quot;Student <span class="hljs-subst">&#123;student_id&#125;</span> not found&quot;</span>)<br>    <span class="hljs-keyword">return</span> &#123;&#125;<br><br></code></pre></td></tr></table></figure><h2 id="六、中间件与CORS">六、中间件与CORS</h2><h3 id="6-1、中间件">6.1、中间件</h3><p>你可以向 <strong>FastAPI</strong> 应用添加中间件.</p><p>&quot;中间件&quot;是一个函数,它在每个<strong>请求</strong>被特定的路径操作处理之前,以及在每个<strong>响应</strong>之后工作.</p><style>.nteafhrxrstu{}</style><blockquote><p>如果你使用了 <code>yield</code> 关键字依赖, 依赖中的退出代码将在执行中间件<em>后</em>执行.</p><p>如果有任何后台任务(稍后记录), 它们将在执行中间件<em>后</em>运行.</p></blockquote><p>要创建中间件你可以在函数的顶部使用装饰器 <code>@app.middleware(&quot;http&quot;)</code>.</p><p>中间件参数接收如下参数：</p><blockquote><ul><li><code>request</code>.</li><li>一个函数<code>call_next</code>，它将接收request，作为参数.<ul><li>这个函数将 <code>request</code> 传递给相应的 <em>路径操作</em>.</li><li>然后它将返回由相应的<em>路径操作</em>生成的 <code>response</code>.</li></ul></li><li>然后你可以在返回 <code>response</code> 前进一步修改它.</li></ul></blockquote><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">import</span> uvicorn<br><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI<br><br><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> Request<br><span class="hljs-keyword">from</span> fastapi.responses <span class="hljs-keyword">import</span> Response<br><span class="hljs-keyword">import</span> time<br><br>app = FastAPI()<br><br><br><span class="hljs-meta">@app.middleware(<span class="hljs-params"><span class="hljs-string">&quot;http&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">m2</span>(<span class="hljs-params">request: Request, call_next</span>):<br>    <span class="hljs-comment"># 请求代码块</span><br>    <span class="hljs-built_in">print</span>(<span class="hljs-string">&quot;m2 request&quot;</span>)<br>    response = <span class="hljs-keyword">await</span> call_next(request)<br>    <span class="hljs-comment"># 响应代码块</span><br>    response.headers[<span class="hljs-string">&quot;author&quot;</span>] = <span class="hljs-string">&quot;yuan&quot;</span><br>    <span class="hljs-built_in">print</span>(<span class="hljs-string">&quot;m2 response&quot;</span>)<br>    <span class="hljs-keyword">return</span> response<br><br><br><span class="hljs-meta">@app.middleware(<span class="hljs-params"><span class="hljs-string">&quot;http&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">m1</span>(<span class="hljs-params">request: Request, call_next</span>):<br>    <span class="hljs-comment"># 请求代码块</span><br>    <span class="hljs-built_in">print</span>(<span class="hljs-string">&quot;m1 request&quot;</span>)<br>    <span class="hljs-comment"># if request.client.host in [&quot;127.0.0.1&quot;, ]:  # 黑名单</span><br>    <span class="hljs-comment">#     return Response(content=&quot;visit forbidden&quot;)</span><br><br>    <span class="hljs-comment"># if request.url.path in [&quot;/user&quot;]:</span><br>    <span class="hljs-comment">#     return Response(content=&quot;visit forbidden&quot;)</span><br><br>    start = time.time()<br><br>    response = <span class="hljs-keyword">await</span> call_next(request)<br>    <span class="hljs-comment"># 响应代码块</span><br>    <span class="hljs-built_in">print</span>(<span class="hljs-string">&quot;m1 response&quot;</span>)<br>    end = time.time()<br>    response.headers[<span class="hljs-string">&quot;ProcessTimer&quot;</span>] = <span class="hljs-built_in">str</span>(end - start)<br>    <span class="hljs-keyword">return</span> response<br><br><br><span class="hljs-meta">@app.get(<span class="hljs-params"><span class="hljs-string">&quot;/user&quot;</span></span>)</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">get_user</span>():<br>    time.sleep(<span class="hljs-number">3</span>)<br>    <span class="hljs-built_in">print</span>(<span class="hljs-string">&quot;get_user函数执行&quot;</span>)<br>    <span class="hljs-keyword">return</span> &#123;<br>        <span class="hljs-string">&quot;user&quot;</span>: <span class="hljs-string">&quot;current user&quot;</span><br>    &#125;<br><br><br><span class="hljs-meta">@app.get(<span class="hljs-params"><span class="hljs-string">&quot;/item/&#123;item_id&#125;&quot;</span></span>)</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">get_item</span>(<span class="hljs-params">item_id: <span class="hljs-built_in">int</span></span>):<br>    time.sleep(<span class="hljs-number">2</span>)<br>    <span class="hljs-built_in">print</span>(<span class="hljs-string">&quot;get_item函数执行&quot;</span>)<br>    <span class="hljs-keyword">return</span> &#123;<br>        <span class="hljs-string">&quot;item_id&quot;</span>: item_id<br>    &#125;<br><br><br><span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">&#x27;__main__&#x27;</span>:<br>    uvicorn.run(<span class="hljs-string">&#x27;main:app&#x27;</span>, host=<span class="hljs-string">&#x27;127.0.0.1&#x27;</span>, port=<span class="hljs-number">8030</span>, reload=<span class="hljs-literal">True</span>,<br>                debug=<span class="hljs-literal">True</span>, workers=<span class="hljs-number">1</span>)<br><br></code></pre></td></tr></table></figure><h3 id="6-2、CORS">6.2、CORS</h3> <figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><code class="hljs html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-keyword">html</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">&quot;en&quot;</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">&quot;UTF-8&quot;</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Title<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">&quot;https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span><br><span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span><br><br><span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>click<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span><br><br><span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="language-javascript"></span><br><span class="language-javascript">  </span><br><span class="language-javascript">    $(<span class="hljs-string">&quot;p&quot;</span>).<span class="hljs-title function_">click</span>(<span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) &#123;</span><br><span class="language-javascript">        $.<span class="hljs-title function_">ajax</span>(&#123;</span><br><span class="language-javascript">            <span class="hljs-attr">url</span>: <span class="hljs-string">&quot;http://127.0.0.1:8080/&quot;</span>,</span><br><span class="language-javascript">            <span class="hljs-attr">success</span>: <span class="hljs-keyword">function</span> (<span class="hljs-params">res</span>) &#123;</span><br><span class="language-javascript">                $(<span class="hljs-string">&quot;p&quot;</span>).<span class="hljs-title function_">html</span>(res.<span class="hljs-property">message</span>)</span><br><span class="language-javascript">            &#125;,</span><br><span class="language-javascript">        &#125;)</span><br><span class="language-javascript">    &#125;)</span><br><span class="language-javascript"></span><br><span class="language-javascript"></span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span><br><span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span><br><span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span><br></code></pre></td></tr></table></figure><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-meta">@app.middleware(<span class="hljs-params"><span class="hljs-string">&quot;http&quot;</span></span>)</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">CORSMiddleware</span>(<span class="hljs-params">request: Request, call_next</span>):<br>    response = <span class="hljs-keyword">await</span> call_next(request)<br>    <span class="hljs-built_in">print</span>(response.headers)<br>    <span class="hljs-keyword">return</span> response<br></code></pre></td></tr></table></figure><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI<br><span class="hljs-keyword">from</span> fastapi.middleware.cors <span class="hljs-keyword">import</span> CORSMiddleware<br><br>app = FastAPI()<br>origins = [<br>    <span class="hljs-string">&quot;http://localhost:63342&quot;</span><br>]<br><br>app.add_middleware(<br>    CORSMiddleware,<br>    allow_origins=origins,  <span class="hljs-comment"># *：代表所有客户端</span><br>    allow_credentials=<span class="hljs-literal">True</span>,<br>    allow_methods=[<span class="hljs-string">&quot;GET&quot;</span>],<br>    allow_headers=[<span class="hljs-string">&quot;*&quot;</span>],<br>)<br><br><span class="hljs-meta">@app.get(<span class="hljs-params"><span class="hljs-string">&quot;/&quot;</span></span>)</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">main</span>():<br>    <span class="hljs-keyword">return</span> &#123;<span class="hljs-string">&quot;message&quot;</span>: <span class="hljs-string">&quot;Hello World&quot;</span>&#125;<br><br><br><span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">&#x27;__main__&#x27;</span>:<br>    <span class="hljs-keyword">import</span> uvicorn<br><br>    uvicorn.run(<span class="hljs-string">&quot;main:app&quot;</span>, host=<span class="hljs-string">&quot;127.0.0.1&quot;</span>, port=<span class="hljs-number">8080</span>, debug=<span class="hljs-literal">True</span>, reload=<span class="hljs-literal">True</span>)<br><br></code></pre></td></tr></table></figure>]]>
    </content>
    <id>https://www.furinaluna.top/posts/fastapi-framework.html</id>
    <link href="https://www.furinaluna.top/posts/fastapi-framework.html"/>
    <published>2026-03-31T08:30:00.000Z</published>
    <summary>
      <![CDATA[<h1>FastAPI框架</h1>
<style>.wiopbmiodoiu{}</style>
<p><code>fastapi</code>，一个用于构建 API 的现代、快速（高性能）的web框架。</p>
<p><code>fastapi</code>是建立在Starl]]>
    </summary>
    <title>FastAPI框架</title>
    <updated>2026-09-11T13:48:57.131Z</updated>
  </entry>
  <entry>
    <author>
      <name>Furina</name>
    </author>
    <category term="SQLAlchemy" scheme="https://www.furinaluna.top/categories/SQLAlchemy/"/>
    <category term="SQLAlchemy,Python" scheme="https://www.furinaluna.top/tags/SQLAlchemy-Python/"/>
    <content>
      <![CDATA[<p>数据库表是一个二维表，包含多行多列。把一个表的内容用Python的数据结构表示出来的话，可以用一个list表示多行，list的每一个元素是tuple，表示一行记录，比如，包含id和name的user表：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs python">[<br>    (<span class="hljs-string">&#x27;1&#x27;</span>, <span class="hljs-string">&#x27;Michael&#x27;</span>),<br>    (<span class="hljs-string">&#x27;2&#x27;</span>, <span class="hljs-string">&#x27;Bob&#x27;</span>),<br>    (<span class="hljs-string">&#x27;3&#x27;</span>, <span class="hljs-string">&#x27;Adam&#x27;</span>)<br>]<br></code></pre></td></tr></table></figure><p>Python的DB-API返回的数据结构就是像上面这样表示的。</p><p>但是用tuple表示一行很难看出表的结构。如果把一个tuple用class实例来表示，就可以更容易地看出表的结构来：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">class</span> <span class="hljs-title class_">User</span>(<span class="hljs-title class_ inherited__">object</span>):<br>    <span class="hljs-keyword">def</span> <span class="hljs-title function_">__init__</span>(<span class="hljs-params">self, <span class="hljs-built_in">id</span>, name</span>):<br>        <span class="hljs-variable language_">self</span>.<span class="hljs-built_in">id</span> = <span class="hljs-built_in">id</span><br>        <span class="hljs-variable language_">self</span>.name = name<br><br>[<br>    User(<span class="hljs-string">&#x27;1&#x27;</span>, <span class="hljs-string">&#x27;Michael&#x27;</span>),<br>    User(<span class="hljs-string">&#x27;2&#x27;</span>, <span class="hljs-string">&#x27;Bob&#x27;</span>),<br>    User(<span class="hljs-string">&#x27;3&#x27;</span>, <span class="hljs-string">&#x27;Adam&#x27;</span>)<br>]<br></code></pre></td></tr></table></figure><p>这就是传说中的ORM技术：<code>Object-Relational Mapping</code>，把关系数据库的表结构映射到对象上。是不是很简单？</p><p>但是由谁来做这个转换呢？所以就有了<code>ORM框架</code>。</p><p>在Python中，最有名的ORM框架是SQLAlchemy。我们来看看SQLAlchemy的用法。</p><p>首先通过pip安装SQLAlchemy：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs shell"><span class="hljs-meta prompt_">$ </span><span class="language-bash">pip install sqlalchemy</span><br></code></pre></td></tr></table></figure><p>第一步，导入SQLAlchemy，并初始化DBSession：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><code class="hljs python"><br><span class="hljs-comment"># 导入:</span><br><span class="hljs-keyword">from</span> sqlalchemy <span class="hljs-keyword">import</span> Column, String, create_engine<br><span class="hljs-keyword">from</span> sqlalchemy.orm <span class="hljs-keyword">import</span> sessionmaker<br><span class="hljs-keyword">from</span> sqlalchemy.orm <span class="hljs-keyword">import</span> declarative_base<br><br><span class="hljs-comment"># 创建对象的基类:</span><br>Base = declarative_base()<br><br><span class="hljs-comment"># 定义User对象:</span><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">User</span>(<span class="hljs-title class_ inherited__">Base</span>):<br>    <span class="hljs-comment"># 表的名字:</span><br>    __tablename__ = <span class="hljs-string">&#x27;user&#x27;</span><br><br>    <span class="hljs-comment"># 表的结构:</span><br>    <span class="hljs-built_in">id</span> = Column(String(<span class="hljs-number">20</span>), primary_key=<span class="hljs-literal">True</span>)<br>    name = Column(String(<span class="hljs-number">20</span>))<br><br><span class="hljs-comment"># 初始化数据库连接:</span><br>engine = create_engine(<span class="hljs-string">&#x27;mysql+mysqlconnector://root:password@localhost:3306/test&#x27;</span>)<br><span class="hljs-comment"># 创建DBSession类型:</span><br>DBSession = sessionmaker(bind=engine)<br><br></code></pre></td></tr></table></figure><p>以上代码完成SQLAlchemy的初始化和具体每个表的class定义。如果有多个表，就继续定义其他class，例如School：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">class</span> <span class="hljs-title class_">School</span>(<span class="hljs-title class_ inherited__">Base</span>):<br>    __tablename__ = <span class="hljs-string">&#x27;school&#x27;</span><br>    <span class="hljs-built_in">id</span> = ...<br>    name = ...<br></code></pre></td></tr></table></figure><figure class="highlight stylus"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs stylus"><span class="hljs-function"><span class="hljs-title">create_engine</span><span class="hljs-params">()</span></span><br></code></pre></td></tr></table></figure><p>用来初始化数据库连接。SQLAlchemy用一个字符串表示连接信息：</p><p>‘数据库’mysql+mysqlconnector://root:password@localhost:3306/test’</p><figure class="highlight autohotkey"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><code class="hljs autohotkey">```mysql+mysqlconnector```表示使用MySQL的```mysql-connector-python```驱动，```root```是用户名，```password```是数据库密码，```localhost```是机器地址，```3306```是端口号，```test```是数据库名。<br><br>你只需要根据需要替换掉用户名、口令等信息即可。<br><br><br>下面，我们看看如何向数据库表中添加一行记录。<br><br>由于有了ORM，我们向数据库表中添加一行记录，可以视为添加一个User对象：<br><br>```python<br><span class="hljs-title"># 创建session对象:</span><br>session = DBSession()<br><span class="hljs-title"># 创建新User对象:</span><br>new_user = User(id=&#x27;<span class="hljs-number">5</span>&#x27;, name=&#x27;Bob&#x27;)<br><span class="hljs-title"># 添加到session:</span><br>session.add(new_user)<br><span class="hljs-title"># 提交即保存到数据库:</span><br>session.commit()<br><span class="hljs-title"># 关闭session:</span><br>session.close()<br></code></pre></td></tr></table></figure><p>可见，关键是获取session，然后把对象添加到session，最后提交并关闭。DBSession对象可视为当前数据库连接。</p><p>如何从数据库表中查询数据呢？有了ORM，查询出来的可以不再是tuple，而是User对象。SQLAlchemy提供的查询接口如下：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-comment"># 创建Session:</span><br>session = DBSession()<br><span class="hljs-comment"># 创建Query查询，filter是where条件，最后调用one()返回唯一行，如果调用all()则返回所有行:</span><br>user = session.query(User).<span class="hljs-built_in">filter</span>(User.<span class="hljs-built_in">id</span>==<span class="hljs-string">&#x27;5&#x27;</span>).one()<br><span class="hljs-comment"># 打印类型和对象的name属性:</span><br><span class="hljs-built_in">print</span>(<span class="hljs-string">&#x27;type:&#x27;</span>, <span class="hljs-built_in">type</span>(user))<br><span class="hljs-built_in">print</span>(<span class="hljs-string">&#x27;name:&#x27;</span>, user.name)<br><span class="hljs-comment"># 关闭Session:</span><br>session.close()<br></code></pre></td></tr></table></figure><p>运行结果如下：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-built_in">type</span>: &lt;<span class="hljs-keyword">class</span> <span class="hljs-string">&#x27;__main__.User&#x27;</span>&gt;<br>name: Bob<br></code></pre></td></tr></table></figure><p>可见，ORM就是把数据库表的行与相应的对象建立关联，互相转换。</p><p>由于关系数据库的多个表还可以用外键实现一对多、多对多等关联，相应地，ORM框架也可以提供两个对象之间的一对多、多对多等功能。</p><p>例如，如果一个User拥有多个Book，就可以定义一对多关系如下：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">class</span> <span class="hljs-title class_">User</span>(<span class="hljs-title class_ inherited__">Base</span>):<br>    __tablename__ = <span class="hljs-string">&#x27;user&#x27;</span><br><br>    <span class="hljs-built_in">id</span> = Column(String(<span class="hljs-number">20</span>), primary_key=<span class="hljs-literal">True</span>)<br>    name = Column(String(<span class="hljs-number">20</span>))<br>    <span class="hljs-comment"># 一对多:</span><br>    books = relationship(<span class="hljs-string">&#x27;Book&#x27;</span>)<br><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">Book</span>(<span class="hljs-title class_ inherited__">Base</span>):<br>    __tablename__ = <span class="hljs-string">&#x27;book&#x27;</span><br><br>    <span class="hljs-built_in">id</span> = Column(String(<span class="hljs-number">20</span>), primary_key=<span class="hljs-literal">True</span>)<br>    name = Column(String(<span class="hljs-number">20</span>))<br>    <span class="hljs-comment"># “多”的一方的book表是通过外键关联到user表的:</span><br>    user_id = Column(String(<span class="hljs-number">20</span>), ForeignKey(<span class="hljs-string">&#x27;user.id&#x27;</span>))<br></code></pre></td></tr></table></figure><p>当我们查询一个User对象时，该对象的books属性将返回一个包含若干个Book对象的list。</p><h2 id="小结">小结</h2><p>ORM框架的作用就是把数据库表的一行记录与一个对象互相做自动转换。<br>正确使用ORM的前提是了解关系数据库的原理。<br>ORM框架的使用方法是：先定义映射关系，然后就可以把数据库表看成一个对象来操作。</p>]]>
    </content>
    <id>https://www.furinaluna.top/posts/57e0396f.html</id>
    <link href="https://www.furinaluna.top/posts/57e0396f.html"/>
    <published>2026-03-30T00:36:52.000Z</published>
    <summary>
      <![CDATA[<p>数据库表是一个二维表，包含多行多列。把一个表的内容用Python的数据结构表示出来的话，可以用一个list表示多行，list的每一个元素是tuple，表示一行记录，比如，包含id和name的user表：</p>
<figure class="highlight python]]>
    </summary>
    <title>SQLAlchemy简单实践</title>
    <updated>2026-09-11T13:48:57.151Z</updated>
  </entry>
  <entry>
    <author>
      <name>Furina</name>
    </author>
    <category term="Python, 人工智能" scheme="https://www.furinaluna.top/categories/Python-%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD/"/>
    <category term="Python, 人工智能" scheme="https://www.furinaluna.top/tags/Python-%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD/"/>
    <content>
      <![CDATA[<p>没办法，准备转python了，后续再更新，急。</p><p>个人浅薄的看法：如果还在学前端的话，建议学完<strong>vue/react，和ts</strong>，再转python，没学前端的可以直接学python。</p><h2 id="先学python基础，很简单，有编程基础三四天就可以过完">先学python基础，很简单，有编程基础三四天就可以过完</h2><p>学习资料：</p><ul><li><a href="https://www.runoob.com/python3/python3-tutorial.html">菜鸟教程python3</a></li><li><a href="https://docs.python.org/3/">python官方文档</a></li></ul><h2 id="学习fastapi，django或者flask也行，选一个，快一点">学习fastapi，django或者flask也行，选一个，快一点</h2><p>学习资料：</p><ul><li><a href="https://www.runoob.com/fastapi/fastapi-tutorial.html">菜鸟教程fastapi</a></li><li><a href="https://fastapi.tiangolo.com/">fastapi官方文档</a></li></ul><h2 id="学orm了解基本概念，然后学个sqlalchemy，快速上手基础增删改查">学orm了解基本概念，然后学个sqlalchemy，快速上手基础增删改查</h2><p>学习资料：</p><ul><li><a href="https://liaoxuefeng.com/books/python/database/sqlalchemy/index.html">廖雪峰的sqlalchemy教程</a></li><li><a href="https://www.sqlalchemy.org/">sqlalchemy官方文档</a></li></ul><h2 id="知道怎么样快速实现ai应用开发">知道怎么样快速实现ai应用开发</h2><p>怎么调ai接口并且和前端后端交互，让ai模型按照具体项目所需要的提示词进行响应等等。可以网上找找项目，课程可以看黑马的，但是建议不用全看，多数时候看相关的<strong>官方文档</strong>。</p><h2 id="继续深入学习">继续深入学习</h2><p>一般的人还是先不要搞底层，算法那些，我们暂时只学应用层面，搓ai应用开发。快速找个实习，你要能够快速做出东西来，对自己做的项目优化可说，基本的面试题会一点，进一个中小厂实习积累经验应该没问题。</p><h2 id="待更新">待更新</h2>]]>
    </content>
    <id>https://www.furinaluna.top/posts/797bc1cb.html</id>
    <link href="https://www.furinaluna.top/posts/797bc1cb.html"/>
    <published>2026-03-19T14:13:37.000Z</published>
    <summary>
      <![CDATA[<p>没办法，准备转python了，后续再更新，急。</p>
<p>个人浅薄的看法：如果还在学前端的话，建议学完<strong>vue/react，和ts</strong>，再转python，没学前端的可以直接学python。</p>
<h2 id="先学python基础，很简单]]>
    </summary>
    <title>前端转python ai全栈开发</title>
    <updated>2026-09-11T13:48:57.147Z</updated>
  </entry>
  <entry>
    <author>
      <name>Furina</name>
    </author>
    <category term="工作室日志" scheme="https://www.furinaluna.top/categories/%E5%B7%A5%E4%BD%9C%E5%AE%A4%E6%97%A5%E5%BF%97/"/>
    <category term="工作室日志" scheme="https://www.furinaluna.top/tags/%E5%B7%A5%E4%BD%9C%E5%AE%A4%E6%97%A5%E5%BF%97/"/>
    <content>
      <![CDATA[<p>为进一步提升工作室的整体形象与信息展示体验，<strong>半月工作室官网已于近日完成全新站点迁移与升级</strong>，并正式上线运行。<br>而<strong>此站点则将作为我个人的技术博客</strong>，用于记录工作室的技术进展、项目经验、学习笔记等。<br>本次迁移标志着工作室在技术建设与规范化运营方面迈出了重要一步。新站点采用前后端分离的现代 Web 架构，在页面设计、访问速度与可扩展性等方面均进行了系统性的优化。</p><h2 id="🔧-本次迁移主要内容">🔧 本次迁移主要内容</h2><h3 id="新站点👉-lab-furinaluna-top">新站点👉 <a href="https://lab.furinaluna.top">lab.furinaluna.top</a></h3><p>更清晰地体现工作室 / 实验室属性，便于对外展示与长期维护。<br>前端架构全面升级：<br>新站点基于现代前端技术栈重构，页面结构更加清晰，视觉风格更加简洁统一。<br>为后续系统扩展预留空间：<br>新站点已按前后端分离模式设计，已经了接入<strong>后台管理系统</strong>。</p><h2 id="🌱-为什么要进行站点迁移？">🌱 为什么要进行站点迁移？</h2><p>随着工作室项目与成员规模的逐步扩大，原有页面在扩展性与管理方式上已难以满足后续需求。本次迁移不仅是一次页面调整，更是一次面向长期发展的技术升级，为以下目标打下基础：</p><p>更系统地展示工作室成员与项目成果<br>更方便地进行内容维护与更新<br>构建规范、可持续演进的技术体系</p><h2 id="🚀-展望未来">🚀 展望未来</h2><p>新站点的上线只是开始。接下来，工作室将陆续推进：</p><p>成员与项目的动态展示<br>技术文章<br>招新<br>我们也欢迎大家在使用过程中提出宝贵建议，帮助工作室不断完善与进步。</p><p>让我们以全新的站点为起点，继续在技术道路上稳步前行。</p><p>半月工作室</p><blockquote><p>📍 lab.furinaluna.top</p></blockquote>]]>
    </content>
    <id>https://www.furinaluna.top/posts/694a08e1.html</id>
    <link href="https://www.furinaluna.top/posts/694a08e1.html"/>
    <published>2025-12-22T08:29:29.000Z</published>
    <summary>
      <![CDATA[<p>为进一步提升工作室的整体形象与信息展示体验，<strong>半月工作室官网已于近日完成全新站点迁移与升级</strong>，并正式上线运行。<br>
而<strong>此站点则将作为我个人的技术博客</strong>，用于记录工作室的技术进展、项目经验、学习笔记等。<br>]]>
    </summary>
    <title>半月工作室站点迁移啦!</title>
    <updated>2026-09-11T13:48:57.195Z</updated>
  </entry>
  <entry>
    <author>
      <name>Furina</name>
    </author>
    <category term="招新考核" scheme="https://www.furinaluna.top/categories/%E6%8B%9B%E6%96%B0%E8%80%83%E6%A0%B8/"/>
    <category term="招新考核" scheme="https://www.furinaluna.top/tags/%E6%8B%9B%E6%96%B0%E8%80%83%E6%A0%B8/"/>
    <content>
      <![CDATA[<h2 id="考核时间">考核时间</h2><h3 id="大一上学期考核（3次）">大一上学期考核（3次）</h3><ul><li>内容：以基本的<strong>编码能力和思维能力</strong>为基础，检验你们的<strong>学习能力</strong>和<strong>积极进取的态度</strong>。在牛客网上写<strong>算法编程题</strong>，不要求全对，但希望你们可以独立思考和解决问题</li></ul><h3 id="大一下学期考核（3次）（重点）">大一下学期考核（3次）<strong>（重点）</strong></h3><ul><li>内容：<em><strong>寒假期间</strong></em> 会分学习的方向，如<strong>前端</strong>、<strong>后端</strong>、<strong>嵌入式</strong>等等，到时候在放假前会发通知，请大家提前做好准备。</li></ul><h2 id="关于大一上学期算法考核题">关于大一上学期算法考核题</h2><h3 id="为什么大一上要算法考核题">为什么大一上要算法考核题</h3><p>有同学会说：&quot;哎，学长，我大一学的太少了，你出的题目太难了，你这不是为难我吗，<s>把我当日本人整</s><br>其实，算法考核题不是“为难”大家，而是为了培大家的能力。</p><ul><li><p>1.首先，你们大一会学习c语言，大一下学期会学习c++，数据结构与算法，大二还有算法设计与分析等等，你先接触一点这些考核题，对你的专业学习肯定会有帮助的，只要你自己想过，写过，你肯定会学到新的东西的。而且到时候你们就会知道，一个人见识越多，才会越优秀。这些都是后话。<br><strong>总结，对你们专业考试有帮助</strong></p></li><li><p>2.<strong>基本的学习能力</strong>。觉得难很正常，因为<strong>学的东西太少了</strong>，而这就是考核题的第二个作用，<strong>培养你们的自主学习能力</strong>，我们程序员，都是在<strong>不断学习</strong>的，<strong>遇到问题</strong>，我们会<strong>去搜索</strong>，去<strong>解决问题</strong>。这就是<strong>自主学习</strong>的能力。有问题你要会找办法解决，首先一定是自己要去找答案，互联网这么发达对吧，什么都可以找到，你说太乱了，太杂了没有好的总结资料，那肯定是很少的，所以<strong>信息检索能力</strong>也是一个非常重要的东西，学会如何在网上找到高质量的知识和体系。还有就是如今，技术一直都在更新迭代，想要能在计算机的行业站住脚，非常考验我们的学习能力和思考能力。比如一个框架，也许过几天又出新的版本了，又或者出了新的流行的框架，别人一下就掌握了，而你却没有，那你就落后了。为什么？因为你没有好的<strong>学习的能力与总结能力</strong>（这个在数学上有的大佬叫它<strong>元学习能力</strong>）。很多<strong>技术是相连通</strong>的，你越学到后面就越会发现，比如高数，线代，概率论，离散数学，<s>你觉得这些对我有个猫的用处</s>，但其实，你学精了，你学其他的，有种手到擒来的感觉。说的有点多了，展开来<s>吹牛</s>讲可以讲一晚上。<br><strong>总之，考核题一个是让你们脑子动起来，一个是让你们学会学习。遇到问题要知道怎么解决，去找大佬，去网上搜索，去哔哩哔哩，去逛论坛，去问AI，这都是很重要的能力。学习能力，总结能力，信息检索能力这种东西，即使从事其他行业，甚至对你的生活都有很大的帮助</strong></p></li><li><p>3.<strong>工作面试和考研升学</strong>，首先一点，去进入一些中大厂，几乎没有不考算法题的，无论是前端还是后端。哎嘿，那又回到原来的问题，为什么考算法考核题，实际的工作业务中，有很多场景会利用到算法的一些思想，比如一个前端，公司给他一个项目，他需要做一个页面，这个页面需要展示很多数据，而这些数据是从后端接口获取的，那么他就需要对这些数据进行排序，筛选，分页等等，你觉得这些数据怎么处理，暴力遍历一遍，你发现页面渲染非常慢，公司经理叫你优化一下，你不会，然后你就偶吼了（这只是一个简单的例子）还有叫你写一个什么富文本编辑器呀乱七八糟的。<br>还有就是你开发时候的一些场景，算法锻炼出的的逻辑思路让你知道要做什么</p></li></ul><blockquote><p>比如叫我做一个项目的评论区，我第一步肯定要数据库设计，我要考虑到评论区的一些功能，比如评论，回复，点赞，删除等等，我要考虑到这些功能的实现，我要找后端要接口，和后端沟通。我要考虑前端渲染，用什么框架，前端性能等等。（你写一道算法题，你是不是也要想，哎，这题用什么，动规，贪心，回溯，如果是动规，那我下一步要怎么做来着，哦，找动规状态转移方程，建立DP表，哎，还有dp的初始化，哎，测试对了。啊，为什么过不了超时了，怎么优化？哦。记忆化搜索，滚动数组对吧。。。。。）</p></blockquote><p><strong>总之，之后你们学习的一些技术，给你一个要求，你要知道第一步是什么，第二步是什么。怎么做更好，哪步错了，怎么改。这些通过写一定的算法题，你能够有一个清晰的认知，业务逻辑的思路和解一道算法题目的思路是很相似的</strong></p><ul><li><p><strong>4.最后，考核题不是为了打击大家，而是为了帮助大家，希望大家能够在考核题中有所收获，有所成长。写不出来不要紧，去看一下答案，学习一下，多思考，多总结，多提升。一定不要傻傻的死磕哈。</strong></p></li><li><p><strong>5.有想走算法方面的同学，可以去看看</strong></p></li></ul><blockquote></blockquote><ul><li><a href="https://www.luogu.com.cn/">算法竞赛</a></li><li><a href="https://www.nowcoder.com/">牛客网</a></li><li><a href="https://leetcode-cn.com/">力扣</a></li><li><a href="https://www.acwing.com/">acwing</a></li><li><a href="https://zhuanlan.zhihu.com/p/354492741?utm_psn=1978916977750856875">如何评估自己的算法能力</a></li></ul><h3 id="总结上学期招新要求">总结上学期招新要求</h3><p>只要你</p><ul><li><p>能够独立思考，解决问题</p></li><li><p>能够有积极进取的态度，会学习，有能力，不放弃</p></li><li><p>能够有良好的学习态度，有良好的学习习惯，有良好的信息检索能力</p></li><li><p>良好的人品，有责任心，有团队精神</p></li></ul><p><strong>欢迎学弟学妹来我们半月工作室，一起学习，一起进步，一起成长，我们一起加油哦</strong></p><style>.wdspnhsleaeu{zoom:50%;}</style><img src="/posts/3c9e7367/1.gif" class="wdspnhsleaeu"><blockquote><p><strong>写的可能有比较多的瑕疵，最近事情多，见谅</strong></p></blockquote><h2 id="关于大一下学期考核题">关于大一下学期考核题</h2><p><strong>待更新</strong></p>]]>
    </content>
    <id>https://www.furinaluna.top/posts/3c9e7367.html</id>
    <link href="https://www.furinaluna.top/posts/3c9e7367.html"/>
    <published>2025-12-01T11:13:38.000Z</published>
    <summary>
      <![CDATA[<h2 id="考核时间">考核时间</h2>
<h3 id="大一上学期考核（3次）">大一上学期考核（3次）</h3>
<ul>
<li>内容：以基本的<strong>编码能力和思维能力</strong>为基础，检验你们的<strong>学习能力</strong>和<stro]]>
    </summary>
    <title>关于考核题</title>
    <updated>2026-09-11T13:48:57.151Z</updated>
  </entry>
  <entry>
    <author>
      <name>Furina</name>
    </author>
    <category term="期末复习" scheme="https://www.furinaluna.top/categories/%E6%9C%9F%E6%9C%AB%E5%A4%8D%E4%B9%A0/"/>
    <category term="期末考试 算法设计与分析 专业课" scheme="https://www.furinaluna.top/tags/%E6%9C%9F%E6%9C%AB%E8%80%83%E8%AF%95-%E7%AE%97%E6%B3%95%E8%AE%BE%E8%AE%A1%E4%B8%8E%E5%88%86%E6%9E%90-%E4%B8%93%E4%B8%9A%E8%AF%BE/"/>
    <content>
      <![CDATA[<h2 id="前言">前言</h2><blockquote><p><strong>本篇主要用于我自己的复习，如果想速成的话掌握这些够用了，再看看书上或者视频的解题的模板过程</strong></p></blockquote><h2 id="考试内容">考试内容</h2><ul><li>时间复杂度(直接带入和主定理法)</li><li>分治</li><li>动态规划</li><li>贪心算法</li><li>回溯</li><li>分支限界</li></ul><h2 id="时间复杂度-直接带入和主定理法">时间复杂度(直接带入和主定理法)</h2><h2 id="分治">分治</h2><h3 id="分治的基本思想">分治的基本思想</h3><p>将一个较难解决的规模较大的问题，分解成多个相似的规模较小的子问题，递归地解决这些子问题，然后将这些子问题的解合并成原问题的解。</p><h3 id="分治的基本步骤">分治的基本步骤</h3><ol><li>分解：将原问题分解成多个相似的子问题。</li><li>解决：（一般通过递归）地解决这些子问题。</li><li>合并：将这些子问题的解合并成原问题的解。</li></ol><h3 id="分治算法的特点（条件）">分治算法的特点（条件）</h3><ol><li>原问题的规模缩小到一定程度就可以直接解决。</li><li>原问题可以分解成多个相似的子问题。（分治法的前提：最优子结构性质）</li><li>子问题的解可以合并成原问题的解。（能否利用分治法完全取决于这个特征：可以合并）</li><li>子问题之间是独立的，不包含公共的子问题。（子问题相互独立，互不影响）</li></ol><h3 id="案例">案例</h3><ul><li>归并排序</li><li><strong>快速排序</strong></li><li>二分查找</li><li>大整数乘法</li><li>平面内最近点对问题</li><li>矩阵乘法</li><li>Strassen矩阵乘法</li><li>最大子数组问题</li><li>快速傅里叶变换(FFT)</li></ul><h2 id="动态规划（带备忘录法自顶向下，空间换时间，储存记录子问题的解，避免重复计算）">动态规划（带备忘录法自顶向下，空间换时间，储存记录子问题的解，避免重复计算）</h2><h3 id="动态规划的基本思想">动态规划的基本思想</h3><p>解决<strong>多阶段决策最优化问题</strong>的数学模型。将多阶段决策过程分解成多个子问题，每个子问题对应一个阶段，<strong>每个阶段的决策依赖于前一个阶段的决策结果</strong>。通过动态规划的方法，从第一个阶段开始，逐步计算每个阶段的最优解，最终得到多阶段决策过程的最优解。（从全局考虑）</p><h3 id="动态规划的基本步骤">动态规划的基本步骤</h3><ol><li>分析最优解的性质是不是：最优子结构性质、无后效性、重叠子问题，是否满足动规的前提。</li><li>合适划分子问题，子问题重叠度高，考虑动态规划。</li><li>确定动态规划的状态转移方程。</li><li>确定边界条件。</li><li>自底向上计算每个状态的最优解。(迭代)/自顶向下(备忘录法)。</li><li>根据计算结果，构造最优解。用备忘录和标记函数值通过追溯来得到最优解。</li></ol><h3 id="特点">特点</h3><ol><li><strong>最优子结构性质</strong>：如果问题的最优解所包含的子问题的解也是最优的，我们就称该问题具有最优子结构性质（即满足最优子结构性质的问题可以用动态规划求解）。</li><li><strong>无后效性（马尔可夫性）</strong>：即某阶段状态一旦确定，则该状态下的决策不再受到以后决策的影响。</li><li><strong>子问题重叠性质</strong>：子问题之间是重叠的，即不同的子问题之间存在公共的子子问题。（与分治法的区别，<strong>分治法的子问题是独立的</strong>，互不影响）</li></ol><h3 id="案例-2">案例</h3><ul><li><strong>0-1背包问题</strong></li><li>最长公共子序列问题</li><li>最大子段和问题（也可以用<strong>分治法</strong>解决）</li><li>矩阵链乘法问题</li><li>最小编辑距离问题</li></ul><h3 id="0-1背包问题">0-1背包问题</h3><h2 id="贪心算法">贪心算法</h2><h3 id="贪心算法的基本思想">贪心算法的基本思想</h3><p><strong>部分最优解</strong>构成<strong>全局最优解</strong>。</p><h3 id="贪心的性质（贪心不可以解决0-1背包问题，可以解决部分背包问题）">贪心的性质（<strong>贪心不可以解决0-1背包问题，可以解决部分背包问题</strong>）</h3><ol><li><strong>贪心选择性质</strong>：每一步都做出当前最优的选择，期望通过一系列的最优选择，最终得到全局最优解。（与动态规划的区别：<strong>动态规划的每一步都考虑了所有可能的选择（全局），而贪心算法只考虑当前最优的选择</strong>。或者说动态规划有重叠子问题性质，贪心有贪心选择性质）</li><li><strong>最优子结构性质</strong>：<strong>问题的最优解包含子问题的最优解。</strong></li></ol><h3 id="案例-3">案例</h3><ul><li>活动选择问题</li><li>哈夫曼编码问题</li><li><strong>最小生成树问题（Kruskal算法，Prim算法）</strong></li><li><strong>单源最短路径问题（Dijkstra算法）</strong></li><li>多机调度问题</li><li><strong>糖果均分问题</strong></li><li><strong>装载问题</strong></li><li><strong>背包问题</strong>（<strong>部分背包问题</strong>，不是0-1背包问题）</li><li>旅行商问题</li><li>最优二叉查找树问题</li><li>最小路径覆盖问题</li><li>最小路径权问题</li><li>最小费用流问题</li><li>最大流问题</li></ul><h2 id="回溯（通法，深度优先搜索，栈，递归，剪枝，解空间树的画法）">回溯（通法，深度优先搜索，栈，递归，剪枝，解空间树的画法）</h2><h3 id="回溯的基本思想">回溯的基本思想</h3><p>搜索<strong>解空间树</strong>，找到所有满足约束条件的<strong>所有解</strong>。</p><h3 id="回溯的基本步骤">回溯的基本步骤</h3><ol><li>针对问题，定义问题的解空间。</li><li>确定<strong>解空间树</strong>的搜索策略。</li><li>确定剪枝函数。</li><li>以<strong>深度优先搜索</strong>的方式，搜索解空间树。</li><li>在解空间树上，从根节点出发，沿着一条路径，不断选择子节点，直到找到一个满足约束条件的解，或者到达叶子节点，无法继续选择。</li><li>如果找到一个满足约束条件的解，则记录下来。</li><li>如果到达叶子节点，无法继续选择，则返回上一个节点，重新选择其他节点，继续搜索。</li><li>重复步骤4-6，直到解空间树上所有节点都被搜索过。</li><li>输出所有满足约束条件的解。<br>DFS递归<br>模板：</li></ol><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><code class="hljs c++"><span class="hljs-function">type <span class="hljs-title">dfs</span><span class="hljs-params">(type x, ... )</span> <span class="hljs-comment">// 可以存在多个变量</span></span><br><span class="hljs-function"></span>&#123;<br>    <span class="hljs-keyword">if</span>( ... ) <span class="hljs-comment">// 达成目标，找到答案</span><br>    &#123;<br>        ... <span class="hljs-comment">// 输出答案或判断最优解等等</span><br>        <span class="hljs-keyword">return</span>;<br>    &#125;<br>    <span class="hljs-keyword">if</span>( ... ) <span class="hljs-comment">// 达到搜索边界（即到边界了还没搜到，有时没有此步骤）</span><br>    &#123;<br>        <span class="hljs-keyword">return</span>;<br>    &#125;<br>    <span class="hljs-keyword">for</span>( ... ) <span class="hljs-comment">// 遍历所有子节点</span><br>    &#123;<br>        <span class="hljs-keyword">if</span>( ... ) <span class="hljs-comment">// 可以转移状态，一般用标志变量判断</span><br>        &#123;<br>            ... <span class="hljs-comment">// 修改标志变量，表明此节点不可转移</span><br>            <span class="hljs-built_in">dfs</span>( ... ) <span class="hljs-comment">// 搜索子节点，经常为x+1</span><br>            ... <span class="hljs-comment">// 还原标志变量，表面此节点可转移(回溯)</span><br>        &#125;<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure><h3 id="案例-4">案例</h3><ul><li>全排列问题</li><li><strong>n皇后问题</strong></li><li>0-1背包问题</li><li>图的着色问题</li><li>旅行商问题</li><li>最大团问题</li><li>最小覆盖问题</li><li>最大流问题</li><li>最小费用流问题</li></ul><h3 id="解空间树">解空间树</h3><ul><li><p>1子集树<br>给定问题是从n个元素的集合S中找出满足某种条件的所有子集，相应的解空间树称为子集树。如0-1背包问题的解空间树就是子集树。对于01背包，子集树有2^n+1^-1个节点，每个节点对应一个子集。遍历子集树的时间复杂度为O(2^n)。</p></li><li><p>2排列树<br>给定问题是从n个元素找出满足某种条件的所有排列，相应的解空间树称为排列树。如n皇后问题、旅行商问题的解空间树就是排列树。旅行商问题的解空间树有n!个节点，每个节点对应一个排列。遍历排列树的时间复杂度为O(n!)。</p></li></ul><h2 id="分支限界">分支限界</h2><h3 id="分支限界的基本思想">分支限界的基本思想</h3><p>把问题的解空间转化为<strong>解空间树</strong>，然后用<strong>广度优先搜索</strong>或者<strong>最小耗费（最大效益）优先策略</strong>的方式，搜索解空间树。（<strong>找到某一个解/最优解</strong>，<strong>回溯找的是所有解</strong>）</p><h3 id="分支限界的方式">分支限界的方式</h3><p>分支限界根据从活结点表中选择下一个扩展结点的方式，可分为<strong>队列式分支限界</strong>和<strong>优先队列式分支限界</strong>。</p><ul><li><p><strong>队列式分支限界（FIFO）</strong></p></li><li><p><strong>优先队列式分支限界/最小耗费优先/最大效益优先</strong><br>出队的队首结点，是优先级最高（最有利）的结点，成为当前扩展结点。<br>最大（效益）优先队列：大根堆（大顶堆）（最大化问题）<br>最小（耗费）优先队列：小根堆（小顶堆）（最小化问题）</p></li></ul><h3 id="左用分支函数，右用限界函数">左用分支函数，右用限界函数</h3><p><strong>分支函数生成子节点，限界函数判断是否剪枝</strong></p><h3 id="案例-5">案例</h3><ul><li>0-1背包问题</li><li>装载问题</li><li>旅行商问题</li><li>最小路径覆盖问题</li><li>最小路径权问题</li><li>最小费用流问题</li><li>最大流问题</li><li>8数码问题</li></ul>]]>
    </content>
    <id>https://www.furinaluna.top/posts/3274548c.html</id>
    <link href="https://www.furinaluna.top/posts/3274548c.html"/>
    <published>2025-11-27T13:17:59.000Z</published>
    <summary>
      <![CDATA[<h2 id="前言">前言</h2>
<blockquote>
<p><strong>本篇主要用于我自己的复习，如果想速成的话掌握这些够用了，再看看书上或者视频的解题的模板过程</strong></p>
</blockquote>
<h2 id="考试内容">考试内容</h2>]]>
    </summary>
    <title>算法设计与分析</title>
    <updated>2026-09-11T13:48:57.195Z</updated>
  </entry>
  <entry>
    <author>
      <name>Furina</name>
    </author>
    <category term="后端" scheme="https://www.furinaluna.top/categories/%E5%90%8E%E7%AB%AF/"/>
    <category term="后端 学习路线 IT" scheme="https://www.furinaluna.top/tags/%E5%90%8E%E7%AB%AF-%E5%AD%A6%E4%B9%A0%E8%B7%AF%E7%BA%BF-IT/"/>
    <content>
      <![CDATA[<blockquote><p><strong>以下是我和工作室的林学姐总结的后端学习路线</strong></p></blockquote><p><a href="https://www.bilibili.com/opus/494817843530680807">具体的学习路线可以点击这个链接参考下黑马提供的这个路线</a></p><blockquote><p>当然，也可以看看工作室学长、学姐传承下来的学习路线图</p></blockquote><style>.hgcnlqtwtvgj{zoom:50%;}</style><img src="/posts/bae03965/1.jpg" class="hgcnlqtwtvgj" alt="后端学习路线"><blockquote><p>这个视频也可以参考下<br><a href="https://www.bilibili.com/video/BV1Z6YCzSEWq?vd_source=4f10d1af011e56ba00018311e401608d">后端学习路线</a></p></blockquote><h2 id="后端是什么">后端是什么</h2><p>后端在计算机科学和软件开发的领域中，指的是处理<strong>服务器端逻辑</strong>、<strong>数据存储</strong>、以及<strong>与前端进行通信</strong>的部分。后端通常<strong>运行在服务器上</strong>，<strong>不可见于最终用户</strong>，但负责处理所有<strong>业务逻辑</strong>、<strong>数据存取</strong>、以及<strong>与前端的接口交互</strong>。</p><ul><li>说最简单点，后端就是软件的 “大脑” 和 “心脏”，它在幕后负责处理所有核心工作，确保你在前端的每一个操作都能得到正确的响应和结果。它虽然不直接与用户见面，却是整个系统稳定、高效运行的关键。（大一用C语言做的的学生管理系统，后台管理系统，就是简单的后端，大二的时候学校会教Java，还有数据库）</li></ul><blockquote><p><strong>以下是林学姐提供的图示</strong><br>后端开发流程</p></blockquote><style>.lkdnxuzfhvsg{zoom:50%;}</style><img src="/posts/bae03965/3.webp" class="lkdnxuzfhvsg" alt="后端开发流程"><p>后端技术栈</p><style>.bchalikpbmdx{zoom:50%;}</style><img src="/posts/bae03965/2.webp" class="bchalikpbmdx" alt="后端技术栈"><p>后端主要职责</p><style>.qytcoegkjogh{zoom:50%;}</style><img src="/posts/bae03965/4.webp" class="qytcoegkjogh" alt="后端主要职责"><p>后端开发语言</p><ul><li><p>后端开发语言主要有Java、C#、PHP、Python、JavaScript、Node.js、C/C++等。</p></li><li><p>后端开发选择上的话建议首选<strong>Java</strong>其次<strong>Python</strong>。</p></li></ul><h2 id="开发工具">开发工具</h2><ul><li><p>无脑使用<strong>IDEA</strong><br>地址：<a href="https://www.jetbrains.com/idea/">IDEA官方网站</a><br>不过想要免费的话，可以去网上找<strong>激活码</strong>教程，<s>或者联系学长也可以</s></p></li><li><p>python的话，也可以使用<strong>pycharm</strong></p></li></ul><blockquote><p><strong>我主修前端的，不过也了解后端的知识，search到了一些后端的学习资源，可以参考下，具体的可以问后端的学长学姐</strong></p></blockquote><h2 id="后端学习资源">后端学习资源</h2><h3 id="Java基础">Java基础</h3><ul><li><p><strong>黑马程序员</strong>（如果选黑马的话直接按照他们给的路线和教程学习即可）</p></li><li><p><a href="https://www.bilibili.com/video/BV1fh411y7R8/?share_source=copy_web">韩顺平 零基础30天学会Java</a></p></li><li><p><a href="https://www.runoob.com/java/java-tutorial.html">菜鸟教程</a>（可以在线练习）</p></li><li><p><a href="https://www.liaoxuefeng.com/wiki/1252599548343744/1255961913673152">廖雪峰的官方网站</a></p></li></ul><h4 id="练手项目-不要只copy，练手项目做一做，别骗自己">练手项目(不要只copy，练手项目做一做，别骗自己)</h4><ul><li><p><a href="https://github.com/uboger/LibraryManager">JAVA GUI 图书馆管理系统</a></p></li><li><p><a href="https://github.com/wangzhengyi/TankWar">坦克大战小游戏</a></p></li><li><p><a href="https://github.com/HelloClyde/Tetris-Swing">Swing编写的俄罗斯方块</a></p></li><li><p><a href="https://github.com/xenv/SmallAccount">小小记账本</a>(了解数据库的可以看一下)</p></li></ul><h3 id="MySQL数据库">MySQL数据库</h3><p>-<a href="https://www.bilibili.com/video/BV1Kr4y1i7ru/">2022年的黑马程序员 MySQL数据库入门到精通</a><br>（这个视频的话，我作为前端感觉可以速成一下，看前面57节基础篇，后续的话可以根据需要再看）</p><ul><li><p><a href="https://www.bilibili.com/video/BV1xW411u7ax/">尚硅谷MySQL数据库</a>(小姐姐版)</p></li><li><p><a href="https://www.runoob.com/mysql/mysql-tutorial.html">MYSQL菜鸟教程</a></p></li><li><p><a href="https://www.runoob.com/sql/sql-tutorial.html">SQL 教程</a></p></li></ul><h3 id="开发框架">开发框架</h3><p><strong>按照黑马的路线或者尚硅谷的学习路线来</strong><br><strong>哔哩哔哩上都有对应的视频，按照视频学习即可。</strong></p><h2 id="持续更新"><strong>持续更新</strong></h2>]]>
    </content>
    <id>https://www.furinaluna.top/posts/bae03965.html</id>
    <link href="https://www.furinaluna.top/posts/bae03965.html"/>
    <published>2025-11-25T01:51:55.000Z</published>
    <summary>
      <![CDATA[<blockquote>
<p><strong>以下是我和工作室的林学姐总结的后端学习路线</strong></p>
</blockquote>
<p><a href="https://www.bilibili.com/opus/494817843530680807">具体的学习]]>
    </summary>
    <title>后端学习路线</title>
    <updated>2026-09-11T13:48:57.172Z</updated>
  </entry>
  <entry>
    <author>
      <name>Furina</name>
    </author>
    <category term="嵌入式" scheme="https://www.furinaluna.top/categories/%E5%B5%8C%E5%85%A5%E5%BC%8F/"/>
    <category term="嵌入式 学习路线 IT" scheme="https://www.furinaluna.top/tags/%E5%B5%8C%E5%85%A5%E5%BC%8F-%E5%AD%A6%E4%B9%A0%E8%B7%AF%E7%BA%BF-IT/"/>
    <content>
      <![CDATA[<blockquote><p><strong>前言：以下是我和工作室的舒学姐总结的嵌入式学习路线，希望对大家有所帮助。欢迎大家补充和指正</strong></p></blockquote><hr><h2 id="软件工程嵌入式方向学习总结">软件工程嵌入式方向学习总结</h2><h2 id="一、嵌入式开发：给万物“赋魂”">一、嵌入式开发：给万物“赋魂”</h2><p>你可以把嵌入式开发想象成一个“赋魂”的过程。我们身边有无数普通的机器和设备，嵌入式工程师的工作，就是为这些冰冷的机器设计和植入一个“大脑”和“神经”系统，让它们变得“聪明”起来，能够感知环境、独立思考并执行命令。</p><h2 id="二、一个生动的比喻：智能机器人的“体内世界”">二、一个生动的比喻：智能机器人的“体内世界”</h2><p>想象一下，我们要造一个能自己打扫房间的扫地机器人。它的“身体”是轮子、刷子和外壳。而它的“生命”——也就是嵌入式系统——则由以下几部分构成：</p><h3 id="1-硬件层-机器人的“身体和感官”">1. 硬件层 - 机器人的“身体和感官”</h3><p>这是嵌入式系统的物质基础，就像机器人的骨骼、五官和神经末梢。</p><ul><li><strong>主控制器</strong>：相当于机器人的大脑。但这是一种“专注”、“高效”的大脑（通常是微控制器MCU，如STM32系列），专门为特定任务而生，不像我们用的电脑CPU那样“啥都干”。</li><li><strong>传感器</strong>：相当于机器人的眼睛、耳朵和皮肤。比如：<ul><li>红外传感器/超声波传感器 -&gt; 用来“看”路，防止撞墙。</li><li>灰尘传感器 -&gt; 用来“感觉”地面是否干净。</li></ul></li><li><strong>执行器</strong>：相当于机器人的手脚。比如：<ul><li>电机和轮子 -&gt; 负责移动。</li><li>抽气风扇 -&gt; 负责吸尘。</li></ul></li></ul><h3 id="2-软件层-机器人的“思想和本能”">2. 软件层 - 机器人的“思想和本能”</h3><p>这是嵌入式系统的灵魂，决定了这个“身体”如何思考和行动。</p><ul><li><strong>嵌入式操作系统</strong>：一个精简、高效的管理员。它负责管理大脑的“思维”资源，比如先处理“避障”任务，再处理“吸尘”任务。常见的如FreeRTOS，它是一个非常轻量级的任务调度器。</li><li><strong>应用软件/驱动程序</strong>：这是你写的核心代码，是机器人的“技能和本能”。<ul><li><strong>驱动程序</strong>：你写的代码告诉大脑如何控制轮子转动、如何读取传感器的数据。（直接和硬件打交道）</li><li><strong>应用逻辑</strong>：你写的算法，比如“如果传感器发现前方有障碍，就立刻让轮子转向”。（处理数据，做出决策）</li></ul></li></ul><h2 id="三、这个方向主要学什么？">三、这个方向主要学什么？</h2><ol><li><strong>C语言是母语</strong>：这是和硬件沟通最直接、最高效的语言，必须非常精通。</li><li><strong>玩转硬件基础</strong>：要懂数字电路和模拟电路，知道电阻、电容、晶体管是怎么工作的，能看懂芯片的数据手册。</li><li><strong>理解计算机结构</strong>：学习计算机组成原理和体系结构，明白CPU、内存、总线是如何协同工作的。</li><li><strong>拥抱微控制器</strong>：深入学习一种主流的微控制器（如STM32或ESP32），学会如何配置它的各种功能（GPIO、定时器、串口、ADC等）。</li><li><strong>掌握实时操作系统</strong>：学习像FreeRTOS这样的系统，理解多任务、消息队列、信号量等概念，让你的程序能“一心多用”。</li><li><strong>软硬结合，动手实践</strong>：最重要的环节！买一块开发板，亲手焊接电路，点亮第一个LED灯，驱动一个电机，完成一个属于自己的小项目（比如智能小车、天气站）。</li></ol><h2 id="四、未来的职业方向">四、未来的职业方向</h2><p>毕业后，你可以成为：</p><ul><li>嵌入式软件开发工程师</li><li>嵌入式硬件开发工程师</li><li>固件工程师</li><li>物联网开发工程师</li></ul><p>你会在各行各业大展身手：智能家居、汽车电子、无人机、工业自动化、医疗仪器、航空航天等等。</p><h2 id="五、总结一下">五、总结一下</h2><p>软件工程（嵌入式方向）= 软件编程的思维 + 硬件电子的动手能力。</p><p>它不是一个纯虚拟的编程世界，而是一个能让你写的每一行代码，都直接在物理世界中产生效果的非常有成就感的领域。</p><hr><blockquote><p><strong>嵌入式的学习资源还是太少了，我下面再补充一些学习资源吧，虽然我不是嵌入式开发的，但我也了解一点并且search到了一些比较好的资源，分享给大家~</strong></p></blockquote><h2 id="六、嵌入式学习资源推荐">六、嵌入式学习资源推荐</h2><h3 id="1、书籍教材-虽然但是，建议想清楚再决定，不要买了就不看了，或者直接看视频，看技术文档，电子版的">1、书籍教材(虽然但是，建议想清楚再决定，不要买了就不看了，或者直接看视频，看技术文档，电子版的)</h3><table><thead><tr><th style="text-align:left">学习阶段</th><th style="text-align:left">推荐书籍</th><th style="text-align:left">推荐理由</th></tr></thead><tbody><tr><td style="text-align:left"><strong>入门基础</strong></td><td style="text-align:left">《C Primer Plus》</td><td style="text-align:left">嵌入式开发的“圣经”，讲解细致，实例丰富，能帮你彻底吃透C语言的指针、内存管理等核心知识点。 (C语言建议直接看哔哩哔哩的视频)</td></tr><tr><td style="text-align:left"><strong>入门基础</strong></td><td style="text-align:left">《嵌入式系统 - 塑造世界》</td><td style="text-align:left">以实际硬件平台为依托，通过实验案例讲解I/O接口、中断处理等基础概念，特别适合零基础理解嵌入式系统的核心逻辑。</td></tr><tr><td style="text-align:left"><strong>硬件基础</strong></td><td style="text-align:left">《数字电子技术基础》（阎石 著）</td><td style="text-align:left">电子类专业经典教材，清晰讲解逻辑门、时序电路等内容，帮你看懂硬件电路原理，为读懂芯片手册打下基础。</td></tr><tr><td style="text-align:left"><strong>进阶提升</strong></td><td style="text-align:left">《ARM Cortex-M3与Cortex-M4权威指南》</td><td style="text-align:left">深入讲解主流ARM内核的架构和编程模型，是学习STM32等热门微控制器的核心参考资料。</td></tr><tr><td style="text-align:left"><strong>进阶提升</strong></td><td style="text-align:left">《FreeRTOS参考手册》（官方文档）</td><td style="text-align:left">FreeRTOS官方核心资料，详细介绍任务调度、信号量、消息队列等关键功能，搭配实例可快速上手实时操作系统开发。</td></tr><tr><td style="text-align:left"><strong>实战精通</strong></td><td style="text-align:left">《嵌入式Linux开发实战》</td><td style="text-align:left">聚焦嵌入式Linux的系统构建与驱动开发，适合想向Linux嵌入式方向进阶的学习者，内容贴合工业级开发需求。</td></tr></tbody></table><h3 id="2、开源项目与实战平台">2、开源项目与实战平台</h3><ul><li><p><strong><a href="https://github.com/iDoka/awesome-embedded-software">0voice/embedded_software_learn (GitHub)</a></strong></p></li><li><p><strong><a href="https://gitcode.com/gh_mirrors/aw/awesome-embedded-software">如果访问GitHub速度慢，可以访问GitCode这个</a></strong><br>非常适合初学者的开源仓库，将学习路径拆分为C语言基础、驱动开发、RTOS、嵌入式Linux等9个模块，每个模块都有中文文档和示例代码，还包含面试题总结，适配电赛、毕设和求职等多种场景。</p></li><li><p><strong><a href="https://gitcode.com/gh_mirrors/aw/awesome-embedded">Awesome-Embedded (GitCode)</a></strong><br>汇总了嵌入式开发的经典资源，涵盖书籍、工具和项目案例，可通过该仓库快速找到不同阶段的学习素材。</p></li><li><p><strong>普中科技51单片机开发板</strong><br>入门硬件的高性价比选择，配套教程和例程丰富，能完成点亮LED、串口通信等基础实验，帮助新手建立硬件操作的直观认知。</p></li><li><p><strong>STM32F103开发板</strong><br>嵌入式进阶的主流硬件，网上有大量HAL库教程和实战项目（如智能小车、环境监测站），适配从裸机开发到RTOS移植的全流程学习。</p></li></ul><h3 id="3、在线课程与社区">3、在线课程与社区</h3><ul><li><p><strong><a href="https://z7po9bxpe4.k.topthink.com/@stm32/kechengjianjie.html">STM32开发指南 —— 面向IoT领域（这个也不错）</a></strong></p></li><li><p><strong>bilibili上找嵌入式相关的视频</strong><br>有许多专业的嵌入式开发视频，讲解原理、实操项目，适合系统学习和项目实践。</p></li><li><p><strong>CSDN 嵌入式板块(不过有一说一，csdn现在不怎么样了，和GitHub比起来格局小了)</strong><br>有大量开发者分享的实战笔记、问题排查经验，比如驱动调试、开发板踩坑等内容，遇到硬件或代码问题时可快速检索解决方案。</p></li><li><p><strong>知乎 嵌入式话题圈</strong><br>能获取行业动态和职业规划建议，还有资深工程师分享的学习路径，适合了解嵌入式在智能家居、汽车电子等领域的实际应用场景。</p></li></ul><h2 id="End">End</h2><p><strong>以下是你们舒学姐提供的图片</strong></p><style>.qvbpftauohtp{zoom:50%;}</style><img src="/posts/3643abdf/1.jpg" class="qvbpftauohtp" alt="1"><style>.jwhcezsjeand{zoom:50%;}</style><img src="/posts/3643abdf/2.jpg" class="jwhcezsjeand" alt="2"><style>.vfwsixhpiytg{zoom:50%;}</style><img src="/posts/3643abdf/3.jpg" class="vfwsixhpiytg" alt="3">]]>
    </content>
    <id>https://www.furinaluna.top/posts/3643abdf.html</id>
    <link href="https://www.furinaluna.top/posts/3643abdf.html"/>
    <published>2025-11-24T10:32:37.000Z</published>
    <summary>
      <![CDATA[<blockquote>
<p><strong>前言：以下是我和工作室的舒学姐总结的嵌入式学习路线，希望对大家有所帮助。欢迎大家补充和指正</strong></p>
</blockquote>
<hr>
<h2 id="软件工程嵌入式方向学习总结">软件工程嵌入式方向学习总结</]]>
    </summary>
    <title>嵌入式学习路线</title>
    <updated>2026-09-11T13:48:57.191Z</updated>
  </entry>
  <entry>
    <author>
      <name>Furina</name>
    </author>
    <category term="算法" scheme="https://www.furinaluna.top/categories/%E7%AE%97%E6%B3%95/"/>
    <category term="C++ 竞赛模板 算法" scheme="https://www.furinaluna.top/tags/C-%E7%AB%9E%E8%B5%9B%E6%A8%A1%E6%9D%BF-%E7%AE%97%E6%B3%95/"/>
    <content>
      <![CDATA[<hr><p><strong>持续更新中</strong></p><hr><h2 id="1基础模板">1基础模板</h2><p>不一定用这个，看题目需要。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;bits/stdc++.h&gt;</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">define</span> endl <span class="hljs-string">&#x27;\n&#x27;</span></span><br><span class="hljs-keyword">using</span> ll=<span class="hljs-type">long</span> <span class="hljs-type">long</span>;<span class="hljs-comment">//定义long long为ll</span><br><br><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> std;<br><br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">solve</span><span class="hljs-params">()</span></span><br><span class="hljs-function"></span>&#123;<br>    <span class="hljs-comment">// 解题</span><br>&#125;<br><br><span class="hljs-function"><span class="hljs-type">signed</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span><br><span class="hljs-function"></span>&#123;<br>    ios::<span class="hljs-built_in">sync_with_stdio</span>(<span class="hljs-literal">false</span>);<br>    cin.<span class="hljs-built_in">tie</span>(<span class="hljs-number">0</span>);<span class="hljs-comment">//关闭cin同步，提高效率</span><br>    cout.<span class="hljs-built_in">tie</span>(<span class="hljs-number">0</span>);<span class="hljs-comment">//关闭cout同步，提高效率</span><br>    <span class="hljs-type">int</span> t = <span class="hljs-number">1</span>;<br>    cin &gt;&gt; t; <span class="hljs-comment">// 单测则注释</span><br>    <span class="hljs-keyword">while</span> (t--)<br>        <span class="hljs-built_in">solve</span>();<br>    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>&#125;<br><br><br></code></pre></td></tr></table></figure>]]>
    </content>
    <id>https://www.furinaluna.top/posts/d097ebe.html</id>
    <link href="https://www.furinaluna.top/posts/d097ebe.html"/>
    <published>2025-11-21T07:04:13.000Z</published>
    <summary>
      <![CDATA[<hr>
<p><strong>持续更新中</strong></p>
<hr>
<h2 id="1基础模板">1基础模板</h2>
<p>不一定用这个，看题目需要。</p>
<figure class="highlight cpp"><table><tr><td class="g]]>
    </summary>
    <title>C++竞赛代码模板</title>
    <updated>2026-09-11T13:48:57.130Z</updated>
  </entry>
  <entry>
    <author>
      <name>Furina</name>
    </author>
    <category term="前端" scheme="https://www.furinaluna.top/categories/%E5%89%8D%E7%AB%AF/"/>
    <category term="前端 互联网 学习路线 IT" scheme="https://www.furinaluna.top/tags/%E5%89%8D%E7%AB%AF-%E4%BA%92%E8%81%94%E7%BD%91-%E5%AD%A6%E4%B9%A0%E8%B7%AF%E7%BA%BF-IT/"/>
    <content>
      <![CDATA[<ul><li>✨必学</li><li>✅推荐</li><li>❌不推荐</li></ul><h2 id="大纲">大纲</h2><ul><li><p>1.前言 - 学编程需要的特质</p></li><li><p>2.前端学习七阶段</p><ul><li>1.前端入门</li><li>2.巩固基础</li><li>3.前端工程化</li><li>4.前端优化</li><li>5.前端生态</li><li>6.前端求职</li><li>7.前端未来</li></ul></li><li><p>3.尾声-持续学习</p></li></ul><h2 id="前言-学编程需要的特质">前言 - 学编程需要的特质</h2><hr><p><s>不怕秃头</s><br>相信自己有能力，你要知道，现在的所有技术都是人发明的，理论上讲，只要你可以一直学习，就可以掌握所有的技术，换句话说，你可以成为一个技术大牛。但是，你需要有以下特质：</p><ul><li>兴趣</li><li>坚持</li><li>付出</li><li>心态</li></ul><hr><h2 id="一、前端入门">一、前端入门</h2><ul><li><p>描述：学习前端基础三件套，建议从实战开始，边学边练，培养兴趣，快速入门。<br>（前端的前期阶段和后端比起来应该更简单，因为入门很快，正反馈比较快，你认真学基本可以快速地掌握前端基础，做出一些好看的静态页面，增加你的自信心。后期的话也不难，只要你不当摆子，It行业忌讳的就是不持续学习）</p></li><li><p>资源：</p><ul><li><a href="https://developer.mozilla.org/zh-CN/E">MDN</a></li><li><a href="https://w3schools.org.cn/">W3Cschool中文网</a></li><li><a href="https://www.bilibili.com/video/BV1MvaVzUEuz/?p=2&amp;share_source=copy_web">pink老师前端入门教程</a></li><li><a href="https://developer.aliyun.com/learning/roadmap/frontend">阿里云前端实战学习</a></li><li>b站上门各种视频等等</li></ul></li><li><p>目标:了解和实践各语言的基本语法，可以使用开发工具来独立开发一个留言板网站。</p></li></ul><h3 id="开发工具">开发工具</h3><ul><li>描述：工欲善其事，必先利其器。</li></ul><h4 id="浏览器">浏览器</h4><ul><li>✨Chrome（推荐，不过可能要做一些配置，国内访问会被墙，直接用<s>代理或者</s>Edge就可以了）</li><li>✅Firefox</li><li>✅Edge</li><li>❌Opera</li><li>Safari</li></ul><h4 id="编辑器">编辑器</h4><ul><li>✨VS Code</li><li>✅WebStorm</li><li>Atom</li><li>Sublime Text</li><li>HbuilderX</li><li>❌记事本</li><li>在线IDE</li></ul><h4 id="文档笔记">文档笔记</h4><ul><li>✨Markdown</li><li>✨Typora</li><li>在线笔记:<ul><li>语雀</li><li>腾讯文档</li><li>石墨文档</li><li>等等</li></ul></li></ul><h3 id="✨HTML">✨HTML</h3><ul><li>描述：HTML是超文本标记语言，用于创建网页的结构。</li><li>资源：<ul><li><a href="https://developer.mozilla.org/zh-CN/docs/Web/HTML">MDN HTML 文档</a></li></ul></li><li>✨基本语法</li><li>✨标签<ul><li>分区 div</li><li>标题 h1~h6</li><li>段落 p</li><li>超链接 a</li><li>图片 img</li><li>列表 ul/ol</li><li>表格 table</li><li>表单 form</li><li>其他标签 span、strong、em、br、hr等</li></ul></li><li>✅HTML5新特性<ul><li>语义化标签 article、section、nav、aside、header、footer等</li><li>浏览器支持</li><li>多媒体标签 audio、video等</li><li>Canvas 画布</li><li>❌SVG 矢量图</li><li>本地存储<ul><li>localStorage</li><li>sessionStorage</li><li>❌Web SQL</li></ul></li><li>Web Workers</li><li>应用缓存(Cache Manifest)</li><li>无障碍等等</li></ul></li></ul><h3 id="✨CSS">✨CSS</h3><ul><li><p>描述：CSS是层叠样式表，用于描述网页的外观和布局。</p></li><li><p>资源：</p><ul><li><a href="https://developer.mozilla.org/zh-CN/docs/Web/CSS">MDN CSS 文档</a></li></ul></li><li><p>✨基本语法</p></li><li><p>✨引入方式</p><ul><li>行内样式</li><li>内部样式表</li><li>外部样式表</li></ul></li><li><p>✨选择器</p><ul><li>通用选择器 *</li><li>标签选择器</li><li>id选择器 #id</li><li>类选择器 .class</li><li>属性选择器 [attr]</li><li>派生选择器<ul><li>子选择器 &gt;</li><li>后代选择器 “空格”</li><li>相邻兄弟选择器 +</li><li>通用兄弟选择器 ~</li></ul></li><li>组合选择器<ul><li>逗号选择器 ,</li><li>并集选择器</li></ul></li><li>伪类选择器<ul><li>链接伪类 :link</li><li>动态伪类 :active</li><li>静态伪类 :hover</li><li>其他伪类 :first-child、:last-child、:nth-child(n)、:nth-last-child(n)</li><li>选择器优先级<ul><li>内联样式 &gt; ID选择器 &gt; 类选择器 = 属性选择器 = 伪类选择器 &gt; 标签选择器 &gt; 通配符选择器（一般而言）</li></ul></li></ul></li></ul></li><li><p>✨属性</p><ul><li>单位<ul><li>像素 px</li><li>相对单位 em、rem、%</li><li>视口单位 vw、vh</li></ul></li><li>背景</li><li>文本</li><li>字体</li><li>列表</li><li>表格</li></ul></li><li><p>✨文档流</p><ul><li>标准流</li><li>浮动流</li><li>定位流</li></ul></li><li><p>✨内联元素/块级元素/行内块元素</p></li><li><p>✨盒子模型</p><ul><li>content(内容)</li><li>padding(内边距)</li><li>border(边框)</li><li>margin(外边距)</li></ul></li><li><p>✨浮动</p><ul><li>设置浮动 float</li><li>清除浮动 clear</li></ul></li><li><p>✨定位</p><ul><li>静态定位 static</li><li>绝对定位 absolute</li><li>相对定位 relative</li><li>固定定位 fixed</li><li>粘性定位 sticky</li></ul></li><li><p>✨层叠规则</p></li><li><p>BFC和IFC机制</p></li><li><p>CSS3新特性</p><ul><li>盒子阴影 box-shadow</li><li>圆角 border-radius</li><li>渐变</li><li>过渡 transition</li><li>动画 animation</li><li>2D/3D转换 transform</li><li>✨响应式布局<ul><li>媒体查询</li><li>Flex布局</li><li>Grid布局</li><li>瀑布流</li></ul></li></ul></li></ul><h3 id="✨JavaScript">✨JavaScript</h3><p>（就是Java脚本，不过是弱类型语言）,学过一门编程语言很好理解，很快就可以学完</p><ul><li>描述:具有函数优先的轻量级，解释性或即时编译型的编程语言</li><li>资源：<ul><li><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript">MDN JavaScript 文档</a></li><li><a href="https://wangdoc.com/javascript/">wangdoc</a></li></ul></li><li>✨基本语法</li><li>✨数据类型<ul><li>值类型<ul><li>字符串 String</li><li>数字 Number</li><li>布尔值 Boolean</li><li>空值 Null</li><li>未定义 Undefined</li><li>符号 Symbol</li></ul></li><li>引用类型<ul><li>对象 Object</li><li>数组 Array</li><li>函数 Function</li></ul></li></ul></li><li>✅数据类型转换</li><li>✅函数<ul><li>✨概念</li><li>✨自定义函数</li><li>调用方式<ul><li>✨全局调用</li><li>构造函数调用</li><li>✨函数方法调用</li><li>apply() 调用</li><li>call() 调用</li><li>bind() 调用</li></ul></li><li>✨闭包</li></ul></li><li>✨对象<ul><li>概念</li><li>this</li><li>原型链和继承</li><li>常用的对象<ul><li>数组 Array</li><li>字符串 String</li><li>日期 Date</li><li>正则表达式 RegExp</li><li>数学 Math</li><li>全局对象 window</li><li>事件对象 Event</li><li>错误对象 Error</li></ul></li><li>自定义对象</li></ul></li><li>✨作用域(简单记忆：一个大括号就是一个作用域)</li><li>✨BOM API</li><li>✨DOM API</li><li>✨JSON</li><li>✨AJAX</li><li>JavaScript 执行机制</li></ul><p>✨ES6+特性</p><ul><li><p>描述：ES6是ECMAScript 2015的简称，是JavaScript的一个版本，引入了许多新的语法和特性。</p></li><li><p>资源</p><ul><li><a href="https://es6.ruanyifeng.com/">阮一峰的ES6入门</a></li><li><a href="https://wangdoc.com/es6/">官方镜像</a></li><li><a href="https://es6.ruanyifeng.com/#docs/standard">ES6 标准中文文档</a></li></ul></li><li><p>✨let和const</p></li><li><p>✨箭头函数</p></li><li><p>✨模板字符串</p></li><li><p>✨解构赋值</p></li><li><p>✨Symbol</p></li><li><p>✨Set和Map数据结构</p></li><li><p>✨Promise &amp; async/await异步编程</p></li><li><p>✨Proxy代理</p></li><li><p>Generator函数异步编程</p></li></ul><h3 id="✨TypeScript-有时间可以学，直接学框架也行">✨TypeScript(有时间可以学，直接学框架也行)</h3><ul><li>描述：TypeScript是JavaScript的一个超集，添加了<strong>静态类型检查</strong>和其他一些特性。</li><li>资源：<ul><li><a href="https://wangdoc.com/typescript/">TypeScript</a></li></ul></li></ul><h2 id="二、巩固基础（日积月累）">二、巩固基础（日积月累）</h2><p>（这部分内容比较多，建议是日积月累，平时专业课到后面也会学到，追求速度<strong>可以先去学框架</strong>了。但是<strong>✨JavaScript一定要学好，尤其是✨ES6+特性</strong>，不然看不懂后面的框架的）</p><ul><li>描述：学习前端、计算机、软件开发相关基础知识，并且巩固上阶段学的前端三件套。</li><li>📌目标:熟悉前端三件套语法，尤其是JS,并了解互联网、域名、浏览器、服务器等等，我觉得至少要了解这些，开发方向的程序员怎么样都要了解一些，以后从事这个行业少不了和这些打交道</li></ul><h3 id="基础知识-先有个印象">基础知识(先有个印象)</h3><ul><li>✅互联网</li><li>✨协议</li><li>✨域名</li><li>✨服务器</li><li>✨DNS</li><li>✅浏览器（面试重点）<ul><li>浏览器DOM事件流/事件委托</li><li>✨浏览器渲染机制</li><li>✨浏览器加载顺序</li><li>✨浏览器事件循环</li><li>浏览器同源策略</li><li>跨域解决方案</li><li>浏览器缓存机制</li><li>控制台 console.log()</li><li>调试工具（浏览器开发者工具）</li></ul></li><li>✅HTTP<ul><li>HTTP请求</li><li>HTTP协议</li><li>HTTP请求类别</li><li>常见的状态码<ul><li>1xx 信息性状态码</li><li>2xx 成功状态码</li><li>3xx 重定向状态码</li><li>4xx 客户端错误状态码</li><li>5xx 服务器错误状态码</li></ul></li><li>WebSocket</li><li>Cookie</li><li>Session</li><li>HTTPS</li></ul></li></ul><h3 id="计算机基础-其实基本上就是408考研的内容">计算机基础(其实基本上就是408考研的内容)</h3><h4 id="✅数据结构与算法"><strong>✅数据结构与算法</strong></h4><ul><li><p>✨时间、空间复杂度</p></li><li><p>数据结构</p><ul><li><p>✨数组</p></li><li><p>✨链表</p></li><li><p>✨栈</p></li><li><p>✨队列</p></li><li><p>✨树</p></li><li><p>✨图</p></li><li><p>✨哈希表</p></li></ul></li><li><p>算法</p><ul><li><p>✨排序</p></li><li><p>✨双指针</p></li><li><p>✨查找</p></li><li><p>✨分治</p></li><li><p>✨动态规划（DP）</p></li><li><p>✨递归</p></li><li><p>✨回溯</p></li><li><p>✨贪心</p></li><li><p>✨位运算</p></li><li><p>✨DFS（深度优先搜索）</p></li><li><p>✨BFS（广度优先搜索）</p></li><li><p>✨图论</p></li></ul></li></ul><h4 id="✅计算机网络"><strong>✅计算机网络</strong></h4><ul><li><p>✨协议</p></li><li><p>✨IP地址</p></li><li><p>✨端口号</p></li><li><p>✨TCP/UDP</p></li><li><p>✨HTTP/HTTPS</p></li><li><p>✨DNS</p></li><li><p>✨路由</p></li><li><p>✨交换机</p></li><li><p>✨路由器</p></li><li><p>✨防火墙</p></li></ul><h4 id="✅操作系统"><strong>✅操作系统</strong></h4><ul><li><p>进程、线程</p></li><li><p>进程/线程之间的通讯方式</p></li><li><p>进程调度算法</p></li><li><p>进程/线程状态</p></li><li><p>死锁</p></li><li><p>内存管理</p></li></ul><h3 id="软件开发基础（大三软考也会考到）">软件开发基础（大三软考也会考到）</h3><p><strong>✅软件设计模式</strong></p><ul><li><p>单例模式</p></li><li><p>代理模式</p></li><li><p>工厂模式</p></li><li><p>装饰着模式</p></li><li><p>观察者模式</p></li><li><p>策略模式</p></li><li><p>门面模式</p></li></ul><h3 id="✨Git版本控制"><strong>✨Git版本控制</strong></h3><p>这个更是重量级的内容，建议是日积月累，不用去背，用到多查文档，用得多了就记住了。<br>资源：</p><ul><li><p><a href="https://www.runoob.com/git/git-tutorial.html">Git教程</a></p></li><li><p><a href="https://www.runoob.com/git/git-command-reference.html">Git命令参考</a></p></li><li><p>常用的命令</p><ul><li>git init</li><li>git clone</li><li>git add</li><li>git commit</li><li>git push</li><li>git pull</li><li>git branch</li><li>git checkout</li><li>git merge</li></ul></li></ul><h3 id="✅Linux服务器"><strong>✅Linux服务器</strong></h3><ul><li>常用的命令</li></ul><h3 id="✅正则表达式-Java和JavaScript有"><strong>✅正则表达式</strong>(Java和JavaScript有)</h3><h2 id="三、前端工程化">三、前端工程化</h2><ul><li>描述：前端工程化是使用软件工程的技术和方法来进行掐断的开发流程、技术、工具、经验等的规范化和标准化，以提高开发效率、代码质量和可维护性，减少不必要的重复工作时间。</li><li><a href="https://www.zhihu.com/question/433854153/answer/1713597311">看这篇文章，了解什么是前端工程化</a></li><li>目标:至少学会一门主流的前端开发框架（如React、Vue、Angular等），并配合脚手架、组件库、工具等从0开始搭建并开发一个完整的前端网站，可以试着模仿一些知名的站点。要求遵循企业开发规范，将项目代码提交到代码仓库中，斌且独立发布到线上，供他人访问。此外，建议要多找机会组织和参与一些团队开发的项目，如果你将来要从事开发，这几乎是必备的技能，一个人的社交能力在工作还是生活还是学习都是非常重要的。要多加练习。</li></ul><h3 id="研发大致流程">研发大致流程</h3><ol><li>需求分析</li><li>技术选型</li><li>初始化(广义的，包括工作的分配)</li><li>开发</li><li>本地测试</li><li>代码提交</li><li>编译、打包、构建</li><li>部署</li><li>集成测试</li><li>发布上线</li><li>监控运维</li></ol><h3 id="代码托管">代码托管</h3><ul><li>描述:集中存储、备份你的代码，还能和团队成员协作开发。</li><li>✨GitHub<ul><li>这个也是重量级，程序员不会这个，那属实是太难了。</li><li>资源<ul><li><a href="https://github.com/">GitHub官方网站</a></li><li><a href="https://docs.github.com/cn">GitHub官方文档</a></li></ul></li></ul></li><li>Gitee(国内的，和GitHub类似，更快)<ul><li>资源<ul><li><a href="https://gitee.com/">Gitee官方网站</a></li><li><a href="https://gitee.com/help">Gitee官方文档</a></li></ul></li></ul></li><li>GitLab</li></ul><h3 id="✨Node-JS">✨Node.JS</h3><ul><li>描述：一个开源与跨平台的JavaScript运行时环境,非常流行！</li><li>资源<ul><li><a href="https://nodejs.org/">Node.js官方网站</a></li><li><a href="https://nodejs.cn/">Node.js中文网</a></li></ul></li></ul><p><strong>✨包管理</strong></p><ul><li><p>描述：用于安装Node.js的扩展、工具等。</p></li><li><p>✨npm</p></li><li><p>✨yarn</p></li><li><p>✨pnpm</p></li><li><p>❌bower</p></li><li><p>✅npx</p></li></ul><h3 id="✨前端开发框架">✨前端开发框架</h3><ul><li>描述：用于快速构建前端应用的开发框架，提供了组件化、路由、状态管理等功能，帮助开发者更高效地开发前端应用。</li></ul><h4 id="CSS框架">CSS框架</h4><ul><li><p>✅Bootstrap</p></li><li><p>资源</p><ul><li><a href="https://getbootstrap.com/">Bootstrap官方网站</a></li><li><a href="https://v3.bootcss.com/">Bootstrap中文网</a></li></ul></li><li><p>✨Tailwind CSS</p></li><li><p>资源</p><ul><li><a href="https://tailwindcss.com/">Tailwind CSS官方网站</a></li><li><a href="https://tailwindcss.cn/">Tailwind CSS中文网</a></li></ul></li></ul><h4 id="JavaScript框架">JavaScript框架</h4><ul><li><p>✨Vue（大厂，中小厂）</p></li><li><p>资源</p><ul><li><p><a href="https://vuejs.org/">Vue官方网站</a></p></li><li><p><a href="https://cn.vuejs.org/">Vue中文网</a></p></li><li><p>✅Vue 2</p></li><li><p>✨Vue 3</p></li></ul></li><li><p>✨React（大厂）</p></li><li><p>资源</p><ul><li><a href="https://reactjs.org/">React官方网站</a></li><li><a href="https://react.docschina.org/">React中文网</a></li></ul></li><li><p>❌Angular</p></li><li><p>Svelte</p></li><li><p>UmiJS</p></li></ul><h4 id="封装库">封装库</h4><h5 id="✨组件库">✨组件库</h5><ul><li><p>✨Element UI(Vue)</p></li><li><p>资源</p><ul><li><a href="https://element.eleme.cn/#/zh-CN">Element UI官方网站</a></li><li><a href="https://element.eleme.cn/#/zh-CN">Element UI中文网</a></li></ul></li><li><p>✨Ant Design (React)</p></li><li><p>资源</p><ul><li><a href="https://ant.design/">Ant Design官方网站</a></li><li><a href="https://ant.design/index-zh">Ant Design中文网</a></li></ul></li><li><p>✅LayUI</p></li><li><p>✅VantUI(Vue)</p></li><li><p>MaterialUI</p></li></ul><h5 id="数据可视化">数据可视化</h5><ul><li><p>✨ECharts(大屏)</p></li><li><p>Highcharts</p></li><li><p>D3.js</p></li><li><p>✅ AntV</p></li></ul><h5 id="组件-插件">组件(插件)</h5><ul><li>富文本编辑器</li><li>弹窗</li><li>✨轮播图</li></ul><h5 id="工具库">工具库</h5><ul><li><strong>✨Axios()</strong><ul><li>这个非常重要</li><li>资源<ul><li><a href="https://axios-http.com/">Axios官方网站</a></li><li><a href="https://www.axios-http.cn/">Axios中文网</a></li></ul></li></ul></li><li>Lodash</li><li>Moment.js</li><li>Day.js</li><li>时间处理Moment.js</li></ul><h5 id="动效库">动效库</h5><ul><li>✅Animate.css</li><li>Ant Motion</li></ul><h5 id="字体图标库">字体图标库</h5><ul><li>✨Iconfont</li><li>IconPark</li><li>✅FontAwesome</li></ul><h4 id="脚手架">脚手架</h4><ul><li>描述：用于快速生成新项目的目录模板，提升开发效率和开发舒适性</li><li>Vue CLI</li><li>资源<ul><li><a href="https://cli.vuejs.org/">Vue CLI官方网站</a></li><li><a href="https://cli.vuejs.org/zh/">Vue CLI中文网</a></li></ul></li><li>✨Vite（打包工具）</li><li>资源<ul><li><a href="https://vitejs.dev/">Vite官方网站</a></li><li><a href="https://cn.vite.dev/">Vite中文网</a></li></ul></li></ul><h4 id="✨前端架构设计">✨前端架构设计</h4><ul><li>描述：一系列工具和流程的集合，旨在提升前端代码的质量，并实现高效可持续的工作流</li><li>模块化</li><li>组件化</li><li>MVVM架构</li><li>设计原则</li><li>✨SPA单页面应用</li><li>✨多页应用</li><li>✨前端路由</li><li>PWA</li><li>有损服务</li></ul><h4 id="✅服务端渲染">✅服务端渲染</h4><ul><li>描述：在服务器端渲染HTML页面，将动态生成的内容发送给客户端，而不是在客户端使用JavaScript渲染。</li><li>资源<ul><li><a href="https://ssr.vuejs.org/zh/">服务端渲染官方网站</a></li><li><a href="https://ssr.vuejs.org/zh/">服务端渲染中文网</a></li><li><a href="https://www.zhihu.com/question/379563505">服务端渲染-知乎</a></li><li>Next.js(React)</li><li>Nuxt.js(Vue)</li><li>❌Universal(Angular)</li></ul></li></ul><h4 id="✨CSS预编译">✨CSS预编译</h4><ul><li>描述：用新的扩展语言以增强CSS的能力，并通过预编译的方式将其转换为浏览器认识的CSS代码。</li><li>✨Sass</li><li>资源<ul><li><a href="https://sass-lang.com/">Sass官方网站</a></li><li><a href="https://www.sass.hk/">Sass中文网</a></li></ul></li><li>Less</li><li>Stylus</li></ul><h3 id="部署">部署</h3><p>✨Web服务器</p><p>✨Nginx</p><ul><li><p>描述：一个高性能的HTTP服务器和反向代理服务器，常用于部署静态网站和动态应用。</p></li><li><p>资源</p><ul><li><a href="https://www.nginx.com/">Nginx官方网站</a></li><li><a href="https://www.nginx.cn/">Nginx中文网</a></li><li>反向代理</li><li>解决跨域问题</li><li>改写请求头</li><li></li></ul></li></ul><p>Apache</p><p><strong>容器</strong></p><p>Docker</p><ul><li><p>描述：容器是一个标准化软件单元，将代码及其所有依赖打包在一起，以便于在任何环境中运行。Docker容器镜像是一个轻量的独立的可执行包，包含运行应用程序所需的所有内容，包括代码、运行时、系统工具、系统库和设置。</p></li><li><p>资源</p><ul><li><p><a href="https://www.docker.com/">Docker官方网站</a></p></li><li><p><a href="https://www.docker.com.cn/">Docker中文网</a></p></li><li><p><a href="https://vuepress.mirror.docker-practice.com/">Docker从入门到实践</a></p></li></ul></li></ul><p>Kubernetes</p><h2 id="四、前端优化">四、前端优化</h2><ul><li><p>描述：通过优化前端代码和资源，提升页面加载速度、用户体验和搜索引擎排名。</p></li><li><p>目标：实践前端优化方法，优化自己做过的项目</p></li></ul><p><strong>待更新</strong></p><h2 id="五、前端生态">五、前端生态</h2><ul><li>描述：前端技的领域越来越广，有非常多前端工程师需要了解和关注的技术。</li><li>目标：用用学到的技术，做更多事情，如开发文档网站，小程序,APP等等。</li></ul><p><strong>待更新</strong></p><h2 id="六、前端求职">六、前端求职</h2><p><strong>待更新</strong></p><h2 id="七、前端未来">七、前端未来</h2><ul><li>描述：前端技术在不断发展，有很多新的技术和工具出现。前端工程师需要保持学习和跟进，以跟上技术的发展。</li><li>目标：关注前端技术的最新动态，学习新的技术和工具，提升自己的技能。</li></ul><blockquote><p><strong>未来的路一定充满光明</strong></p></blockquote><style>.ntofjolhyyqg{zoom: 50%;}</style><img src="/posts/e5cf9037/2.jpg" class="ntofjolhyyqg" alt="致敬自由">]]>
    </content>
    <id>https://www.furinaluna.top/posts/e5cf9037.html</id>
    <link href="https://www.furinaluna.top/posts/e5cf9037.html"/>
    <published>2025-11-20T06:37:38.000Z</published>
    <summary>
      <![CDATA[<ul>
<li>✨必学</li>
<li>✅推荐</li>
<li>❌不推荐</li>
</ul>
<h2 id="大纲">大纲</h2>
<ul>
<li>
<p>1.前言 - 学编程需要的特质</p>
</li>
<li>
<p>2.前端学习七阶段</p>
<ul>
<li]]>
    </summary>
    <title>前端学习路线(从入门到入土)</title>
    <updated>2026-09-11T13:48:57.171Z</updated>
  </entry>
  <entry>
    <author>
      <name>Furina</name>
    </author>
    <category term="算法" scheme="https://www.furinaluna.top/categories/%E7%AE%97%E6%B3%95/"/>
    <category term="算法 二分查找 三分查找" scheme="https://www.furinaluna.top/tags/%E7%AE%97%E6%B3%95-%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE-%E4%B8%89%E5%88%86%E6%9F%A5%E6%89%BE/"/>
    <content>
      <![CDATA[<p><strong>有序数列查找特定大小数的算法</strong>：二分查找（Binary Search）<br><strong>凸函数或者凹函数寻找极值的算法</strong>：三分查找（Ternary Search）</p><h2 id="二分查找">二分查找</h2><p><strong>分析</strong><br>以递增数组为例，将其分为两半。若中间值大于要找的数，说明要找的数在左边一半；若中间值小于要找的数，说明要找的数在右边一半。不断将范围缩小一半，直到找到要找的数。</p><p><strong>时间复杂度</strong>：O(log n)<br>（n为数据规模）</p><h3 id="代码实现">代码实现</h3><p><strong>整数</strong></p><ul><li>查找 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo>≥</mo></mrow><annotation encoding="application/x-tex">\geq</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7719em;vertical-align:-0.136em;"></span><span class="mrel">≥</span></span></span></span> x的第一个数：<br>（将 if (a[mid] <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo>≥</mo></mrow><annotation encoding="application/x-tex">\geq</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7719em;vertical-align:-0.136em;"></span><span class="mrel">≥</span></span></span></span> x) 改为 if (a[mid] &gt; x) ，可查找 &gt; x的第一个数）</li></ul><figure class="highlight arcade"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs arcade"><span class="hljs-comment">// a[ ]为储存数据的有序递增数组</span><br><span class="hljs-comment">// l ~ r为二分查找的数组范围</span><br>int l = <span class="hljs-number">0</span>, r = n - <span class="hljs-number">1</span>;<br><span class="hljs-keyword">while</span> (l &lt; r)<br>&#123;<br>    int <span class="hljs-built_in">mid</span> = (l + r) / <span class="hljs-number">2</span>;<br>    <span class="hljs-keyword">if</span> (a[<span class="hljs-built_in">mid</span>] &gt;= x)<br>        r = <span class="hljs-built_in">mid</span>;<br>    <span class="hljs-keyword">else</span><br>        l = <span class="hljs-built_in">mid</span> + <span class="hljs-number">1</span>;<br>&#125;<br></code></pre></td></tr></table></figure><ul><li>查找 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo>≤</mo></mrow><annotation encoding="application/x-tex">\leq</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7719em;vertical-align:-0.136em;"></span><span class="mrel">≤</span></span></span></span> x的最后一个数：<br>（将 if (a[mid] <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo>≤</mo></mrow><annotation encoding="application/x-tex">\leq</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7719em;vertical-align:-0.136em;"></span><span class="mrel">≤</span></span></span></span> x) 改为 if (a[mid] &lt; x) ，查找 &lt; x的最后一个数）</li></ul><p>易错点：此时 mid为（l+r+1）/2，若忘记 “+1” ，将会死循环。</p><figure class="highlight arcade"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs arcade"><span class="hljs-comment">// a[ ]为储存数据的有序递增数组</span><br><span class="hljs-comment">// l ~ r为二分查找的数组范围</span><br>int l = <span class="hljs-number">0</span>, r = n - <span class="hljs-number">1</span>;<br><span class="hljs-keyword">while</span> (l &lt; r)<br>&#123;<br>    int <span class="hljs-built_in">mid</span> = (l + r + <span class="hljs-number">1</span>) / <span class="hljs-number">2</span>;<br>    <span class="hljs-keyword">if</span> (a[<span class="hljs-built_in">mid</span>] &lt;= x)<br>        l = <span class="hljs-built_in">mid</span>;<br>    <span class="hljs-keyword">else</span><br>        r = <span class="hljs-built_in">mid</span> - <span class="hljs-number">1</span>;<br>&#125;<br></code></pre></td></tr></table></figure><p><strong>实数</strong><br>查找满足一定条件的实数：（即以下代码令 check(x) == true 的 x）</p><figure class="highlight pgsql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs pgsql">// <span class="hljs-keyword">check</span>(<span class="hljs-type">int</span> x): 判断x是否满足条件<br>// eps: 精度（因为浮点数误差，不可直接比大小）<br>// <span class="hljs-keyword">BEGIN</span>: 查找左边界<br>// <span class="hljs-keyword">END</span>: 查找右边界<br><span class="hljs-type">bool</span> <span class="hljs-keyword">check</span>(<span class="hljs-type">int</span> x);<br><span class="hljs-type">double</span> eps = <span class="hljs-number">1e-6</span>;<br><span class="hljs-type">double</span> l = <span class="hljs-keyword">BEGIN</span>, r = <span class="hljs-keyword">END</span>;<br><span class="hljs-keyword">while</span> (r - l &gt; eps)<br>&#123;<br>    <span class="hljs-type">double</span> mid = (l + r) / <span class="hljs-number">2</span>;<br>    <span class="hljs-keyword">if</span> (<span class="hljs-keyword">check</span>(mid))<br>        l = mid;<br>    <span class="hljs-keyword">else</span><br>        r = mid;<br>&#125;   <br></code></pre></td></tr></table></figure><hr><h2 id="三分查找">三分查找</h2><hr><p><strong>分析</strong><br>将数组分为三份，分别对应数组的左中右三个位置。若左中右三个位置的函数值在同一侧，则将数组缩小到左中右三个位置；若左中右三个位置的函数值在不同侧，则将数组缩小到中间两个位置。不断缩小数组范围，直到找到极值。</p><p><strong>时间复杂度</strong>：O(log n)<br>（n为数据规模）</p><h3 id="代码实现-2">代码实现</h3><figure class="highlight arduino"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs arduino"><span class="hljs-comment">// f(double x): 给定区间内的凹函数或凸函数</span><br><span class="hljs-comment">// eps: 精度（因为浮点数误差，不可直接比大小）</span><br><span class="hljs-comment">// BEGIN: 查找左边界</span><br><span class="hljs-comment">// END: 查找右边界</span><br><span class="hljs-function"><span class="hljs-type">double</span> <span class="hljs-title">f</span><span class="hljs-params">(<span class="hljs-type">double</span> x)</span></span>;<br><span class="hljs-type">double</span> eps = <span class="hljs-number">1e-6</span>;<br><span class="hljs-type">double</span> l = BEGIN, r = END;<br><span class="hljs-keyword">while</span>(r - l &gt; eps)<br>&#123;<br>    <span class="hljs-type">double</span> m1 = l + (r - l) / <span class="hljs-number">3</span>, m2 = r - (r - l) / <span class="hljs-number">3</span>;<br>    <span class="hljs-keyword">if</span>(<span class="hljs-built_in">f</span>(m1) &gt; <span class="hljs-built_in">f</span>(m2)) <span class="hljs-comment">// 此为找最小值，若要找最大值，则改为&lt;</span><br>        l = m1;<br>    <span class="hljs-keyword">else</span><br>        r = m2;<br>&#125;   <br></code></pre></td></tr></table></figure>]]>
    </content>
    <id>https://www.furinaluna.top/posts/f0faee69.html</id>
    <link href="https://www.furinaluna.top/posts/f0faee69.html"/>
    <published>2025-11-17T04:44:59.000Z</published>
    <summary>
      <![CDATA[<p><strong>有序数列查找特定大小数的算法</strong>：二分查找（Binary Search）<br>
<strong>凸函数或者凹函数寻找极值的算法</strong>：三分查找（Ternary Search）</p>
<h2 id="二分查找">二分查找</h2>]]>
    </summary>
    <title>二分查找、三分查找</title>
    <updated>2026-09-11T13:48:57.151Z</updated>
  </entry>
  <entry>
    <author>
      <name>Furina</name>
    </author>
    <category term="算法" scheme="https://www.furinaluna.top/categories/%E7%AE%97%E6%B3%95/"/>
    <category term="算法 快速幂" scheme="https://www.furinaluna.top/tags/%E7%AE%97%E6%B3%95-%E5%BF%AB%E9%80%9F%E5%B9%82/"/>
    <content>
      <![CDATA[<p><strong>提升效率的快速幂运算</strong></p><h2 id="快速幂">快速幂</h2><h2 id="时间复杂度-O-log-2-n">时间复杂度 O(log_2 n)</h2><p>(n为幂次)</p><h2 id="代码实现">代码实现</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-type">long</span> <span class="hljs-type">long</span> <span class="hljs-title function_">fastpow</span><span class="hljs-params">(<span class="hljs-type">long</span> <span class="hljs-type">long</span> a, <span class="hljs-type">long</span> <span class="hljs-type">long</span> b)</span><br>&#123;<br>    <span class="hljs-type">long</span> <span class="hljs-type">long</span> <span class="hljs-variable">ans</span> <span class="hljs-operator">=</span> <span class="hljs-number">1</span>;<br>    <span class="hljs-keyword">while</span> (b)<br>    &#123;<br>        <span class="hljs-keyword">if</span> (b % <span class="hljs-number">2</span> == <span class="hljs-number">1</span>)<br>            ans = ans * a;<br>        a = a * a;<br>        b &gt;&gt;= <span class="hljs-number">1</span>;<br>    &#125;<br>    <span class="hljs-keyword">return</span> ans;<br>&#125;<br></code></pre></td></tr></table></figure><h2 id="变体-大数取模">变体-大数取模</h2><p>一般能用到快速幂算法的，答案都会非常巨大，肯定是超出已有数据类型范围的，所以题目一般会说答案对某个数字取模。快速幂可以完成这个操作，只需要进行很小的修改。</p><h2 id="时间复杂度-O-log-2-n-2">时间复杂度 O(log_2 n)</h2><p>(n为幂次)</p><h2 id="代码实现-2">代码实现</h2><figure class="highlight axapta"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs axapta"><span class="hljs-built_in">long</span> <span class="hljs-built_in">long</span> fastpow(<span class="hljs-built_in">long</span> <span class="hljs-built_in">long</span> a, <span class="hljs-built_in">long</span> <span class="hljs-built_in">long</span> b, <span class="hljs-built_in">long</span> <span class="hljs-built_in">long</span> <span class="hljs-keyword">mod</span>)<br>&#123;<br>    a %= <span class="hljs-keyword">mod</span>;<span class="hljs-comment">//开头先取一次模</span><br>    <span class="hljs-built_in">long</span> <span class="hljs-built_in">long</span> ans = <span class="hljs-number">1</span>;<br>    <span class="hljs-keyword">while</span> (b)<br>    &#123;<br>        <span class="hljs-keyword">if</span> (b % <span class="hljs-number">2</span> == <span class="hljs-number">1</span>)<br>            ans = ans * a % <span class="hljs-keyword">mod</span>;<span class="hljs-comment">//每次运算都取模</span><br>        a = a * a % <span class="hljs-keyword">mod</span>;<span class="hljs-comment">//每次运算都取模</span><br>        b &gt;&gt;= <span class="hljs-number">1</span>;<br>    &#125;<br>    <span class="hljs-keyword">return</span> ans;<br>&#125;<br></code></pre></td></tr></table></figure><h2 id="例题">例题</h2><p><a href="https://www.luogu.com.cn/problem/P1226">P1226 【模板】快速幂||取余运算</a></p>]]>
    </content>
    <id>https://www.furinaluna.top/posts/716f5d8f.html</id>
    <link href="https://www.furinaluna.top/posts/716f5d8f.html"/>
    <published>2025-11-17T01:41:17.000Z</published>
    <summary>
      <![CDATA[<p><strong>提升效率的快速幂运算</strong></p>
<h2 id="快速幂">快速幂</h2>
<h2 id="时间复杂度-O-log-2-n">时间复杂度 O(log_2 n)</h2>
<p>(n为幂次)</p>
<h2 id="代码实现">代码实现</h2]]>
    </summary>
    <title>快速幂运算</title>
    <updated>2026-09-11T13:48:57.195Z</updated>
  </entry>
  <entry>
    <author>
      <name>Furina</name>
    </author>
    <category term="算法" scheme="https://www.furinaluna.top/categories/%E7%AE%97%E6%B3%95/"/>
    <category term="C++, STL, 语法" scheme="https://www.furinaluna.top/tags/C-STL-%E8%AF%AD%E6%B3%95/"/>
    <content>
      <![CDATA[<p>C++ 标准模板库 (STL, Standard Template Library)：包含一些常用数据结构与算法的模板的 C++ 软件库。其包含四个组件——算法 (Algorithms)、容器 (Containers)、仿函数 (Functors)、迭代器 (Iterators).</p><hr><p>示例：<br>算法：sort(a.begin(), a.end())<br>容器：priority_queue<int> pque<br>仿函数：greater<int>()<br>迭代器：vector<int>::iterator it = a.begin()</int></int></int></p><hr><h2 id="1-前言">1 前言</h2><p>STL 作为一个封装良好，性能合格的 C++ 标准库，在算法竞赛中运用极其常见。灵活且正确使用 STL 可以节省非常多解题时间，这一点不仅是由于可以直接调用，还是因为它封装良好，可以让代码的可读性变高，解题思路更清晰，调试过程 往往 更顺利。</p><p>不过 STL 毕竟使用了很多复杂的结构来实现丰富的功能，它的效率往往是比不上自己手搓针对特定题目的数据结构与算法的。因此，STL 的使用相当于使用更长的运行时间换取更高的编程效率。因此，在实际比赛中要权衡 STL 的利弊，不过这一点就得靠经验了。<br>接下来，我会分享在算法竞赛中常用的 STL 容器和算法，对于函数和迭代器，就不着重展开讲了。</p><h2 id="2-常用容器">2 常用容器</h2><h3 id="2-1-内容总览">2.1 内容总览</h3><p>打勾的是本次将会详细讲解的，加粗的是算法竞赛中有必要学习的。</p><h4 id="2-1-1顺序容器">2.1.1顺序容器</h4><p><strong>[ ] array</strong><br><strong>[x] vector</strong><br><strong>[ ] deque</strong><br>[ ] forward_list<br>[ ] list</p><h4 id="2-1-2-关联容器">2.1.2 关联容器</h4><p><strong>[x] set</strong><br><strong>[x] map</strong><br>[ ] multiset<br>[ ] multimap</p><h4 id="2-1-3-无序关联容器">2.1.3 无序关联容器</h4><p><strong>[ ] unordered_set</strong><br><strong>[ ] unordered_map</strong><br><strong>[ ] unordered_multiset</strong><br><strong>[ ] unordered_multimap</strong></p><h4 id="2-1-4-容器适配器">2.1.4 容器适配器</h4><p><strong>[x] stack</strong><br><strong>[x] queue</strong><br><strong>[x] priority_queue</strong><br>[ ] flat_set<br>[ ] flat_map<br>[ ] flat_multiset<br>[ ] flat_multimap</p><h4 id="2-1-5-字符串">2.1.5 字符串</h4><p><strong>[x] string (basic_string&lt;char&gt;)</strong></p><h4 id="2-1-6-对与元组">2.1.6 对与元组</h4><p><strong>[x] pair</strong><br>[ ] tuple</p><h3 id="2-2-向量-vector">2.2 向量 vector</h3><p><strong>#include &lt;vector&gt;</strong><br>连续的顺序的储存结构（和数组一样的类别），但是有长度可变的特性。</p><h4 id="2-2-1-常用方法">2.2.1 常用方法</h4><p><strong>构造</strong><br><code>vector&lt;类型&gt; arr(长度, [初值])</code><br>时间复杂度：O(n)：<br>常用的一维和二维数组构造示例，高维也是一样的（就是会有点长）.</p><figure class="highlight plaintext"><figcaption><span>arr;</span><a href="//">构造int数组</a></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs vector<int>">vector&lt;int&gt; arr(100);    // 构造初始长100的int数组<br>vector&lt;int&gt; arr(100, 1); // 构造初始长100的int数组，初值为1<br>vector&lt;vector&lt;int&gt;&gt; mat(100, vector&lt;int&gt; ());       // 构造初始100行，不指定列数的二维数组<br>vector&lt;vector&lt;int&gt;&gt; mat(100, vector&lt;int&gt; (666, -1)) // 构造初始100行，初始666列的二维数组，初值为-1<br></code></pre></td></tr></table></figure><p>构造二维数组的奇葩写法，千万别用：</p><figure class="highlight apache"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs apache"><span class="hljs-attribute">vector</span>&lt;int&gt; arr[<span class="hljs-number">100</span>];         // 正确，构造初始<span class="hljs-number">100</span>行，不指定列数的二维数组，可用于链式前向星存图<br><span class="hljs-attribute">vector</span>&lt;int&gt; arr[<span class="hljs-number">100</span>](<span class="hljs-number">100</span>, <span class="hljs-number">1</span>); // 语法错误！<br><span class="hljs-attribute">vector</span>&lt;int&gt; arr(<span class="hljs-number">100</span>, <span class="hljs-number">1</span>)[<span class="hljs-number">100</span>]; // 语法错误！<br><span class="hljs-attribute">vector</span>&lt;int&gt; arr[<span class="hljs-number">100</span>] &#123;&#123;<span class="hljs-number">100</span>, <span class="hljs-number">1</span>&#125;, 这里省略<span class="hljs-number">98</span>个 ,&#123;<span class="hljs-number">100</span>, <span class="hljs-number">1</span>&#125;&#125;; // 正确但奇葩，使用列表初始化<br></code></pre></td></tr></table></figure><p><strong>尾接 &amp; 尾删</strong></p><ul><li>.push_back(元素)：在 vector 尾接一个元素，数组长度 .</li><li>.pop_back()：删除 vector 尾部的一个元素，数组长度<br>时间复杂度：均摊 O(1)</li></ul><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs scss"><span class="hljs-comment">// init: arr = []</span><br>arr<span class="hljs-selector-class">.push_back</span>(<span class="hljs-number">1</span>);<br><span class="hljs-comment">// after: arr = [1]</span><br>arr<span class="hljs-selector-class">.push_back</span>(<span class="hljs-number">2</span>);<br><span class="hljs-comment">// after: arr = [1, 2]</span><br>arr<span class="hljs-selector-class">.pop_back</span>();<br><span class="hljs-comment">// after: arr = [1]</span><br>arr<span class="hljs-selector-class">.pop_back</span>();<br><span class="hljs-comment">// after: arr = []</span><br></code></pre></td></tr></table></figure><p>中括号运算符<br>和一般数组一样的作用<br>时间复杂度：O(1)</p><p><strong>获取长度</strong><br><code>.size()</code><br>获取当前 vector 的长度<br>时间复杂度：O(1)</p><figure class="highlight abnf"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs abnf">for (int i <span class="hljs-operator">=</span> <span class="hljs-number">0</span><span class="hljs-comment">; i &lt; arr.size(); i++)</span><br>    cout &lt;&lt; a[i] &lt;&lt; endl<span class="hljs-comment">;</span><br></code></pre></td></tr></table></figure><p><strong>清空</strong><br><code>.clear()</code><br>清空 vector<br>时间复杂度：O(n)</p><p><strong>判空</strong><br><code>.empty()</code><br>如果是空返回 <strong>true</strong> 反之返回 <strong>false</strong>.<br>时间复杂度：O(1)</p><p><strong>改变长度</strong><br><code>.resize(新长度, [默认值])</code><br>修改 vector 的长度</p><ul><li>如果是缩短，则删除多余的值</li><li>如果是扩大，且指定了默认值，则新元素均为默认值 <strong>(旧元素不变)</strong><br>时间复杂度：O(n)</li></ul><h4 id="2-2-2-适用情形">2.2.2 适用情形</h4><p>一般情况 vector 可以替换掉普通数组，除非该题卡常。有些情况普通数组没法解决：n×m的矩阵，1&lt;=n,m&lt;=10^6^，且 n×m&lt;=10^6^.</p><p>如果用普通数组<code> int mat[1000010][1000010]</code>，浪费内存，会导致 MLE。<br>如果使用 <code>vector&lt;vector&lt;int&gt;&gt; mat(n + 10, vector&lt;int&gt; (m + 10))</code>，完美解决该问题。<br>另外，<code>vector</code>的数据储存在堆空间中，不会爆栈。</p><h4 id="2-2-3-注意事项">2.2.3 注意事项</h4><p><strong>提前指定长度</strong><br>如果长度已经确定，那么应当直接在构造函数指定长度，而不是一个一个 <code>.push_back()</code>. 因为 <code>vector</code> 额外内存耗尽后的重分配是有时间开销的，直接指定长度就不会出现重分配了。</p><figure class="highlight plaintext"><figcaption><span>优化前: 522ms</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs //">vector&lt;int&gt; a;<br>for (int i = 0; i &lt; 1e8; i++)<br>    a.push_back(i);<br>// 优化后: 259ms<br>vector&lt;int&gt; a(1e8);<br>for (int i = 0; i &lt; a.size(); i++)<br>    a[i] = i;<br></code></pre></td></tr></table></figure><p><strong>当心 size_t 溢出</strong><br>vector 获取长度的方法 <code>.size() </code>返回值类型为<code> size_t</code>，通常 OJ 平台使用的是 32 位编译器（有些平台例如 cf 可选 64 位），那么该类型范围为 [0 , 2^32^)</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-function">vector&lt;<span class="hljs-type">int</span>&gt; <span class="hljs-title">a</span><span class="hljs-params">(<span class="hljs-number">65536</span>)</span></span>;<br><span class="hljs-type">long</span> <span class="hljs-type">long</span> a = a.<span class="hljs-built_in">size</span>() * a.<span class="hljs-built_in">size</span>(); <span class="hljs-comment">// 直接溢出变成0了</span><br></code></pre></td></tr></table></figure><h3 id="2-3-栈-stack">2.3 栈 stack</h3><p><strong>#include &lt;stack&gt;</strong><br>通过二次封装双端队列 (deque) 容器，实现先进后出的栈数据结构。</p><h4 id="2-3-1-常用方法">2.3.1 常用方法</h4><table><thead><tr><th>作用</th><th>用法</th><th>示例</th></tr></thead><tbody><tr><td>构造</td><td><code>stack&lt;类型&gt; stk</code></td><td><code>stack&lt;int&gt; stk;</code></td></tr><tr><td>进栈</td><td><code>.push(元素)</code></td><td><code>stk.push(1);</code></td></tr><tr><td>出栈</td><td><code>.pop()</code></td><td><code>stk.pop();</code></td></tr><tr><td>取栈顶</td><td><code>.top()</code></td><td><code>int a = stk.top();</code></td></tr><tr><td>查看大小/清空/判空</td><td><code>.size()</code>/<code>.clear()</code>/<code>.empty()</code></td><td>略</td></tr></tbody></table><h4 id="2-3-2-适用情形">2.3.2 适用情形</h4><p>如果不卡常的话，就可以直接用它而不需要手写栈了。另外，<code>vector</code>也可以当栈用：</p><ul><li><code>vector.back()</code> 取尾部元素 → 栈顶</li><li><code>vector.push_back()</code> → 进栈</li><li><code>vector.pop_back()</code> → 出栈</li></ul><h4 id="2-3-3-注意事项">2.3.3 注意事项</h4><p>不可访问内部元素！<strong>下面是错误用法</strong></p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><br><span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i &lt; stk.<span class="hljs-built_in">size</span>(); i++)<br>    cout &lt;&lt; stk[i] &lt;&lt; endl;<br><span class="hljs-keyword">for</span> (<span class="hljs-keyword">auto</span> ele : stk)<br>    cout &lt;&lt; ele &lt;&lt; endl;<br></code></pre></td></tr></table></figure><h3 id="2-4-队列-queue">2.4 队列 queue</h3><p><strong>#include &lt;queue&gt;</strong><br>通过二次封装双端队列 (deque) 容器，实现先进先出的队列数据结构。</p><h4 id="2-4-1-常用方法">2.4.1 常用方法</h4><table><thead><tr><th>作用</th><th>用法</th><th>示例</th></tr></thead><tbody><tr><td>构造</td><td>queue&lt;类型&gt; que</td><td>queue<int> que;</int></td></tr><tr><td>进队</td><td>.push(元素)</td><td>que.push(1);</td></tr><tr><td>出队</td><td>.pop()</td><td>que.pop();</td></tr><tr><td>取队首</td><td>.front()</td><td>int a = que.front()</td></tr><tr><td>取队尾</td><td>.back()</td><td>int a = que.back()</td></tr></tbody></table><h4 id="2-4-2-适用情形">2.4.2 适用情形</h4><p>如果不卡常的话，就可以直接用它而不需要手写队列了。</p><h4 id="2-4-3-注意事项">2.4.3 注意事项</h4><p>不可访问内部元素！下面都是错误用法</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i &lt; que.<span class="hljs-built_in">size</span>(); i++)<br>    cout &lt;&lt; que[i] &lt;&lt; endl;<br><br><span class="hljs-keyword">for</span> (<span class="hljs-keyword">auto</span> ele : que)<br>    cout &lt;&lt; ele &lt;&lt; endl;<br></code></pre></td></tr></table></figure><h3 id="2-5-优先队列-priority-queue">2.5 优先队列 priority_queue</h3><p><strong>#include &lt;queue&gt;</strong><br>提供常数时间的最大元素查找，对数时间的插入与提取，底层原理是二叉堆。</p><h4 id="2-5-1-常用方法">2.5.1 常用方法</h4><p><strong>构造</strong><br>构造<code>priority_queue&lt;类型, 容器, 比较器&gt; pque</code></p><ul><li>类型：要储存的数据类型</li><li>容器：储存数据的底层容器，默认为<code>vector&lt;类型&gt;</code>，竞赛中保持默认即可</li><li>比较器：比较大小使用的比较器，默认为 <code>less&lt;类型&gt;</code>，可自定义</li></ul><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs cpp">priority_queue&lt;<span class="hljs-type">int</span>&gt;pque1;     <span class="hljs-comment">// 储存int的大顶堆</span><br>priority_queue&lt;<span class="hljs-type">int</span>, vector&lt;<span class="hljs-type">int</span>&gt;, greater&lt;<span class="hljs-type">int</span>&gt;&gt; pque2; <span class="hljs-comment">// 储存int的小顶堆</span><br></code></pre></td></tr></table></figure><blockquote><p>对于需要自定义比较器的情况，涉及一些初学时容易看迷糊的语法（重载小括号运算符 / lambda 表达式），在此就不展开讲了。如果想要了解，可以查阅 cppreference 中的代码示例。</p></blockquote><p><strong>其他</strong></p><table><thead><tr><th>作用</th><th>用法</th><th>示例</th></tr></thead><tbody><tr><td>进堆</td><td>.push(元素)</td><td>que.push(1);</td></tr><tr><td>出堆</td><td>.pop()</td><td>que.pop();</td></tr><tr><td>取堆顶</td><td>.top()</td><td>int a = que.top();</td></tr><tr><td>查看大小 / 判空</td><td>略</td><td>略</td></tr></tbody></table><p>进出队复杂度为 O(logn),取堆顶复杂度为 O(1)</p><h4 id="2-5-2-适用情形">2.5.2 适用情形</h4><p>持续维护元素的有序性：每次向队列插入大小不定的元素，或者每次从队列里取出大小最小/最大的元素，元素数量 n<br>，插入操作数量 k</p><ul><li>每次插入后进行快速排序：k*nlogn</li><li>使用优先队列维护：k*logn</li></ul><h4 id="2-5-3-注意事项">2.5.3 注意事项</h4><p><strong>仅堆顶可读</strong>,只可访问堆顶，其他元素都无法读取到。<strong>下面是错误用法</strong><br><code>cout &lt;&lt; pque[1] &lt;&lt; endl;</code><br><strong>所有元素不可写</strong><br>堆中所有元素是不可修改的。<strong>下面是错误用法</strong>：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs pque[1]">pque.top() = 1;<br></code></pre></td></tr></table></figure><p>如果你恰好要修改的是堆顶元素，那么是可以完成的：</p><figure class="highlight gradle"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs gradle"><span class="hljs-keyword">int</span> tp = pque.top();<br>pque.<span class="hljs-keyword">pop</span>();<br>pque.<span class="hljs-keyword">push</span>(tp + <span class="hljs-number">1</span>);<br></code></pre></td></tr></table></figure><h3 id="2-6-集合-set">2.6 集合 set</h3><p><strong>#include &lt;set&gt;</strong><br>提供对数时间的插入、删除、查找的集合数据结构。底层原理是红黑树。</p><table><thead><tr><th>集合三要素</th><th>解释</th><th>set</th><th>multiset</th><th>unordered_set</th></tr></thead><tbody><tr><td>确定性</td><td>一个元素要么在集合中，要么不在</td><td>✔</td><td>✔</td><td>✔</td></tr><tr><td>互异性</td><td>一个元素仅可以在集合中出现一次</td><td>✔</td><td>❌（任意次）</td><td>✔</td></tr><tr><td>无序性</td><td>集合中的元素是没有顺序的</td><td>❌（从小到大）</td><td>❌（从小到大）</td><td>✔</td></tr></tbody></table><h4 id="2-6-1-常用方法">2.6.1 常用方法</h4><p><strong>构造</strong><br><code>set&lt;类型, 比较器&gt; st</code></p><ul><li>类型：要储存的数据类型</li><li>比较器：比较大小使用的比较器，默认为 <code>less&lt;类型&gt;</code>，可自定义</li></ul><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs cpp">set&lt;<span class="hljs-type">int</span>&gt; st1;               <span class="hljs-comment">// 储存int的集合（从小到大）</span><br>set&lt;<span class="hljs-type">int</span>, greater&lt;<span class="hljs-type">int</span>&gt;&gt; st2; <span class="hljs-comment">// 储存int的集合（从大到小）</span><br></code></pre></td></tr></table></figure><blockquote><p>对于需要自定义比较器的情况，涉及一些初学时容易看迷糊的语法（重载小括号运算符 / lambda 表达式），在此就不展开讲了。</p></blockquote><p><strong>遍历</strong><br>可使用迭代器进行遍历：</p><figure class="highlight applescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs applescript"><span class="hljs-keyword">for</span> (<span class="hljs-keyword">set</span>&lt;int&gt;::iterator <span class="hljs-keyword">it</span> = st.begin(); <span class="hljs-keyword">it</span> != st.<span class="hljs-keyword">end</span>(); ++<span class="hljs-keyword">it</span>)<br>    cout &lt;&lt; *<span class="hljs-keyword">it</span> &lt;&lt; endl;<br></code></pre></td></tr></table></figure><p>基于范围的循环（C++ 11）：</p><figure class="highlight lisp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs lisp">for (<span class="hljs-name">auto</span> <span class="hljs-symbol">&amp;ele</span> : st)<br>    cout &lt;&lt; ele &lt;&lt; endl<span class="hljs-comment">;</span><br></code></pre></td></tr></table></figure><p><strong>其他</strong></p><table><thead><tr><th>作用</th><th>用法</th><th>示例</th></tr></thead><tbody><tr><td>插入元素</td><td>.insert(元素)</td><td>st.insert(1);</td></tr><tr><td>删除元素</td><td>.erase(元素)</td><td>st.erase(2);</td></tr><tr><td>查找元素</td><td>.find(元素)</td><td>auto it = st.find(1);</td></tr><tr><td>判断元素是否存在</td><td>.count(元素)</td><td>st.count(3);</td></tr><tr><td>查看大小 / 清空 / 判空</td><td>略</td><td>略</td></tr></tbody></table><p>增删查时间复杂度均为 O(logn)</p><h4 id="2-6-2-适用情形">2.6.2 适用情形</h4><ul><li>元素去重：[1,1,3,2,4,4]-&gt;[1,2,3,4]</li><li>维护顺序：[1,3,7,2,5,4,6]-&gt;[1,2,3,4,5,6,7]</li><li>元素是否出现过：元素大小 [-10^18^,10^18^]，元素数量 10^6^，vis 数组无法实现，通过 set 可以完成。</li></ul><h4 id="2-6-3-注意事项">2.6.3 注意事项</h4><p>不存在下标索引**<br>set 虽说可遍历，但仅可使用迭代器进行遍历，它不存在下标这一概念，无法通过下标访问到数据。<strong>下面是错误用法</strong>：<br><code>cout &lt;&lt; st[0] &lt;&lt; endl;</code></p><p><strong>元素只读</strong><br>set 的迭代器取到的元素是只读的（因为是 const 迭代器），不可修改其值。如果要改，需要先 erase 再 insert. 下面是<strong>错误用法</strong>：</p><figure class="highlight delphi"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs delphi">cout &lt;&lt; *st.<span class="hljs-keyword">begin</span>() &lt;&lt; endl; <span class="hljs-comment">// 正确。可读。</span><br>*st.<span class="hljs-keyword">begin</span>() = <span class="hljs-number">1</span>;             <span class="hljs-comment">// 错误！不可写！</span><br></code></pre></td></tr></table></figure><p><strong>不可用迭代器计算下标</strong><br>set 的迭代器不能像 vector 一样相减得到下标。下面是错误用法：</p><figure class="highlight abnf"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs abnf">auto it <span class="hljs-operator">=</span> st.find(<span class="hljs-number">2</span>)<span class="hljs-comment">;      // 正确，返回2所在位置的迭代器。</span><br>int idx <span class="hljs-operator">=</span> it - st.begin()<span class="hljs-comment">; // 错误！不可相减得到下标。</span><br></code></pre></td></tr></table></figure><h3 id="2-7-映射-map">2.7 映射 map</h3><p><strong>#include &lt;map&gt;</strong></p><p>提供对数时间的有序键值对结构。底层原理是红黑树。<br>映射：键值对的集合，键是唯一的，值可以重复。<br>例如：<br>1-&gt;2<br>2-&gt;3<br>3-&gt;4</p><table><thead><tr><th>性质</th><th>解释</th><th>map</th><th>multimap</th><th>unordered_map</th></tr></thead><tbody><tr><td>互异性</td><td>一个键仅可以在映射中出现一次</td><td>✔</td><td>❌（任意次）</td><td>✔</td></tr><tr><td>无序性</td><td>键是没有顺序的</td><td>❌（从小到大）</td><td>❌（从小到大）</td><td>✔</td></tr></tbody></table><h4 id="2-7-1-常用方法">2.7.1 常用方法</h4><p><strong>构造</strong><br><code>map&lt;键类型, 值类型, 比较器&gt; mp</code></p><ul><li>键类型：要储存键的数据类型</li><li>值类型：要储存值的数据类型</li><li>比较器：键比较大小使用的比较器，默认为 <code>less&lt;类型&gt;</code>，可自定义</li></ul><figure class="highlight arduino"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs arduino">map&lt;<span class="hljs-type">int</span>, <span class="hljs-type">int</span>&gt; mp1;               <span class="hljs-comment">// int-&gt;int 的映射（键从小到大）</span><br>map&lt;<span class="hljs-type">int</span>, <span class="hljs-type">int</span>, greater&lt;<span class="hljs-type">int</span>&gt;&gt; st2; <span class="hljs-comment">// int-&gt;int 的映射（键从大到小）</span><br></code></pre></td></tr></table></figure><blockquote><p>对于需要自定义比较器的情况，涉及一些初学时容易看迷糊的语法（重载小括号运算符 / lambda 表达式），在此就不展开讲了。类似于前端的箭头函数。学过前端都知道，斯，学Java的应该更知道，<s>所以还是去学一下吧</s>,qwq.</p></blockquote><p><strong>遍历</strong><br>可使用迭代器进行遍历：</p><figure class="highlight livescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs livescript"><span class="hljs-keyword">for</span> (<span class="hljs-keyword">map</span>&lt;int, int&gt;::iterator <span class="hljs-literal">it</span> = mp.begin(); <span class="hljs-literal">it</span> != mp.end(); ++<span class="hljs-literal">it</span>)<br>    cout &lt;&lt; <span class="hljs-literal">it</span>-&gt;first &lt;&lt; <span class="hljs-string">&#x27; &#x27;</span> &lt;&lt; <span class="hljs-literal">it</span>-&gt;second &lt;&lt; endl;<br></code></pre></td></tr></table></figure><p>基于范围的循环（C++ 11）：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-keyword">for</span> (auto &amp;<span class="hljs-built_in">pr</span> : mp)<br>    cout &lt;&lt; <span class="hljs-string">pr.first &lt;&lt; &#x27; &#x27; &lt;&lt; pr</span>.second &lt;&lt; <span class="hljs-string">endl;</span><br><span class="hljs-string">结构化绑定 + 基于范围的循环（C++17）：</span><br><span class="hljs-string"></span><br><span class="hljs-string">for (auto &amp;[key, val] : mp)</span><br><span class="hljs-string">    cout &lt;&lt; key &lt;&lt; &#x27; &#x27; &lt;&lt; val &lt;&lt; endl</span><br></code></pre></td></tr></table></figure><p><strong>其他</strong></p><table><thead><tr><th>作用</th><th>用法</th><th>示例</th></tr></thead><tbody><tr><td>增 / 改 / 查元素</td><td>中括号</td><td>mp[1] = 2;</td></tr><tr><td>查元素（返回迭代器）</td><td>.find(元素)</td><td>auto it = mp.find(1);</td></tr><tr><td>删除元素</td><td>.erase(元素)</td><td>mp.erase(2);</td></tr><tr><td>判断元素是否存在</td><td>.count(元素)</td><td>mp.count(3);</td></tr><tr><td>查看大小 / 清空 / 判空</td><td>略</td><td>略</td></tr></tbody></table><p>增删改查时间复杂度均为 O(logn)</p><h4 id="2-7-2-适用情形">2.7.2 适用情形</h4><p>需要维护映射的场景可以使用：输入若干字符串，统计每种字符串的出现次数。(map&lt;string, int&gt; mp)</p><h4 id="2-7-3-注意事项">2.7.3 注意事项</h4><p><strong>中括号访问时默认值</strong><br>如果使用中括号访问 map 时对应的键不存在，那么会新增这个键，并且值为默认值，因此中括号会影响键的存在性。</p><figure class="highlight arduino"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs arduino">map&lt;<span class="hljs-type">char</span>, <span class="hljs-type">int</span>&gt; mp;<br>cout &lt;&lt; mp.<span class="hljs-built_in">count</span>(<span class="hljs-string">&#x27;a&#x27;</span>) &lt;&lt; endl; <span class="hljs-comment">// 0</span><br>mp[<span class="hljs-string">&#x27;a&#x27;</span>];                       <span class="hljs-comment">// 即使什么都没做，此时mp[&#x27;a&#x27;]=0已经插入了</span><br>cout &lt;&lt; mp.<span class="hljs-built_in">count</span>(<span class="hljs-string">&#x27;a&#x27;</span>) &lt;&lt; endl; <span class="hljs-comment">// 1</span><br>cout &lt;&lt; mp[<span class="hljs-string">&#x27;a&#x27;</span>] &lt;&lt; endl;       <span class="hljs-comment">// 0</span><br></code></pre></td></tr></table></figure><p><strong>不可用迭代器计算下标</strong><br>map 的迭代器不能像 vector 一样相减得到下标。下面是错误用法：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-keyword">auto</span> it = mp.<span class="hljs-built_in">find</span>(<span class="hljs-string">&#x27;a&#x27;</span>);      <span class="hljs-comment">// 正确，返回2所在位置的迭代器。</span><br><span class="hljs-type">int</span> idx = it - mp.<span class="hljs-built_in">begin</span>();   <span class="hljs-comment">// 错误！不可相减得到下标。</span><br></code></pre></td></tr></table></figure><h3 id="2-8-字符串-string">2.8 字符串 string</h3><p><strong>#include &lt;string&gt;</strong></p><p>顾名思义，就是储存字符串的。</p><h4 id="2-8-1-常用方法">2.8.1 常用方法</h4><p>构造<br>构造函数：<strong>string(长度, 初值)</strong></p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs csharp"><span class="hljs-built_in">string</span> s1;           <span class="hljs-comment">// 构造字符串，为空</span><br><span class="hljs-built_in">string</span> s2 = <span class="hljs-string">&quot;awa!&quot;</span>;  <span class="hljs-comment">// 构造字符串，并赋值awa!</span><br><span class="hljs-function"><span class="hljs-built_in">string</span> <span class="hljs-title">s3</span>(<span class="hljs-params"><span class="hljs-number">10</span>, <span class="hljs-string">&#x27;6&#x27;</span></span>)</span>;  <span class="hljs-comment">// 构造字符串，通过构造函数构造为6666666666</span><br></code></pre></td></tr></table></figure><p><strong>输入输出</strong><br>C++</p><figure class="highlight abnf"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs abnf">string s<span class="hljs-comment">;</span><br>cin &gt;&gt; s<span class="hljs-comment">;</span><br>cout &lt;&lt; s<span class="hljs-comment">;</span><br></code></pre></td></tr></table></figure><p>C</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;cstdio&gt;</span></span><br>string s;<br><span class="hljs-type">char</span> buf[<span class="hljs-number">100</span>];<br><span class="hljs-built_in">scanf</span>(<span class="hljs-string">&quot;%s&quot;</span>, &amp;buf);<br>s = buf;<br><span class="hljs-built_in">printf</span>(<span class="hljs-string">&quot;%s&quot;</span>, s.<span class="hljs-built_in">c_str</span>());<br></code></pre></td></tr></table></figure><p><strong>其他</strong></p><table><thead><tr><th>作用</th><th>用法</th><th>示例</th></tr></thead><tbody><tr><td>修改、查询指定下标字符</td><td>[]</td><td>s[1] = ‘a’;</td></tr><tr><td>是否相同</td><td>==</td><td>if (s1 == s2) …</td></tr><tr><td>字符串连接</td><td>+</td><td>string s = s1 + s2;</td></tr><tr><td>尾接字符串</td><td>+=</td><td>s += “awa”;</td></tr><tr><td>取子串</td><td>.substr(起始下标, 子串长度)</td><td>string sub = s.substr(2, 10);</td></tr><tr><td>查找字符串</td><td>.find(字符串, 起始下标)</td><td>int pos = s.find(“awa”);</td></tr></tbody></table><p><strong>数值与字符串互转（C++11）</strong></p><table><thead><tr><th>源</th><th>目的</th><th>函数</th></tr></thead><tbody><tr><td>int / long long / float / double / long double</td><td>string</td><td>to_string()</td></tr><tr><td>string</td><td>int</td><td>stoi()</td></tr><tr><td>string</td><td>long long</td><td>stoll()</td></tr><tr><td>string</td><td>float</td><td>stof()</td></tr><tr><td>string</td><td>double</td><td>stod()</td></tr><tr><td>string</td><td>long double</td><td>stold()</td></tr></tbody></table><h4 id="2-8-2-适用情形">2.8.2 适用情形</h4><p><s>非常好用！建议直接把字符数组扔了，赶快投入 string 的怀抱。</s></p><h4 id="2-8-3-注意事项">2.8.3 注意事项</h4><p>尾接字符串一定要用<code>+=</code><br>string 的 += 运算符，将会在原字符串原地尾接字符串。而 + 了再 = 赋值，会先生成一个临时变量，在复制给 string.</p><p>通常字符串长度可以很长，如果使用 + 字符串很容易就 TLE 了。</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs csharp"><span class="hljs-comment">// 优化前: 15139ms</span><br><span class="hljs-built_in">string</span> s;<br><span class="hljs-keyword">for</span> (<span class="hljs-built_in">int</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">5e5</span>; i++)<br>    s = s + <span class="hljs-string">&quot;a&quot;</span>;<br><br><span class="hljs-comment">// 优化后: &lt; 1ms (计时器显示0)</span><br><span class="hljs-built_in">string</span> s;<br><span class="hljs-keyword">for</span> (<span class="hljs-built_in">int</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">5e5</span>; i++)<br>    s += <span class="hljs-string">&quot;a&quot;</span>;<br></code></pre></td></tr></table></figure><p><code>.substr() </code>方法的奇葩参数<br>一定要注意，C++ string 的取子串的第一个参数是<strong>子串起点下标</strong>，第二个参数是<strong>子串长度。</strong></p><p>第二个参数不是子串终点！不是子串终点！要与 java 等其他语言区分开来。</p><p><code>.find() </code>方法的复杂度<br>该方法实现为暴力实现，时间复杂度为O(n^2^)<br><s>不要幻想 STL 内置了个 的 KMP 算法</s></p><h3 id="2-9-二元组-pair">2.9 二元组 pair</h3><p><strong>#include &lt;utility&gt;</strong><br>顾名思义，就是储存二元组的。</p><h4 id="2-9-1-常用方法">2.9.1 常用方法</h4><p><strong>构造</strong></p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs cpp">pair&lt;第一个值类型, 第二个值类型&gt; pr<br></code></pre></td></tr></table></figure><ul><li>第一个值类型：要储存的第一个值的数据类型</li><li>第二个值类型：要储存的第二个值的数据类型</li></ul><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs cpp">pair&lt;<span class="hljs-type">int</span>, <span class="hljs-type">int</span>&gt; p1;<br>pair&lt;<span class="hljs-type">int</span>, <span class="hljs-type">long</span> <span class="hljs-type">long</span>&gt; p2;<br>pair&lt;<span class="hljs-type">char</span>, <span class="hljs-type">int</span>&gt; p3;<br><span class="hljs-comment">// ...</span><br></code></pre></td></tr></table></figure><p><strong>赋值</strong><br>老式</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs cpp">pair&lt;<span class="hljs-type">int</span>, <span class="hljs-type">char</span>&gt; pr = <span class="hljs-built_in">make_pair</span>(<span class="hljs-number">1</span><span class="hljs-string">&#x27;a&#x27;</span>);<br></code></pre></td></tr></table></figure><p>列表构造 C++11</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs cpp">pair&lt;<span class="hljs-type">int</span>, <span class="hljs-type">char</span>&gt; pr = &#123;<span class="hljs-number">1</span>, <span class="hljs-string">&#x27;a&#x27;</span>&#125;;<br></code></pre></td></tr></table></figure><p><strong>取值</strong><br>直接取值</p><ul><li>取第一个值：.first</li><li>取第二个值：.second</li></ul><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs cpp">pair&lt;<span class="hljs-type">int</span>, <span class="hljs-type">char</span>&gt; pr = &#123;<span class="hljs-number">1</span>, <span class="hljs-string">&#x27;a&#x27;</span>&#125;;<br><span class="hljs-type">int</span> awa = pr.first;<br><span class="hljs-type">char</span> bwb = pr.second;<br></code></pre></td></tr></table></figure><p>结构化绑定 C++17</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs cpp">pair&lt;<span class="hljs-type">int</span>, <span class="hljs-type">char</span>&gt; pr = &#123;<span class="hljs-number">1</span>, <span class="hljs-string">&#x27;a&#x27;</span>&#125;;<br><span class="hljs-keyword">auto</span> &amp;[awa, bwb] = pr;<br><br></code></pre></td></tr></table></figure><p><strong>判同</strong><br>直接用<code>==</code>运算符</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs cpp">pair&lt;<span class="hljs-type">int</span>, <span class="hljs-type">int</span>&gt; p1 = &#123;<span class="hljs-number">1</span>, <span class="hljs-number">2</span>&#125;;<br>pair&lt;<span class="hljs-type">int</span>, <span class="hljs-type">int</span>&gt; p2 = &#123;<span class="hljs-number">1</span>, <span class="hljs-number">3</span>&#125;;<br><span class="hljs-keyword">if</span> (p1 == p2) &#123; ... &#125; <span class="hljs-comment">// false</span><br></code></pre></td></tr></table></figure><h4 id="2-9-2-适用场景">2.9.2 适用场景</h4><p>所有需要二元组的场景均可使用，效率和自己定义结构体差不多。</p><h4 id="2-9-3-注意事项">2.9.3 注意事项</h4><p>无</p><h2 id="3-迭代器简介">3 迭代器简介</h2><p><strong>#include &lt;iterator&gt;</strong></p><h3 id="3-1-迭代器是什么？">3.1 迭代器是什么？</h3><p>迭代器是 STL 中的一种数据结构，它可以帮助我们遍历容器中的元素。<br>不搞抽象，直接举例。<br>对于一个 vector，我们可以用下标遍历：</p><figure class="highlight abnf"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs abnf">for (int i <span class="hljs-operator">=</span> <span class="hljs-number">0</span><span class="hljs-comment">; i &lt; a.size(); i++)</span><br>    cout &lt;&lt; a[i] &lt;&lt; endl<span class="hljs-comment">;</span><br></code></pre></td></tr></table></figure><p>我们同时也可以用迭代器来遍历：</p><figure class="highlight applescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs applescript"><span class="hljs-keyword">for</span> (vector&lt;int&gt;::iterator <span class="hljs-keyword">it</span> = a.begin(); <span class="hljs-keyword">it</span> != a.<span class="hljs-keyword">end</span>(); ++<span class="hljs-keyword">it</span>)<br>    cout &lt;&lt; *<span class="hljs-keyword">it</span> &lt;&lt; endl;<br></code></pre></td></tr></table></figure><ul><li>a.begin() 是一个迭代器，指向的是第一个元素</li><li>a.end() 是一个迭代器，指向的是最后一个元素再后面一位</li><li>上述迭代器具有自增运算符，自增则迭代器向下一个元素移动</li><li>迭代器与指针相似，如果对它使用解引用运算符，即 *it，就能取到对应值了</li></ul><h3 id="3-2-为何需要迭代器？">3.2 为何需要迭代器？</h3><p>很多数据结构并不是线性的（例如红黑树），对于非线性数据结构，下标是无意义的。无法使用下标来遍历整个数据结构。</p><p>迭代器的作用就是定义某个数据结构的遍历方式，通过迭代器的增减，代表遍历到的位置，通过迭代器便能成功遍历非线性结构了。</p><p>例如，set 的实现是红黑树，我们是没法用下标来访问元素的。但是通过迭代器，我们就能遍历 set 中的元素了：<br>其实我也不太清楚红黑树，www，彭老师数据结构好像没有太细讲这个，<s>总之会用就行了，不管它的来龙去脉</s></p><figure class="highlight applescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs applescript"><span class="hljs-keyword">for</span> (<span class="hljs-keyword">set</span>&lt;int&gt;::iterator <span class="hljs-keyword">it</span> = st.begin(); <span class="hljs-keyword">it</span> != st.<span class="hljs-keyword">end</span>(); ++<span class="hljs-keyword">it</span>)<br>    cout &lt;&lt; *<span class="hljs-keyword">it</span> &lt;&lt; endl;<br></code></pre></td></tr></table></figure><h3 id="3-3-迭代器用法">3.3 迭代器用法</h3><p>对于 vector 容器，它的迭代器功能比较完整，以它举例：</p><ul><li>.begin()：头迭代器</li><li>.end()：尾迭代器</li><li>.rbegin()：反向头迭代器</li><li>.rend()：反向尾迭代器</li><li>迭代器 + 整型：将迭代器向后移动</li><li>迭代器 - 整型：将迭代器向前移动</li><li>迭代器 ++：将迭代器向后移动 1 位</li><li>迭代器 --：将迭代器向前移动 1 位</li><li>迭代器 - 迭代器：两个迭代器的距离</li><li>prev(it)：返回 it 的前一个迭代器</li><li>next(it)：返回 it 的后一个迭代器<br>对于其他容器，由于其结构特性，上面的功能不一定都有（例如 set 的迭代器是不能相减求距离的）</li></ul><h3 id="3-4-常见问题">3.4 常见问题</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs cpp">.<span class="hljs-built_in">end</span>()<br></code></pre></td></tr></table></figure><p>和</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs cpp">.<span class="hljs-built_in">rend</span>()<br></code></pre></td></tr></table></figure><p>指向的位置是无意义的值</p><p>对于一个长度为 10 的数组：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">10</span>; i++)<br></code></pre></td></tr></table></figure><p>，第 10 位是不可访问的</p><p>对于一个长度为 10 的容器：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-keyword">for</span> (<span class="hljs-keyword">auto</span> it = a.<span class="hljs-built_in">begin</span>(); it != a.<span class="hljs-built_in">end</span>(); ++it)<br></code></pre></td></tr></table></figure><p>，<code>.end()</code> 是不可访问的</p><p><strong>不同容器的迭代器功能可能不一样</strong></p><p>迭代器细化的话有正向、反向、双向，每个容器的迭代器支持的运算符也可能不同，因此不同容器的迭代器细节很有可能是不一样的。</p><p><strong>删除操作时需要警惕</strong></p><p><s>建议直接找大佬</s></p><p>建议：如无必要，别用迭代器操作容器。（遍历与访问没关系）</p><hr><h2 id="4-常用算法">4 常用算法</h2><p><strong><code>#include &lt;algorithm&gt;</code></strong></p><h3 id="4-1-内容总览">4.1 内容总览</h3><p>打勾的是本次将会详细讲解的，其他的是算法竞赛中建议学习的，不在下表列出的在比赛中基本用不到。（很多函数的功能很简单，自己都能快速写出来，但是使用函数可以让代码可读性变得更高，这在比赛中是至关紧要的）<s>反正我不太写得出来，还是太菜了</s></p><h4 id="4-1-1-算法库-Algorithm">4.1.1 算法库 Algorithm</h4><ul><li>[ ] <code>count()</code></li><li>[ ] <code>find()</code></li><li>[ ] <code>fill()</code></li><li>[x] <code>swap()</code></li><li>[x] <code>reverse()</code></li><li>[ ] <code>shuffle()</code> C++11</li><li>[x] <code>unique()</code></li><li>[x] <code>sort()</code></li><li>[x] <code>lower_bound()</code> / <code>upper_bound()</code></li><li>[x] <code>max()</code> / <code>min()</code></li><li>[ ] <code>max_element()</code> / <code>min_element()</code></li><li>[ ] <code>prev_permutation()</code> / <code>next_permutation()</code></li></ul><h4 id="4-1-2-数学函数-cmath">4.1.2 数学函数 <code>cmath</code></h4><ul><li>[x] <code>abs()</code></li><li>[x] <code>exp()</code></li><li>[x] <code>log()</code> / <code>log10()</code> / <code>log2()</code></li><li>[x] <code>pow()</code></li><li>[x] <code>sqrt()</code></li><li>[ ] <code>sin()</code> / <code>cos()</code> / <code>tan()</code></li><li>[ ] <code>asin()</code> / <code>acos()</code> / <code>atan()</code></li><li>[ ] <code>sinh()</code> / <code>cosh()</code> / <code>tanh()</code></li><li>[ ] <code>asinh()</code> / <code>acosh()</code> / <code>atanh()</code> C++11</li><li>[x] <code>ceil()</code> / <code>floor()</code></li><li>[x] <code>round()</code> C++11</li></ul><h4 id="4-1-3-数值算法-numeric">4.1.3 数值算法 <code>numeric</code></h4><ul><li>[ ] <code>iota()</code> C++11</li><li>[ ] <code>accumulate()</code></li><li>[x] <code>gcd()</code> C++17</li><li>[x] <code>lcm()</code> C++17</li></ul><h4 id="4-1-4-伪随机数生成-random">4.1.4 伪随机数生成 <code>random</code></h4><ul><li>[ ] <code>mt19937</code></li><li>[ ] <code>random_device()</code></li></ul><h3 id="4-2-swap">4.2 <code>swap()</code></h3><p>交换两个变量的值</p><p>用法示例</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-function"><span class="hljs-keyword">template</span>&lt; <span class="hljs-keyword">class</span> T &gt;</span><br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">swap</span><span class="hljs-params">( T&amp; a, T&amp; b )</span></span>;<br></code></pre></td></tr></table></figure><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-type">int</span> a = <span class="hljs-number">0</span>, b = <span class="hljs-number">1</span>;<br><span class="hljs-built_in">swap</span>(a, b);<br><span class="hljs-comment">// now a = 1, b = 0</span><br><br><span class="hljs-type">int</span> arr[<span class="hljs-number">10</span>] &#123;<span class="hljs-number">0</span>, <span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">6</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>&#125;;<br><span class="hljs-built_in">swap</span>(arr[<span class="hljs-number">4</span>], arr[<span class="hljs-number">6</span>]);<br><span class="hljs-comment">// now arr = &#123;0, 1, 2, 3, 6, 5, 4, 7, 8, 9&#125;</span><br></code></pre></td></tr></table></figure><p><strong>注意事项</strong></p><p>这个 <code>swap</code> 参数是引用的，不需要像 C 语言一样取地址。</p><hr><h3 id="4-3-sort">4.3 <code>sort()</code></h3><p>使用快速排序给一个可迭代对象排序</p><p>用法示例</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-function"><span class="hljs-keyword">template</span>&lt; <span class="hljs-keyword">class</span> RandomIt, <span class="hljs-keyword">class</span> Compare &gt;</span><br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">sort</span><span class="hljs-params">( RandomIt first, RandomIt last, Compare comp )</span></span>;<br></code></pre></td></tr></table></figure><p>默认排序从小到大</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs cpp">vector&lt;<span class="hljs-type">int</span>&gt; arr&#123;<span class="hljs-number">1</span>, <span class="hljs-number">9</span>, <span class="hljs-number">1</span>, <span class="hljs-number">9</span>, <span class="hljs-number">8</span>, <span class="hljs-number">1</span>, <span class="hljs-number">0</span>&#125;;<br><span class="hljs-built_in">sort</span>(arr.<span class="hljs-built_in">begin</span>(), arr.<span class="hljs-built_in">end</span>());<br><span class="hljs-comment">// arr = [0, 1, 1, 1, 8, 9, 9]</span><br></code></pre></td></tr></table></figure><p>如果要从大到小，则需要传比较器进去。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs cpp">vector&lt;<span class="hljs-type">int</span>&gt; arr&#123;<span class="hljs-number">1</span>, <span class="hljs-number">9</span>, <span class="hljs-number">1</span>, <span class="hljs-number">9</span>, <span class="hljs-number">8</span>, <span class="hljs-number">1</span>, <span class="hljs-number">0</span>&#125;;<br><span class="hljs-built_in">sort</span>(arr.<span class="hljs-built_in">begin</span>(), arr.<span class="hljs-built_in">end</span>(), <span class="hljs-built_in">greater</span>&lt;<span class="hljs-type">int</span>&gt;());<br><span class="hljs-comment">// arr = [9, 9, 8, 1, 1, 1, 0]</span><br></code></pre></td></tr></table></figure><p>如果需要完成特殊比较，则需要手写比较器。</p><p>比较器函数返回值是 <code>bool</code> 类型，传参是需要比较的两个元素。</p><p>注意：如果传的两个参数相等，返回值必须是 <code>false</code></p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-function"><span class="hljs-type">bool</span> <span class="hljs-title">cmp</span><span class="hljs-params">(pair&lt;<span class="hljs-type">int</span>, <span class="hljs-type">int</span>&gt; a, pair&lt;<span class="hljs-type">int</span>, <span class="hljs-type">int</span>&gt; b)</span></span><br><span class="hljs-function"></span>&#123;<br>    <span class="hljs-keyword">if</span> (a.second != b.second)<br>        <span class="hljs-keyword">return</span> a.second &lt; b.second;<br>    <span class="hljs-keyword">return</span> a.first &gt; b.first;<br>&#125;<br></code></pre></td></tr></table></figure><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span><br><span class="hljs-function"></span>&#123;<br>    vector&lt;pair&lt;<span class="hljs-type">int</span>, <span class="hljs-type">int</span>&gt;&gt; arr&#123;&#123;<span class="hljs-number">1</span>, <span class="hljs-number">9</span>&#125;, &#123;<span class="hljs-number">2</span>, <span class="hljs-number">9</span>&#125;, &#123;<span class="hljs-number">8</span>, <span class="hljs-number">1</span>&#125;, &#123;<span class="hljs-number">0</span>, <span class="hljs-number">0</span>&#125;&#125;;<br>    <span class="hljs-built_in">sort</span>(arr.<span class="hljs-built_in">begin</span>(), arr.<span class="hljs-built_in">end</span>(), cmp);<br>    <span class="hljs-comment">// arr = [(0, 0), (8, 1), (2, 9), (1, 9)]</span><br>&#125;<br></code></pre></td></tr></table></figure><h3 id="4-4-lower-bound-upper-bound">4.4 lower_bound() / upper_bound()</h3><p>在<strong>已升序排序</strong>的元素中，应用二分查找检索指定元素，返回对应元素迭代器位置。<strong>找不到则返回尾迭代器。</strong></p><ul><li>lower_bound(): 寻找<br>的第一个元素的位置</li><li>upper_bound(): 寻找<br>的第一个元素的位置<br>怎么找<br>/<br>的第一个元素呢？</li></ul><p>的第一个元素的前一个元素（如果有）便是<br>的第一个元素<br>的第一个元素的前一个元素（如果有）便是<br>的第一个元素<br>返回的是迭代器，如何转成下标索引呢？减去头迭代器即可。</p><p><strong>用法示例</strong></p><figure class="highlight perl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs perl">template&lt; <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ForwardIt</span>, <span class="hljs-title">class</span> <span class="hljs-title">T</span> &gt;</span><br><span class="hljs-class"><span class="hljs-title">ForwardIt</span> <span class="hljs-title">lower_bound</span>( <span class="hljs-title">ForwardIt</span> <span class="hljs-title">first</span>, <span class="hljs-title">ForwardIt</span> <span class="hljs-title">last</span>, <span class="hljs-title">const</span> <span class="hljs-title">T</span>&amp; <span class="hljs-title">value</span> )</span>;<br></code></pre></td></tr></table></figure><figure class="highlight plaintext"><figcaption><span>arr&#123;0, 1, 1, 1, 8, 9, 9&#125;;</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs vector<int>">vector&lt;int&gt;::iterator it = lower_bound(arr.begin(), arr.end(), 7);<br>int idx = it - arr.begin();<br>// idx = 4<br></code></pre></td></tr></table></figure><p>我们通常写成一行：</p><figure class="highlight fsharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs fsharp">vector<span class="hljs-operator">&lt;</span>int<span class="hljs-operator">&gt;</span> <span class="hljs-keyword">arr</span>&#123;<span class="hljs-number">0</span>, <span class="hljs-number">1</span>, <span class="hljs-number">1</span>, <span class="hljs-number">1</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>, <span class="hljs-number">9</span>&#125;;<br>idx <span class="hljs-operator">=</span> lower_bound(arr.<span class="hljs-keyword">begin</span>(), arr.<span class="hljs-keyword">end</span>(), <span class="hljs-number">7</span>) <span class="hljs-operator">-</span> arr.<span class="hljs-keyword">begin</span>(); <span class="hljs-comment">// 4</span><br>idx <span class="hljs-operator">=</span> lower_bound(arr.<span class="hljs-keyword">begin</span>(), arr.<span class="hljs-keyword">end</span>(), <span class="hljs-number">8</span>) <span class="hljs-operator">-</span> arr.<span class="hljs-keyword">begin</span>(); <span class="hljs-comment">// 4</span><br>idx <span class="hljs-operator">=</span> upper_bound(arr.<span class="hljs-keyword">begin</span>(), arr.<span class="hljs-keyword">end</span>(), <span class="hljs-number">7</span>) <span class="hljs-operator">-</span> arr.<span class="hljs-keyword">begin</span>(); <span class="hljs-comment">// 4</span><br>idx <span class="hljs-operator">=</span> upper_bound(arr.<span class="hljs-keyword">begin</span>(), arr.<span class="hljs-keyword">end</span>(), <span class="hljs-number">8</span>) <span class="hljs-operator">-</span> arr.<span class="hljs-keyword">begin</span>(); <span class="hljs-comment">// 5</span><br></code></pre></td></tr></table></figure><h3 id="4-5-reverse">4.5 reverse()</h3><p>反转一个可迭代对象的元素顺序</p><p>用法示例</p><figure class="highlight perl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs perl">template&lt; <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">BidirIt</span> &gt;</span><br><span class="hljs-class"><span class="hljs-title">void</span> <span class="hljs-title">reverse</span>( <span class="hljs-title">BidirIt</span> <span class="hljs-title">first</span>, <span class="hljs-title">BidirIt</span> <span class="hljs-title">last</span> )</span>;<br></code></pre></td></tr></table></figure><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs scss">vector&lt;int&gt; <span class="hljs-built_in">arr</span>(<span class="hljs-number">10</span>);<br><span class="hljs-built_in">iota</span>(arr.begin(), arr<span class="hljs-selector-class">.end</span>(), <span class="hljs-number">1</span>);<br><span class="hljs-comment">// 1, 2, 3, 4, 5, 6, 7, 8, 9, 10</span><br><span class="hljs-built_in">reverse</span>(arr.begin(), arr<span class="hljs-selector-class">.end</span>());<br><span class="hljs-comment">// 10, 9, 8, 7, 6, 5, 4, 3, 2, 1</span><br></code></pre></td></tr></table></figure><h3 id="4-6-max-min">4.6 max() / min()</h3><p>返回最大值 / 最小值的数值</p><p>用法示例</p><figure class="highlight apache"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs apache"><span class="hljs-attribute">int</span> mx = max(<span class="hljs-number">1</span>, <span class="hljs-number">2</span>); // <span class="hljs-number">2</span><br><span class="hljs-attribute">int</span> mn = min(<span class="hljs-number">1</span>, <span class="hljs-number">2</span>); // <span class="hljs-number">1</span><br></code></pre></td></tr></table></figure><p>在 C++11 之后，可以使用列表构造语法传入一个列表，这样就能一次性给多个元素找最大值而不用套娃了：</p><figure class="highlight glsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs glsl"><span class="hljs-comment">// Before C++11</span><br><span class="hljs-type">int</span> mx = <span class="hljs-built_in">max</span>(<span class="hljs-built_in">max</span>(<span class="hljs-number">1</span>, <span class="hljs-number">2</span>), <span class="hljs-built_in">max</span>(<span class="hljs-number">3</span>, <span class="hljs-number">4</span>)); <span class="hljs-comment">// 4</span><br><span class="hljs-type">int</span> mn = <span class="hljs-built_in">min</span>(<span class="hljs-built_in">min</span>(<span class="hljs-number">1</span>, <span class="hljs-number">2</span>), <span class="hljs-built_in">min</span>(<span class="hljs-number">3</span>, <span class="hljs-number">4</span>)); <span class="hljs-comment">// 1</span><br><br><span class="hljs-comment">// After C++11</span><br><span class="hljs-type">int</span> mx = <span class="hljs-built_in">max</span>(&#123;<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>&#125;); <span class="hljs-comment">// 4</span><br><span class="hljs-type">int</span> mn = <span class="hljs-built_in">min</span>(&#123;<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>&#125;); <span class="hljs-comment">// 1</span><br></code></pre></td></tr></table></figure><h3 id="4-7-unique">4.7 unique()</h3><p>消除数组的重复<strong>相邻</strong>元素，数组长度不变，但是有效数据缩短，返回的是有效数据位置的结尾迭代器。</p><p>例如：[1,1,4,5,1,4] -&gt; [1,4,5,1,4,_] ，下划线位置为返回的迭代器指向。</p><figure class="highlight perl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs perl">template&lt; <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ForwardIt</span> &gt;</span><br><span class="hljs-class"><span class="hljs-title">ForwardIt</span> <span class="hljs-title">unique</span>( <span class="hljs-title">ForwardIt</span> <span class="hljs-title">first</span>, <span class="hljs-title">ForwardIt</span> <span class="hljs-title">last</span> )</span>;<br></code></pre></td></tr></table></figure><p><strong>用法示例</strong></p><p>单独使用 unique 并不能达成去重效果，因为它只消除相邻的重复元素。但是如果序列有序，那么它就能去重了。</p><p>但是它去重后，序列尾部会产生一些无效数据： [1,1,2,4,4,5,5]-&gt;[1,2,4,5,_,_,_]，为了删掉这些无效数据，我们需要结合 erase.</p><p>最终，给 vector 去重的写法便是：</p><figure class="highlight apache"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs apache"><span class="hljs-attribute">vector</span>&lt;int&gt; arr&#123;<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">1</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">4</span>, <span class="hljs-number">4</span>&#125;;<br><span class="hljs-attribute">sort</span>(arr.begin(), arr.end());<br><span class="hljs-attribute">arr</span>.erase(unique(arr.begin(), arr.end()), arr.end());<br></code></pre></td></tr></table></figure><h3 id="4-8-数学函数">4.8 数学函数</h3><p>所有函数参数均支持 int / long long / float / double / long double</p><table><thead><tr><th>公式</th><th>示例</th></tr></thead><tbody><tr><td>f(x)=|x|</td><td>abs(-1.0)</td></tr><tr><td>f(x)=e^x^</td><td>exp(2)</td></tr><tr><td>f(x)=ln x</td><td>log(3)</td></tr><tr><td>f(x)=x^y^</td><td>pow(2, 3)</td></tr><tr><td>f(x)=√x</td><td>sqrt(2)</td></tr><tr><td>f(x)=x(向上取整)</td><td>ceil(2.1)</td></tr><tr><td>f(x)=x(向下取整)</td><td>floor(2.1)</td></tr><tr><td>f(x)=&lt;x&gt;</td><td>round(2.1)</td></tr></tbody></table><p><strong>注意事项</strong><br>由于浮点误差，有些的数学函数的行为可能与预期不符，导致 WA。如果你的操作数都是整型，那么用下面的写法会更稳妥。</p><p>[<a href="https://codeforces.com/blog/entry/107717">原文地址:https://codeforces.com/blog/entry/107717</a></p><ul><li><p>计算 a/b 取下整<br>别用：floor(1.0 * a / b)<br>要用：a / b</p></li><li><p>计算 a/b 取上整<br>别用：ceil(1.0 * a / b)<br>要用：(a + b - 1) / b</p></li><li><p>计算 a 的平方根取下整<br>别用：(int) sqrt(a)<br>要用：二分查找 链接 -&gt; <a href="f0faee69.html">二分查找</a></p></li><li><p>计算 a^b^<br>别用：pow(a, b)<br>要用：快速幂 链接 -&gt; <a href="716f5d8f.html">快速幂运算</a></p></li><li><p>计算 log2(a)<br>别用：log2(a)<br>要用：__lg （不规范，但是这是竞赛）/ bit_width（C++20 可用）</p></li></ul><h3 id="4-9-gcd-lcm">4.9 gcd() / lcm()</h3><p>（C++17）返回最大公因数 / 最小公倍数</p><figure class="highlight apache"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs apache"><span class="hljs-attribute">int</span> x = gcd(<span class="hljs-number">8</span>, <span class="hljs-number">12</span>); // <span class="hljs-number">4</span><br><span class="hljs-attribute">int</span> y = lcm(<span class="hljs-number">8</span>, <span class="hljs-number">12</span>); // <span class="hljs-number">24</span><br></code></pre></td></tr></table></figure><p>如果不是 C++17，但是是 GNU 编译器（g++），那么可以用内置函数 <code>__gcd()</code>.<br>当然，gcd / lcm 函数也挺好写，直接写也行（欧几里得算法）：</p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs css">int gcd(int <span class="hljs-selector-tag">a</span>, int <span class="hljs-selector-tag">b</span>)<br>&#123;<br>    if (!<span class="hljs-selector-tag">b</span>)<br>        return <span class="hljs-selector-tag">a</span>;<br>    return gcd(<span class="hljs-selector-tag">b</span>, <span class="hljs-selector-tag">a</span> % <span class="hljs-selector-tag">b</span>);<br>&#125;<br><br>int lcm(int <span class="hljs-selector-tag">a</span>, int <span class="hljs-selector-tag">b</span>)<br>&#123;<br>    return <span class="hljs-selector-tag">a</span> / gcd(<span class="hljs-selector-tag">a</span>, <span class="hljs-selector-tag">b</span>) * <span class="hljs-selector-tag">b</span>;<br>&#125;<br></code></pre></td></tr></table></figure>]]>
    </content>
    <id>https://www.furinaluna.top/posts/d97dbae4.html</id>
    <link href="https://www.furinaluna.top/posts/d97dbae4.html"/>
    <published>2025-11-16T10:42:00.000Z</published>
    <summary>
      <![CDATA[<p>C++ 标准模板库 (STL, Standard Template Library)：包含一些常用数据结构与算法的模板的 C++ 软件库。其包含四个组件——算法 (Algorithms)、容器 (Containers)、仿函数 (Functors)、迭代器 (Iterato]]>
    </summary>
    <title>算法竞赛C++STL常用用法</title>
    <updated>2026-09-11T13:48:57.195Z</updated>
  </entry>
  <entry>
    <author>
      <name>Furina</name>
    </author>
    <category term="杂项" scheme="https://www.furinaluna.top/categories/%E6%9D%82%E9%A1%B9/"/>
    <category term="杂项" scheme="https://www.furinaluna.top/tags/%E6%9D%82%E9%A1%B9/"/>
    <content>
      <![CDATA[<p>“当月光洒在键盘上，我知道——今夜，注定无法平凡。”<br>这不是一篇普通的博文，而是我灵魂燃烧的见证！🔥<br>就在昨夜，星辰黯淡，网络低语，一场与未知 Bug 的终极对决，在寂静的凌晨悄然展开。无数报错如恶魔低语，<code>No layout</code> 的诅咒回荡在控制台深处……SSL证书拒绝握手，Git克隆失败，npm警告如潮水般涌来！<br>但我——从未退却！</p><p><strong>以上内容ai生成</strong></p><p>简单来说，我总算是完成了建站部署，虽然是托管到GitHub上面有点慢，找个时间套一个CDN,或者托管到Netlify,或者Vercel,或者Cloudflare上面吧<br>以后有机会再迁移吧</p><p><strong>2025-11-21:</strong><br>更新了一下，搞了一个域名，然后解析除了几个二级的域名，在cloudflash套cdn，再部署到另一个verel上面，感觉还不错。速度快了很多，移动端也可以访问了。有机会写一篇文章讲讲<strong>域名解析，cdn,部署</strong>之类的知识吧，初学者搞这个还是有点小复杂的。很多东西需要自己去摸索，慢慢来吧。</p>]]>
    </content>
    <id>https://www.furinaluna.top/posts/fa05c009.html</id>
    <link href="https://www.furinaluna.top/posts/fa05c009.html"/>
    <published>2025-11-14T02:36:19.000Z</published>
    <summary>
      <![CDATA[<p>“当月光洒在键盘上，我知道——今夜，注定无法平凡。”<br>
这不是一篇普通的博文，而是我灵魂燃烧的见证！🔥<br>
就在昨夜，星辰黯淡，网络低语，一场与未知 Bug 的终极对决，在寂静的凌晨悄然展开。无数报错如恶魔低语，<code>No layout</code> 的诅]]>
    </summary>
    <title>第一篇文章</title>
    <updated>2026-09-11T13:48:57.195Z</updated>
  </entry>
</feed>
