<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <author>
    <name>Super Bing</name>
  </author>
  <generator uri="https://hexo.io/">Hexo</generator>
  <id>https://itdxb.cn/</id>
  <link href="https://itdxb.cn/" rel="alternate"/>
  <link href="https://itdxb.cn/atom.xml" rel="self"/>
  <rights>All rights reserved 2026, Super Bing</rights>
  <subtitle>分享软件工程毕业设计项目</subtitle>
  <title>Super Bing`s Hexo</title>
  <updated>2026-07-24T05:54:02.572Z</updated>
  <entry>
    <author>
      <name>Super Bing</name>
    </author>
    <category term="Python" scheme="https://itdxb.cn/categories/Python/"/>
    <category term="教程" scheme="https://itdxb.cn/tags/%E6%95%99%E7%A8%8B/"/>
    <category term="Python" scheme="https://itdxb.cn/tags/Python/"/>
    <category term="设计模式" scheme="https://itdxb.cn/tags/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/"/>
    <category term="SOLID" scheme="https://itdxb.cn/tags/SOLID/"/>
    <category term="架构" scheme="https://itdxb.cn/tags/%E6%9E%B6%E6%9E%84/"/>
    <content>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><h1 id="十二、设计模式与架构思维"><a href="#十二、设计模式与架构思维" class="headerlink" title="十二、设计模式与架构思维"></a>十二、设计模式与架构思维</h1><blockquote><p><strong>阶段定位</strong>：设计模式是解决常见问题的经验总结，架构思维是站在系统层面思考问题的能力。本阶段不是教条式地背诵 23 种设计模式，而是理解模式的本质，学会在合适的场景选择合适的方案，最终形成自己的架构判断力。</p></blockquote><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">┌──────────────────────────────────────────────────────────────────┐</span><br><span class="line">│                    设计模式与架构知识图谱                         │</span><br><span class="line">├──────────────────────────────────────────────────────────────────┤</span><br><span class="line">│                                                                  │</span><br><span class="line">│  设计原则（SOLID）                                                │</span><br><span class="line">│    ├── SRP（单一职责）                                            │</span><br><span class="line">│    ├── OCP（开闭原则）                                            │</span><br><span class="line">│    ├── LSP（里氏替换）                                            │</span><br><span class="line">│    ├── ISP（接口隔离）                                            │</span><br><span class="line">│    └── DIP（依赖倒置）                                            │</span><br><span class="line">│                                                                  │</span><br><span class="line">│  创建型模式                                                       │</span><br><span class="line">│    ├── 工厂模式（Factory）                                        │</span><br><span class="line">│    ├── 建造者模式（Builder）                                      │</span><br><span class="line">│    ├── 单例模式（Singleton）                                      │</span><br><span class="line">│    ├── 原型模式（Prototype）                                      │</span><br><span class="line">│    └── 抽象工厂模式（Abstract Factory）                           │</span><br><span class="line">│                                                                  │</span><br><span class="line">│  结构型模式                                                       │</span><br><span class="line">│    ├── 适配器模式（Adapter）                                      │</span><br><span class="line">│    ├── 装饰器模式（Decorator）                                    │</span><br><span class="line">│    ├── 代理模式（Proxy）                                          │</span><br><span class="line">│    ├── 组合模式（Composite）                                      │</span><br><span class="line">│    ├── 享元模式（Flyweight）                                      │</span><br><span class="line">│    └── 门面模式（Facade）                                          │</span><br><span class="line">│                                                                  │</span><br><span class="line">│  行为型模式                                                       │</span><br><span class="line">│    ├── 观察者模式（Observer）                                     │</span><br><span class="line">│    ├── 策略模式（Strategy）                                       │</span><br><span class="line">│    ├── 命令模式（Command）                                        │</span><br><span class="line">│    ├── 模板方法模式（Template Method）                            │</span><br><span class="line">│    └── 迭代器模式（Iterator）                                      │</span><br><span class="line">│                                                                  │</span><br><span class="line">│  架构思维                                                         │</span><br><span class="line">│    ├── 分层架构                                                   │</span><br><span class="line">│    ├── 微服务架构                                                 │</span><br><span class="line">│    ├── 事件驱动架构                                               │</span><br><span class="line">│    ├── 领域驱动设计（DDD）                                        │</span><br><span class="line">│    └── 架构权衡与决策                                             │</span><br><span class="line">│                                                                  │</span><br><span class="line">└──────────────────────────────────────────────────────────────────┘</span><br></pre></td></tr></table></figure><hr><h2 id="1-设计原则（SOLID）"><a href="#1-设计原则（SOLID）" class="headerlink" title="1. 设计原则（SOLID）"></a>1. 设计原则（SOLID）</h2><p>在讲具体模式之前，先理解五条核心设计原则。它们是比模式更根本的东西。</p><h3 id="1-1-单一职责原则（SRP）"><a href="#1-1-单一职责原则（SRP）" class="headerlink" title="1.1 单一职责原则（SRP）"></a>1.1 单一职责原则（SRP）</h3><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 反例：一个类做太多事</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">UserManager</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">create_user</span>(<span class="params">self, name, email</span>): ...</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">delete_user</span>(<span class="params">self, user_id</span>): ...</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">send_email</span>(<span class="params">self, user_id, message</span>): ...   <span class="comment"># 不该在这里！</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">generate_report</span>(<span class="params">self</span>): ...                 <span class="comment"># 不该在这里！</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 正例：拆分职责</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">UserService</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">create_user</span>(<span class="params">self, name, email</span>): ...</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">delete_user</span>(<span class="params">self, user_id</span>): ...</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">EmailService</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">send</span>(<span class="params">self, to, message</span>): ...</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">ReportService</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">generate</span>(<span class="params">self</span>): ...</span><br></pre></td></tr></table></figure><p><strong>职责划分示意图</strong>：</p><figure class="highlight plaintext"><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><span class="line">反例：一个类承担多个职责</span><br><span class="line">┌─────────────────────────────────────┐</span><br><span class="line">│           UserManager               │</span><br><span class="line">│  ┌─────────────┐                    │</span><br><span class="line">│  │ 用户管理    │                    │</span><br><span class="line">│  ├─────────────┤                    │</span><br><span class="line">│  │ 邮件发送    │ ← 不该在这里！     │</span><br><span class="line">│  ├─────────────┤                    │</span><br><span class="line">│  │ 报表生成    │ ← 不该在这里！     │</span><br><span class="line">│  └─────────────┘                    │</span><br><span class="line">└─────────────────────────────────────┘</span><br><span class="line"></span><br><span class="line">正例：每个类只有一个职责</span><br><span class="line">┌─────────────────┐  ┌─────────────────┐  ┌─────────────────┐</span><br><span class="line">│    UserService  │  │   EmailService  │  │  ReportService  │</span><br><span class="line">│  ┌───────────┐  │  │  ┌───────────┐  │  │  ┌───────────┐  │</span><br><span class="line">│  │ 用户管理  │  │  │  │ 邮件发送  │  │  │  │ 报表生成  │  │</span><br><span class="line">│  └───────────┘  │  │  └───────────┘  │  │  └───────────┘  │</span><br><span class="line">└─────────────────┘  └─────────────────┘  └─────────────────┘</span><br></pre></td></tr></table></figure><h3 id="1-2-开闭原则（OCP）"><a href="#1-2-开闭原则（OCP）" class="headerlink" title="1.2 开闭原则（OCP）"></a>1.2 开闭原则（OCP）</h3><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><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><span class="line"><span class="comment"># 反例：修改现有代码来添加新功能</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">AreaCalculator</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">calculate</span>(<span class="params">self, shape</span>):</span><br><span class="line">        <span class="keyword">if</span> <span class="built_in">isinstance</span>(shape, Rectangle):</span><br><span class="line">            <span class="keyword">return</span> shape.width * shape.height</span><br><span class="line">        <span class="keyword">elif</span> <span class="built_in">isinstance</span>(shape, Circle):    <span class="comment"># 新增形状要改这里！</span></span><br><span class="line">            <span class="keyword">return</span> <span class="number">3.14</span> * shape.radius ** <span class="number">2</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 正例：扩展而不是修改</span></span><br><span class="line"><span class="keyword">from</span> abc <span class="keyword">import</span> ABC, abstractmethod</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Shape</span>(<span class="title class_ inherited__">ABC</span>):</span><br><span class="line"><span class="meta">    @abstractmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">area</span>(<span class="params">self</span>) -&gt; <span class="built_in">float</span>: ...</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Rectangle</span>(<span class="title class_ inherited__">Shape</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, width, height</span>):</span><br><span class="line">        <span class="variable language_">self</span>.width = width</span><br><span class="line">        <span class="variable language_">self</span>.height = height</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">area</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>.width * <span class="variable language_">self</span>.height</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Circle</span>(<span class="title class_ inherited__">Shape</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, radius</span>):</span><br><span class="line">        <span class="variable language_">self</span>.radius = radius</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">area</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="number">3.14159</span> * <span class="variable language_">self</span>.radius ** <span class="number">2</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 新增三角形，不需要修改 AreaCalculator</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Triangle</span>(<span class="title class_ inherited__">Shape</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, base, height</span>):</span><br><span class="line">        <span class="variable language_">self</span>.base = base</span><br><span class="line">        <span class="variable language_">self</span>.height = height</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">area</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="number">0.5</span> * <span class="variable language_">self</span>.base * <span class="variable language_">self</span>.height</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">AreaCalculator</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">calculate</span>(<span class="params">self, shape: Shape</span>):</span><br><span class="line">        <span class="keyword">return</span> shape.area()</span><br></pre></td></tr></table></figure><p><strong>开闭原则示意图</strong>：</p><figure class="highlight plaintext"><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><span class="line">┌───────────────────────┐     ┌───────────────────────────────┐</span><br><span class="line">│     AreaCalculator    │     │ 新增形状（扩展，不修改原有代码） │</span><br><span class="line">│                       │     │                               │</span><br><span class="line">│  def calculate(shape):│     │ class Triangle(Shape):        │</span><br><span class="line">│      return shape.area()│   │     def area(): ...           │</span><br><span class="line">│                       │     │                               │</span><br><span class="line">│  对修改关闭 ← 不变    │     │ 对扩展开放 ← 新增              │</span><br><span class="line">└───────────────────────┘     └───────────────────────────────┘</span><br></pre></td></tr></table></figure><h3 id="1-3-里氏替换原则（LSP）"><a href="#1-3-里氏替换原则（LSP）" class="headerlink" title="1.3 里氏替换原则（LSP）"></a>1.3 里氏替换原则（LSP）</h3><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 反例：子类破坏了父类的契约</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Bird</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">fly</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;Flying&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Penguin</span>(<span class="title class_ inherited__">Bird</span>):   <span class="comment"># 企鹅是鸟，但不能飞！</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">fly</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">raise</span> Exception(<span class="string">&quot;Penguins can&#x27;t fly&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 正例：重新设计继承体系</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Bird</span>:</span><br><span class="line">    <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">FlyingBird</span>(<span class="title class_ inherited__">Bird</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">fly</span>(<span class="params">self</span>): ...</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Penguin</span>(<span class="title class_ inherited__">Bird</span>):   <span class="comment"># 企鹅是鸟，但不是 FlyingBird</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">swim</span>(<span class="params">self</span>): ...</span><br></pre></td></tr></table></figure><p><strong>里氏替换原则示意图</strong>：</p><figure class="highlight plaintext"><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><span class="line">反例：子类违反父类契约</span><br><span class="line">Bird ──→ fly() ──→ 抛出异常（企鹅不能飞）</span><br><span class="line">    │</span><br><span class="line">    └── Penguin（破坏了 fly 的契约）</span><br><span class="line"></span><br><span class="line">正例：正确的继承体系</span><br><span class="line">Bird</span><br><span class="line">    ├── FlyingBird ──→ fly()</span><br><span class="line">    │       ├── Sparrow</span><br><span class="line">    │       └── Eagle</span><br><span class="line">    │</span><br><span class="line">    └── Penguin ──→ swim()（不继承 fly）</span><br></pre></td></tr></table></figure><h3 id="1-4-接口隔离原则（ISP）"><a href="#1-4-接口隔离原则（ISP）" class="headerlink" title="1.4 接口隔离原则（ISP）"></a>1.4 接口隔离原则（ISP）</h3><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><span class="line"><span class="comment"># 反例：胖接口</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Machine</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">print</span>(<span class="params">self</span>): ...</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">scan</span>(<span class="params">self</span>): ...</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">fax</span>(<span class="params">self</span>): ...</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">OldPrinter</span>(<span class="title class_ inherited__">Machine</span>):   <span class="comment"># 老打印机不支持扫描和传真</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">scan</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">raise</span> NotImplementedError   <span class="comment"># 被迫实现不需要的方法</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">fax</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">raise</span> NotImplementedError</span><br><span class="line"></span><br><span class="line"><span class="comment"># 正例：拆分为小接口</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Printer</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">print</span>(<span class="params">self</span>): ...</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Scanner</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">scan</span>(<span class="params">self</span>): ...</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Fax</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">fax</span>(<span class="params">self</span>): ...</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">OldPrinter</span>(<span class="title class_ inherited__">Printer</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">print</span>(<span class="params">self</span>): ...</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">MultiFunctionDevice</span>(Printer, Scanner, Fax):</span><br><span class="line">    ...</span><br></pre></td></tr></table></figure><p><strong>接口隔离原则示意图</strong>：</p><figure class="highlight plaintext"><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><span class="line">反例：胖接口，被迫实现不需要的方法</span><br><span class="line">┌─────────────────────────────────────────────┐</span><br><span class="line">│              Machine（胖接口）              │</span><br><span class="line">│  print()  │  scan()  │  fax()             │</span><br><span class="line">└───────────┼──────────┼─────────────────────┘</span><br><span class="line">            │          │</span><br><span class="line">            ▼          ▼</span><br><span class="line">    ┌───────────────┐  ┌───────────────┐</span><br><span class="line">    │ OldPrinter    │  │ 被迫实现     │</span><br><span class="line">    │ 只需要 print()│  │ scan()/fax() │</span><br><span class="line">    └───────────────┘  └───────────────┘</span><br><span class="line"></span><br><span class="line">正例：接口隔离，按需实现</span><br><span class="line">┌──────────┐  ┌──────────┐  ┌──────────┐</span><br><span class="line">│ Printer  │  │ Scanner  │  │   Fax    │</span><br><span class="line">│ print()  │  │ scan()   │  │  fax()   │</span><br><span class="line">└────┬─────┘  └────┬─────┘  └────┬─────┘</span><br><span class="line">     │             │             │</span><br><span class="line">     ▼             └──────┬──────┘</span><br><span class="line">┌───────────────┐         │</span><br><span class="line">│ OldPrinter    │         ▼</span><br><span class="line">│ 只实现 print()│  ┌──────────────────────────┐</span><br><span class="line">└───────────────┘  │   MultiFunctionDevice    │</span><br><span class="line">                   │ 实现所有三个接口          │</span><br><span class="line">                   └──────────────────────────┘</span><br></pre></td></tr></table></figure><h3 id="1-5-依赖倒置原则（DIP）"><a href="#1-5-依赖倒置原则（DIP）" class="headerlink" title="1.5 依赖倒置原则（DIP）"></a>1.5 依赖倒置原则（DIP）</h3><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 反例：高层模块依赖低层模块</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">MySQLDatabase</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">query</span>(<span class="params">self, sql</span>): ...</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">UserRepository</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="variable language_">self</span>.db = MySQLDatabase()   <span class="comment"># 直接依赖具体实现</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 正例：依赖抽象</span></span><br><span class="line"><span class="keyword">from</span> abc <span class="keyword">import</span> ABC, abstractmethod</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Database</span>(<span class="title class_ inherited__">ABC</span>):</span><br><span class="line"><span class="meta">    @abstractmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">query</span>(<span class="params">self, sql</span>): ...</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">UserRepository</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, db: Database</span>):   <span class="comment"># 依赖接口</span></span><br><span class="line">        <span class="variable language_">self</span>.db = db</span><br><span class="line"></span><br><span class="line"><span class="comment"># 现在可以随意切换数据库</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">MySQLDatabase</span>(<span class="title class_ inherited__">Database</span>): ...</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">PostgreSQLDatabase</span>(<span class="title class_ inherited__">Database</span>): ...</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">MongoDB</span>(<span class="title class_ inherited__">Database</span>): ...</span><br><span class="line"></span><br><span class="line">repo = UserRepository(PostgreSQLDatabase())</span><br></pre></td></tr></table></figure><p><strong>依赖倒置原则示意图</strong>：</p><figure class="highlight plaintext"><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><span class="line">反例：高层依赖低层（紧耦合）</span><br><span class="line">┌─────────────────┐     ┌─────────────────┐</span><br><span class="line">│ UserRepository  │────→│ MySQLDatabase   │</span><br><span class="line">│   高层模块      │     │   低层模块      │</span><br><span class="line">└─────────────────┘     └─────────────────┘</span><br><span class="line">         ↑</span><br><span class="line">         │ 难以替换数据库</span><br><span class="line"></span><br><span class="line">正例：都依赖抽象（松耦合）</span><br><span class="line">┌─────────────────┐     ┌─────────────────┐</span><br><span class="line">│ UserRepository  │     │ MySQLDatabase   │</span><br><span class="line">│   高层模块      │     │ PostgreSQL      │</span><br><span class="line">└────────┬────────┘     └────────┬────────┘</span><br><span class="line">         │                      │</span><br><span class="line">         └──────────┬───────────┘</span><br><span class="line">                    ▼</span><br><span class="line">            ┌─────────────────┐</span><br><span class="line">            │   Database      │</span><br><span class="line">            │   抽象接口      │</span><br><span class="line">            └─────────────────┘</span><br><span class="line">                    ↑</span><br><span class="line">                    │ 可以轻松切换实现</span><br></pre></td></tr></table></figure><hr><h2 id="2-创建型模式"><a href="#2-创建型模式" class="headerlink" title="2. 创建型模式"></a>2. 创建型模式</h2><p>创建型模式关注对象的创建过程，将对象创建逻辑封装起来，使代码更加灵活。</p><h3 id="2-1-工厂模式"><a href="#2-1-工厂模式" class="headerlink" title="2.1 工厂模式"></a>2.1 工厂模式</h3><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><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><span class="line"><span class="keyword">from</span> abc <span class="keyword">import</span> ABC, abstractmethod</span><br><span class="line"></span><br><span class="line"><span class="comment"># 产品接口</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Notification</span>(<span class="title class_ inherited__">ABC</span>):</span><br><span class="line"><span class="meta">    @abstractmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">send</span>(<span class="params">self, message: <span class="built_in">str</span></span>): ...</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">EmailNotification</span>(<span class="title class_ inherited__">Notification</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">send</span>(<span class="params">self, message</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;Email: <span class="subst">&#123;message&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">SMSNotification</span>(<span class="title class_ inherited__">Notification</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">send</span>(<span class="params">self, message</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;SMS: <span class="subst">&#123;message&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">PushNotification</span>(<span class="title class_ inherited__">Notification</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">send</span>(<span class="params">self, message</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;Push: <span class="subst">&#123;message&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 简单工厂</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">NotificationFactory</span>:</span><br><span class="line"><span class="meta">    @staticmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">create</span>(<span class="params">type_: <span class="built_in">str</span></span>) -&gt; Notification:</span><br><span class="line">        notifications = &#123;</span><br><span class="line">            <span class="string">&quot;email&quot;</span>: EmailNotification,</span><br><span class="line">            <span class="string">&quot;sms&quot;</span>: SMSNotification,</span><br><span class="line">            <span class="string">&quot;push&quot;</span>: PushNotification,</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> type_ <span class="keyword">not</span> <span class="keyword">in</span> notifications:</span><br><span class="line">            <span class="keyword">raise</span> ValueError(<span class="string">f&quot;Unknown type: <span class="subst">&#123;type_&#125;</span>&quot;</span>)</span><br><span class="line">        <span class="keyword">return</span> notifications[type_]()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用</span></span><br><span class="line">notifier = NotificationFactory.create(<span class="string">&quot;email&quot;</span>)</span><br><span class="line">notifier.send(<span class="string">&quot;Hello!&quot;</span>)</span><br></pre></td></tr></table></figure><p><strong>工厂模式结构</strong>：</p><figure class="highlight plaintext"><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><span class="line">┌─────────────────────────────────────────────────────────────┐</span><br><span class="line">│                      客户端代码                               │</span><br><span class="line">│                                                            │</span><br><span class="line">│  notifier = NotificationFactory.create(&quot;email&quot;)            │</span><br><span class="line">│  notifier.send(&quot;Hello!&quot;)                                   │</span><br><span class="line">│                                                            │</span><br><span class="line">└────────────────────┬────────────────────────────────────────┘</span><br><span class="line">                     │</span><br><span class="line">                     ▼</span><br><span class="line">┌─────────────────────────────────────────────────────────────┐</span><br><span class="line">│              NotificationFactory（工厂）                     │</span><br><span class="line">│                                                            │</span><br><span class="line">│  create(type_) ──→ 根据类型返回对应的产品实例                │</span><br><span class="line">│                                                            │</span><br><span class="line">└────────────────────┬────────────────────────────────────────┘</span><br><span class="line">                     │</span><br><span class="line">                     ▼</span><br><span class="line">┌─────────────────────────────────────────────────────────────┐</span><br><span class="line">│              Notification（抽象产品）                        │</span><br><span class="line">│                                                            │</span><br><span class="line">│  ┌─────────────────────────────────────────────────────┐    │</span><br><span class="line">│  │  EmailNotification  │  SMSNotification            │    │</span><br><span class="line">│  │  PushNotification   │                             │    │</span><br><span class="line">│  └─────────────────────────────────────────────────────┘    │</span><br><span class="line">│                                                            │</span><br><span class="line">└─────────────────────────────────────────────────────────────┘</span><br></pre></td></tr></table></figure><h3 id="2-2-建造者模式"><a href="#2-2-建造者模式" class="headerlink" title="2.2 建造者模式"></a>2.2 建造者模式</h3><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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">HttpRequest</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;HTTP 请求对象。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="variable language_">self</span>.method = <span class="string">&quot;GET&quot;</span></span><br><span class="line">        <span class="variable language_">self</span>.url = <span class="string">&quot;&quot;</span></span><br><span class="line">        <span class="variable language_">self</span>.headers = &#123;&#125;</span><br><span class="line">        <span class="variable language_">self</span>.body = <span class="literal">None</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__repr__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="string">f&quot;HttpRequest(<span class="subst">&#123;self.method&#125;</span> <span class="subst">&#123;self.url&#125;</span>)&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">HttpRequestBuilder</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;建造者：分步骤构建复杂对象。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="variable language_">self</span>._request = HttpRequest()</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">set_method</span>(<span class="params">self, method: <span class="built_in">str</span></span>):</span><br><span class="line">        <span class="variable language_">self</span>._request.method = method</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">set_url</span>(<span class="params">self, url: <span class="built_in">str</span></span>):</span><br><span class="line">        <span class="variable language_">self</span>._request.url = url</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">add_header</span>(<span class="params">self, key: <span class="built_in">str</span>, value: <span class="built_in">str</span></span>):</span><br><span class="line">        <span class="variable language_">self</span>._request.headers[key] = value</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">set_body</span>(<span class="params">self, body: <span class="built_in">str</span></span>):</span><br><span class="line">        <span class="variable language_">self</span>._request.body = body</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">build</span>(<span class="params">self</span>) -&gt; HttpRequest:</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>._request</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用（链式调用）</span></span><br><span class="line">request = (</span><br><span class="line">    HttpRequestBuilder()</span><br><span class="line">    .set_method(<span class="string">&quot;POST&quot;</span>)</span><br><span class="line">    .set_url(<span class="string">&quot;https://api.example.com/users&quot;</span>)</span><br><span class="line">    .add_header(<span class="string">&quot;Content-Type&quot;</span>, <span class="string">&quot;application/json&quot;</span>)</span><br><span class="line">    .add_header(<span class="string">&quot;Authorization&quot;</span>, <span class="string">&quot;Bearer token123&quot;</span>)</span><br><span class="line">    .set_body(<span class="string">&#x27;&#123;&quot;name&quot;: &quot;Alice&quot;&#125;&#x27;</span>)</span><br><span class="line">    .build()</span><br><span class="line">)</span><br></pre></td></tr></table></figure><p><strong>建造者模式结构</strong>：</p><figure class="highlight plaintext"><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><span class="line">┌─────────────────────────────────────────────────────────────┐</span><br><span class="line">│                      客户端代码                               │</span><br><span class="line">│                                                            │</span><br><span class="line">│  HttpRequestBuilder()                                      │</span><br><span class="line">│      .set_method(&quot;POST&quot;)                                    │</span><br><span class="line">│      .set_url(url)                                          │</span><br><span class="line">│      .add_header(k, v)                                      │</span><br><span class="line">│      .build()                                               │</span><br><span class="line">│                                                            │</span><br><span class="line">└────────────────────┬────────────────────────────────────────┘</span><br><span class="line">                     │</span><br><span class="line">                     ▼</span><br><span class="line">┌─────────────────────────────────────────────────────────────┐</span><br><span class="line">│              HttpRequestBuilder（建造者）                     │</span><br><span class="line">│                                                            │</span><br><span class="line">│  set_method() / set_url() / add_header() / set_body()      │</span><br><span class="line">│                         │                                   │</span><br><span class="line">│                         ▼                                   │</span><br><span class="line">│              HttpRequest（产品）                              │</span><br><span class="line">│                                                            │</span><br><span class="line">└─────────────────────────────────────────────────────────────┘</span><br></pre></td></tr></table></figure><h3 id="2-3-单例模式"><a href="#2-3-单例模式" class="headerlink" title="2.3 单例模式"></a>2.3 单例模式</h3><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> threading</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">SingletonMeta</span>(<span class="title class_ inherited__">type</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;线程安全单例元类。&quot;&quot;&quot;</span></span><br><span class="line">    _instances = &#123;&#125;</span><br><span class="line">    _lock = threading.Lock()</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__call__</span>(<span class="params">cls, *args, **kwargs</span>):</span><br><span class="line">        <span class="keyword">if</span> cls <span class="keyword">not</span> <span class="keyword">in</span> cls._instances:</span><br><span class="line">            <span class="keyword">with</span> cls._lock:</span><br><span class="line">                <span class="keyword">if</span> cls <span class="keyword">not</span> <span class="keyword">in</span> cls._instances:</span><br><span class="line">                    cls._instances[cls] = <span class="built_in">super</span>().__call__(*args, **kwargs)</span><br><span class="line">        <span class="keyword">return</span> cls._instances[cls]</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Database</span>(metaclass=SingletonMeta):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, host=<span class="string">&quot;localhost&quot;</span></span>):</span><br><span class="line">        <span class="variable language_">self</span>.host = host</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用</span></span><br><span class="line">db1 = Database()</span><br><span class="line">db2 = Database()</span><br><span class="line"><span class="built_in">print</span>(db1 <span class="keyword">is</span> db2)   <span class="comment"># True（同一个实例）</span></span><br></pre></td></tr></table></figure><p><strong>单例模式结构</strong>：</p><figure class="highlight plaintext"><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><span class="line">┌─────────────────────────────────────────────────────────────┐</span><br><span class="line">│                      客户端代码                               │</span><br><span class="line">│                                                            │</span><br><span class="line">│  db1 = Database()  ──┐                                      │</span><br><span class="line">│  db2 = Database()  ──┼──→ 同一个实例                         │</span><br><span class="line">│  db3 = Database()  ──┘                                      │</span><br><span class="line">│                                                            │</span><br><span class="line">└────────────────────┬────────────────────────────────────────┘</span><br><span class="line">                     │</span><br><span class="line">                     ▼</span><br><span class="line">┌─────────────────────────────────────────────────────────────┐</span><br><span class="line">│                 Database（单例类）                            │</span><br><span class="line">│                                                            │</span><br><span class="line">│  ┌─────────────────────────────────────────────────────┐    │</span><br><span class="line">│  │              _instances = &#123;Database: instance&#125;      │    │</span><br><span class="line">│  │              （全局唯一实例）                          │    │</span><br><span class="line">│  └─────────────────────────────────────────────────────┘    │</span><br><span class="line">│                                                            │</span><br><span class="line">└─────────────────────────────────────────────────────────────┘</span><br></pre></td></tr></table></figure><h3 id="2-4-原型模式"><a href="#2-4-原型模式" class="headerlink" title="2.4 原型模式"></a>2.4 原型模式</h3><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> copy</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Prototype</span>(<span class="title class_ inherited__">ABC</span>):</span><br><span class="line"><span class="meta">    @abstractmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">clone</span>(<span class="params">self</span>): ...</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">ConcretePrototype</span>(<span class="title class_ inherited__">Prototype</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, value</span>):</span><br><span class="line">        <span class="variable language_">self</span>.value = value</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">clone</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> copy.deepcopy(<span class="variable language_">self</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用</span></span><br><span class="line">original = ConcretePrototype(<span class="number">42</span>)</span><br><span class="line">clone = original.clone()</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(original.value)   <span class="comment"># 42</span></span><br><span class="line"><span class="built_in">print</span>(clone.value)      <span class="comment"># 42</span></span><br><span class="line"><span class="built_in">print</span>(original <span class="keyword">is</span> clone)   <span class="comment"># False（不同对象，但值相同）</span></span><br></pre></td></tr></table></figure><h3 id="2-5-抽象工厂模式"><a href="#2-5-抽象工厂模式" class="headerlink" title="2.5 抽象工厂模式"></a>2.5 抽象工厂模式</h3><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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> abc <span class="keyword">import</span> ABC, abstractmethod</span><br><span class="line"></span><br><span class="line"><span class="comment"># 抽象产品族</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Button</span>(<span class="title class_ inherited__">ABC</span>):</span><br><span class="line"><span class="meta">    @abstractmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">render</span>(<span class="params">self</span>): ...</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Checkbox</span>(<span class="title class_ inherited__">ABC</span>):</span><br><span class="line"><span class="meta">    @abstractmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">render</span>(<span class="params">self</span>): ...</span><br><span class="line"></span><br><span class="line"><span class="comment"># 具体产品：Windows 风格</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">WindowsButton</span>(<span class="title class_ inherited__">Button</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">render</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;Windows Button&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">WindowsCheckbox</span>(<span class="title class_ inherited__">Checkbox</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">render</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;Windows Checkbox&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 具体产品：Mac 风格</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">MacButton</span>(<span class="title class_ inherited__">Button</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">render</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;Mac Button&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">MacCheckbox</span>(<span class="title class_ inherited__">Checkbox</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">render</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;Mac Checkbox&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 抽象工厂</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">GUIFactory</span>(<span class="title class_ inherited__">ABC</span>):</span><br><span class="line"><span class="meta">    @abstractmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">create_button</span>(<span class="params">self</span>) -&gt; Button: ...</span><br><span class="line"><span class="meta">    @abstractmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">create_checkbox</span>(<span class="params">self</span>) -&gt; Checkbox: ...</span><br><span class="line"></span><br><span class="line"><span class="comment"># 具体工厂</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">WindowsFactory</span>(<span class="title class_ inherited__">GUIFactory</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">create_button</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> WindowsButton()</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">create_checkbox</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> WindowsCheckbox()</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">MacFactory</span>(<span class="title class_ inherited__">GUIFactory</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">create_button</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> MacButton()</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">create_checkbox</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> MacCheckbox()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用</span></span><br><span class="line">factory = WindowsFactory()</span><br><span class="line">button = factory.create_button()</span><br><span class="line">checkbox = factory.create_checkbox()</span><br><span class="line">button.render()      <span class="comment"># Windows Button</span></span><br><span class="line">checkbox.render()    <span class="comment"># Windows Checkbox</span></span><br></pre></td></tr></table></figure><p><strong>抽象工厂模式结构</strong>：</p><figure class="highlight plaintext"><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><span class="line">┌─────────────────────────────────────────────────────────────┐</span><br><span class="line">│                      客户端代码                               │</span><br><span class="line">│                                                            │</span><br><span class="line">│  factory = WindowsFactory() / MacFactory()                 │</span><br><span class="line">│  button = factory.create_button()                          │</span><br><span class="line">│  checkbox = factory.create_checkbox()                      │</span><br><span class="line">│                                                            │</span><br><span class="line">└────────────────────┬────────────────────────────────────────┘</span><br><span class="line">                     │</span><br><span class="line">                     ▼</span><br><span class="line">┌─────────────────────────────────────────────────────────────┐</span><br><span class="line">│                  GUIFactory（抽象工厂）                      │</span><br><span class="line">│                                                            │</span><br><span class="line">│  ┌─────────────────────────────────────────────────────┐    │</span><br><span class="line">│  │  WindowsFactory  │  MacFactory                     │    │</span><br><span class="line">│  └──────────────────┼─────────────────────────────────┘    │</span><br><span class="line">│                     │                                       │</span><br><span class="line">│                     ▼                                       │</span><br><span class="line">│  ┌─────────────────────────────────────────────────────┐    │</span><br><span class="line">│  │  Button / Checkbox（抽象产品）                       │    │</span><br><span class="line">│  │                                                     │    │</span><br><span class="line">│  │  ┌─────────────────┐  ┌─────────────────┐          │    │</span><br><span class="line">│  │  │WindowsButton    │  │MacButton        │          │    │</span><br><span class="line">│  │  │WindowsCheckbox  │  │MacCheckbox      │          │    │</span><br><span class="line">│  │  └─────────────────┘  └─────────────────┘          │    │</span><br><span class="line">│  └─────────────────────────────────────────────────────┘    │</span><br><span class="line">└─────────────────────────────────────────────────────────────┘</span><br></pre></td></tr></table></figure><hr><h2 id="3-结构型模式"><a href="#3-结构型模式" class="headerlink" title="3. 结构型模式"></a>3. 结构型模式</h2><p>结构型模式关注如何将类和对象组合成更大的结构。</p><h3 id="3-1-适配器模式"><a href="#3-1-适配器模式" class="headerlink" title="3.1 适配器模式"></a>3.1 适配器模式</h3><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><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> abc <span class="keyword">import</span> ABC, abstractmethod</span><br><span class="line"></span><br><span class="line"><span class="comment"># 旧系统接口</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">OldPaymentGateway</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">make_payment</span>(<span class="params">self, amount</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;Old gateway: processing $<span class="subst">&#123;amount&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 新系统接口</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">PaymentProcessor</span>(<span class="title class_ inherited__">ABC</span>):</span><br><span class="line"><span class="meta">    @abstractmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">process</span>(<span class="params">self, amount: <span class="built_in">float</span>, currency: <span class="built_in">str</span></span>): ...</span><br><span class="line"></span><br><span class="line"><span class="comment"># 适配器</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">PaymentAdapter</span>(<span class="title class_ inherited__">PaymentProcessor</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, old_gateway: OldPaymentGateway</span>):</span><br><span class="line">        <span class="variable language_">self</span>._old = old_gateway</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">process</span>(<span class="params">self, amount: <span class="built_in">float</span>, currency: <span class="built_in">str</span></span>):</span><br><span class="line">        <span class="keyword">if</span> currency != <span class="string">&quot;USD&quot;</span>:</span><br><span class="line">            amount = <span class="variable language_">self</span>._convert_currency(amount, currency, <span class="string">&quot;USD&quot;</span>)</span><br><span class="line">        <span class="variable language_">self</span>._old.make_payment(amount)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">_convert_currency</span>(<span class="params">self, amount, from_c, to_c</span>):</span><br><span class="line">        ...</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用</span></span><br><span class="line">old = OldPaymentGateway()</span><br><span class="line">adapter = PaymentAdapter(old)</span><br><span class="line">adapter.process(<span class="number">100</span>, <span class="string">&quot;EUR&quot;</span>)   <span class="comment"># 新接口调用旧实现</span></span><br></pre></td></tr></table></figure><p><strong>适配器模式结构</strong>：</p><figure class="highlight plaintext"><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><span class="line">┌─────────────────────────────────────────────────────────────┐</span><br><span class="line">│                      客户端代码                               │</span><br><span class="line">│                                                            │</span><br><span class="line">│  processor.process(amount, currency)                       │</span><br><span class="line">│                                                            │</span><br><span class="line">└────────────────────┬────────────────────────────────────────┘</span><br><span class="line">                     │</span><br><span class="line">                     ▼</span><br><span class="line">┌─────────────────────────────────────────────────────────────┐</span><br><span class="line">│              PaymentAdapter（适配器）                         │</span><br><span class="line">│                                                            │</span><br><span class="line">│  process(amount, currency)                                  │</span><br><span class="line">│         │                                                   │</span><br><span class="line">│         │ 转换接口                                           │</span><br><span class="line">│         ▼                                                   │</span><br><span class="line">│  old_gateway.make_payment(amount)                          │</span><br><span class="line">│                                                            │</span><br><span class="line">└────────────────────┬────────────────────────────────────────┘</span><br><span class="line">                     │</span><br><span class="line">                     ▼</span><br><span class="line">┌─────────────────────────────────────────────────────────────┐</span><br><span class="line">│              OldPaymentGateway（被适配者）                    │</span><br><span class="line">│                                                            │</span><br><span class="line">│  make_payment(amount)                                       │</span><br><span class="line">│                                                            │</span><br><span class="line">└─────────────────────────────────────────────────────────────┘</span><br></pre></td></tr></table></figure><h3 id="3-2-装饰器模式（与-Python-装饰器的关系）"><a href="#3-2-装饰器模式（与-Python-装饰器的关系）" class="headerlink" title="3.2 装饰器模式（与 Python 装饰器的关系）"></a>3.2 装饰器模式（与 Python 装饰器的关系）</h3><p>动态地给对象添加额外的职责。Python 的 <code>@decorator</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Coffee</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;基础组件。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">cost</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="number">5</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">description</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;Coffee&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">CoffeeDecorator</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;装饰器基类。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, coffee: Coffee</span>):</span><br><span class="line">        <span class="variable language_">self</span>._coffee = coffee</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">cost</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>._coffee.cost()</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">description</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>._coffee.description()</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Milk</span>(<span class="title class_ inherited__">CoffeeDecorator</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">cost</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>._coffee.cost() + <span class="number">1</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">description</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>._coffee.description() + <span class="string">&quot;, Milk&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Sugar</span>(<span class="title class_ inherited__">CoffeeDecorator</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">cost</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>._coffee.cost() + <span class="number">0.5</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">description</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>._coffee.description() + <span class="string">&quot;, Sugar&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用</span></span><br><span class="line">coffee = Coffee()</span><br><span class="line">coffee = Milk(coffee)</span><br><span class="line">coffee = Sugar(coffee)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;<span class="subst">&#123;coffee.description()&#125;</span>: $<span class="subst">&#123;coffee.cost()&#125;</span>&quot;</span>)</span><br><span class="line"><span class="comment"># Coffee, Milk, Sugar: $6.5</span></span><br></pre></td></tr></table></figure><p><strong>装饰器模式结构</strong>：</p><figure class="highlight plaintext"><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><span class="line">┌─────────────────────────────────────────────────────────────┐</span><br><span class="line">│                      客户端代码                               │</span><br><span class="line">│                                                            │</span><br><span class="line">│  coffee = Coffee()                                          │</span><br><span class="line">│  coffee = Milk(coffee)                                      │</span><br><span class="line">│  coffee = Sugar(coffee)                                     │</span><br><span class="line">│  coffee.cost() → 6.5                                        │</span><br><span class="line">│                                                            │</span><br><span class="line">└────────────────────┬────────────────────────────────────────┘</span><br><span class="line">                     │</span><br><span class="line">                     ▼</span><br><span class="line">┌─────────────────────────────────────────────────────────────┐</span><br><span class="line">│                      Sugar 装饰器                            │</span><br><span class="line">│  ┌─────────────────────────────────────────────────────┐    │</span><br><span class="line">│  │                   Milk 装饰器                        │    │</span><br><span class="line">│  │  ┌───────────────────────────────────────────────┐  │    │</span><br><span class="line">│  │  │              Coffee（基础组件）                │  │    │</span><br><span class="line">│  │  │              cost() = 5                        │  │    │</span><br><span class="line">│  │  └───────────────────────────────────────────────┘  │    │</span><br><span class="line">│  │              Milk.cost() = 5 + 1 = 6              │    │</span><br><span class="line">│  └─────────────────────────────────────────────────────┘    │</span><br><span class="line">│              Sugar.cost() = 6 + 0.5 = 6.5                   │</span><br><span class="line">└─────────────────────────────────────────────────────────────┘</span><br></pre></td></tr></table></figure><h3 id="3-3-代理模式"><a href="#3-3-代理模式" class="headerlink" title="3.3 代理模式"></a>3.3 代理模式</h3><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><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Image</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;真实对象。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, filename</span>):</span><br><span class="line">        <span class="variable language_">self</span>.filename = filename</span><br><span class="line">        <span class="variable language_">self</span>._load()</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">_load</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;Loading heavy image: <span class="subst">&#123;self.filename&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">display</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;Displaying: <span class="subst">&#123;self.filename&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">ImageProxy</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;代理：延迟加载。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, filename</span>):</span><br><span class="line">        <span class="variable language_">self</span>.filename = filename</span><br><span class="line">        <span class="variable language_">self</span>._image = <span class="literal">None</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">display</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">if</span> <span class="variable language_">self</span>._image <span class="keyword">is</span> <span class="literal">None</span>:</span><br><span class="line">            <span class="variable language_">self</span>._image = Image(<span class="variable language_">self</span>.filename)</span><br><span class="line">        <span class="variable language_">self</span>._image.display()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用</span></span><br><span class="line">images = [ImageProxy(<span class="string">f&quot;image<span class="subst">&#123;i&#125;</span>.jpg&quot;</span>) <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">10</span>)]</span><br><span class="line"><span class="comment"># 此时没有加载任何图片</span></span><br><span class="line">images[<span class="number">0</span>].display()   <span class="comment"># 第一次访问时才加载</span></span><br><span class="line">images[<span class="number">0</span>].display()   <span class="comment"># 第二次直接显示（已缓存）</span></span><br></pre></td></tr></table></figure><p><strong>代理模式结构</strong>：</p><figure class="highlight plaintext"><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><span class="line">┌─────────────────────────────────────────────────────────────┐</span><br><span class="line">│                      客户端代码                               │</span><br><span class="line">│                                                            │</span><br><span class="line">│  proxy.display()                                            │</span><br><span class="line">│                                                            │</span><br><span class="line">└────────────────────┬────────────────────────────────────────┘</span><br><span class="line">                     │</span><br><span class="line">                     ▼</span><br><span class="line">┌─────────────────────────────────────────────────────────────┐</span><br><span class="line">│              ImageProxy（代理）                               │</span><br><span class="line">│                                                            │</span><br><span class="line">│  ┌─────────────────────────────────────────────────────┐    │</span><br><span class="line">│  │  第一次调用: 创建 Image 对象                         │    │</span><br><span class="line">│  │  后续调用: 直接使用缓存的 Image                      │    │</span><br><span class="line">│  └─────────────────────────────────────────────────────┘    │</span><br><span class="line">│                                                            │</span><br><span class="line">└────────────────────┬────────────────────────────────────────┘</span><br><span class="line">                     │ 延迟加载</span><br><span class="line">                     ▼</span><br><span class="line">┌─────────────────────────────────────────────────────────────┐</span><br><span class="line">│                 Image（真实对象）                              │</span><br><span class="line">│                                                            │</span><br><span class="line">│  _load() → 耗时操作                                         │</span><br><span class="line">│  display()                                                  │</span><br><span class="line">│                                                            │</span><br><span class="line">└─────────────────────────────────────────────────────────────┘</span><br></pre></td></tr></table></figure><h3 id="3-4-组合模式"><a href="#3-4-组合模式" class="headerlink" title="3.4 组合模式"></a>3.4 组合模式</h3><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><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><span class="line"><span class="keyword">from</span> abc <span class="keyword">import</span> ABC, abstractmethod</span><br><span class="line"><span class="keyword">from</span> typing <span class="keyword">import</span> <span class="type">List</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Component</span>(<span class="title class_ inherited__">ABC</span>):</span><br><span class="line"><span class="meta">    @abstractmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">display</span>(<span class="params">self, depth=<span class="number">0</span></span>): ...</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Leaf</span>(<span class="title class_ inherited__">Component</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, name</span>):</span><br><span class="line">        <span class="variable language_">self</span>.name = name</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">display</span>(<span class="params">self, depth=<span class="number">0</span></span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;  &quot;</span> * depth + <span class="string">f&quot;- <span class="subst">&#123;self.name&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Composite</span>(<span class="title class_ inherited__">Component</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, name</span>):</span><br><span class="line">        <span class="variable language_">self</span>.name = name</span><br><span class="line">        <span class="variable language_">self</span>._children: <span class="type">List</span>[Component] = []</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">add</span>(<span class="params">self, child: Component</span>):</span><br><span class="line">        <span class="variable language_">self</span>._children.append(child)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">remove</span>(<span class="params">self, child: Component</span>):</span><br><span class="line">        <span class="variable language_">self</span>._children.remove(child)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">display</span>(<span class="params">self, depth=<span class="number">0</span></span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;  &quot;</span> * depth + <span class="string">f&quot;+ <span class="subst">&#123;self.name&#125;</span>&quot;</span>)</span><br><span class="line">        <span class="keyword">for</span> child <span class="keyword">in</span> <span class="variable language_">self</span>._children:</span><br><span class="line">            child.display(depth + <span class="number">1</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用</span></span><br><span class="line">root = Composite(<span class="string">&quot;root&quot;</span>)</span><br><span class="line">root.add(Leaf(<span class="string">&quot;leaf1&quot;</span>))</span><br><span class="line">root.add(Leaf(<span class="string">&quot;leaf2&quot;</span>))</span><br><span class="line"></span><br><span class="line">branch = Composite(<span class="string">&quot;branch&quot;</span>)</span><br><span class="line">branch.add(Leaf(<span class="string">&quot;leaf3&quot;</span>))</span><br><span class="line">branch.add(Leaf(<span class="string">&quot;leaf4&quot;</span>))</span><br><span class="line">root.add(branch)</span><br><span class="line"></span><br><span class="line">root.display()</span><br><span class="line"><span class="comment"># + root</span></span><br><span class="line"><span class="comment">#   - leaf1</span></span><br><span class="line"><span class="comment">#   - leaf2</span></span><br><span class="line"><span class="comment">#   + branch</span></span><br><span class="line"><span class="comment">#     - leaf3</span></span><br><span class="line"><span class="comment">#     - leaf4</span></span><br></pre></td></tr></table></figure><h3 id="3-5-享元模式"><a href="#3-5-享元模式" class="headerlink" title="3.5 享元模式"></a>3.5 享元模式</h3><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><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Flyweight</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, intrinsic_state</span>):</span><br><span class="line">        <span class="variable language_">self</span>.intrinsic_state = intrinsic_state</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">operation</span>(<span class="params">self, extrinsic_state</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;Flyweight: <span class="subst">&#123;self.intrinsic_state&#125;</span>, <span class="subst">&#123;extrinsic_state&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">FlyweightFactory</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="variable language_">self</span>._flyweights = &#123;&#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_flyweight</span>(<span class="params">self, key</span>):</span><br><span class="line">        <span class="keyword">if</span> key <span class="keyword">not</span> <span class="keyword">in</span> <span class="variable language_">self</span>._flyweights:</span><br><span class="line">            <span class="variable language_">self</span>._flyweights[key] = Flyweight(key)</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>._flyweights[key]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用</span></span><br><span class="line">factory = FlyweightFactory()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 获取享元对象</span></span><br><span class="line">f1 = factory.get_flyweight(<span class="string">&quot;key1&quot;</span>)</span><br><span class="line">f2 = factory.get_flyweight(<span class="string">&quot;key1&quot;</span>)</span><br><span class="line">f3 = factory.get_flyweight(<span class="string">&quot;key2&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(f1 <span class="keyword">is</span> f2)   <span class="comment"># True（同一个对象）</span></span><br><span class="line"><span class="built_in">print</span>(f1 <span class="keyword">is</span> f3)   <span class="comment"># False（不同对象）</span></span><br><span class="line"></span><br><span class="line">f1.operation(<span class="string">&quot;state1&quot;</span>)</span><br><span class="line">f2.operation(<span class="string">&quot;state2&quot;</span>)</span><br></pre></td></tr></table></figure><h3 id="3-6-门面模式"><a href="#3-6-门面模式" class="headerlink" title="3.6 门面模式"></a>3.6 门面模式</h3><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><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">CPU</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">boot</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;CPU booting&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Memory</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">load</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;Memory loading&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">HardDrive</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">read</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;HardDrive reading&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Computer</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;门面：简化复杂子系统的访问。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="variable language_">self</span>.cpu = CPU()</span><br><span class="line">        <span class="variable language_">self</span>.memory = Memory()</span><br><span class="line">        <span class="variable language_">self</span>.hard_drive = HardDrive()</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">start</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="variable language_">self</span>.cpu.boot()</span><br><span class="line">        <span class="variable language_">self</span>.memory.load()</span><br><span class="line">        <span class="variable language_">self</span>.hard_drive.read()</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;Computer started!&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用</span></span><br><span class="line">computer = Computer()</span><br><span class="line">computer.start()   <span class="comment"># 一个调用完成所有启动流程</span></span><br></pre></td></tr></table></figure><hr><h2 id="4-行为型模式"><a href="#4-行为型模式" class="headerlink" title="4. 行为型模式"></a>4. 行为型模式</h2><p>行为型模式关注对象之间的通信和职责分配。</p><h3 id="4-1-观察者模式"><a href="#4-1-观察者模式" class="headerlink" title="4.1 观察者模式"></a>4.1 观察者模式</h3><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><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><span class="line"><span class="keyword">from</span> typing <span class="keyword">import</span> <span class="type">List</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Observer</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">update</span>(<span class="params">self, event: <span class="built_in">str</span>, data: <span class="built_in">dict</span></span>):</span><br><span class="line">        <span class="keyword">raise</span> NotImplementedError</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Subject</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="variable language_">self</span>._observers: <span class="type">List</span>[Observer] = []</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">attach</span>(<span class="params">self, observer: Observer</span>):</span><br><span class="line">        <span class="variable language_">self</span>._observers.append(observer)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">detach</span>(<span class="params">self, observer: Observer</span>):</span><br><span class="line">        <span class="variable language_">self</span>._observers.remove(observer)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">notify</span>(<span class="params">self, event: <span class="built_in">str</span>, data: <span class="built_in">dict</span></span>):</span><br><span class="line">        <span class="keyword">for</span> observer <span class="keyword">in</span> <span class="variable language_">self</span>._observers:</span><br><span class="line">            observer.update(event, data)</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">EmailNotifier</span>(<span class="title class_ inherited__">Observer</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">update</span>(<span class="params">self, event, data</span>):</span><br><span class="line">        <span class="keyword">if</span> event == <span class="string">&quot;user_registered&quot;</span>:</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">f&quot;Sending welcome email to <span class="subst">&#123;data[<span class="string">&#x27;email&#x27;</span>]&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">AnalyticsTracker</span>(<span class="title class_ inherited__">Observer</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">update</span>(<span class="params">self, event, data</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;Tracking event: <span class="subst">&#123;event&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">AuditLogger</span>(<span class="title class_ inherited__">Observer</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">update</span>(<span class="params">self, event, data</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;Audit: <span class="subst">&#123;event&#125;</span> at <span class="subst">&#123;data.get(<span class="string">&#x27;timestamp&#x27;</span>)&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用</span></span><br><span class="line">subject = Subject()</span><br><span class="line">subject.attach(EmailNotifier())</span><br><span class="line">subject.attach(AnalyticsTracker())</span><br><span class="line">subject.attach(AuditLogger())</span><br><span class="line"></span><br><span class="line">subject.notify(<span class="string">&quot;user_registered&quot;</span>, &#123;<span class="string">&quot;email&quot;</span>: <span class="string">&quot;alice@example.com&quot;</span>, <span class="string">&quot;timestamp&quot;</span>: <span class="string">&quot;2024-01-01&quot;</span>&#125;)</span><br></pre></td></tr></table></figure><p><strong>观察者模式结构</strong>：</p><figure class="highlight plaintext"><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><span class="line">┌─────────────────────────────────────────────────────────────┐</span><br><span class="line">│                      Subject（主题）                          │</span><br><span class="line">│                                                            │</span><br><span class="line">│  ┌─────────────────────────────────────────────────────┐    │</span><br><span class="line">│  │  _observers = [EmailNotifier,                        │    │</span><br><span class="line">│  │                AnalyticsTracker,                     │    │</span><br><span class="line">│  │                AuditLogger]                          │    │</span><br><span class="line">│  └─────────────────────────────────────────────────────┘    │</span><br><span class="line">│                                                            │</span><br><span class="line">│  notify(event, data) ──→ 通知所有观察者                      │</span><br><span class="line">│                                                            │</span><br><span class="line">└────────────────────┬────────────────────────────────────────┘</span><br><span class="line">                     │</span><br><span class="line">       ┌─────────────┼─────────────┬─────────────┐</span><br><span class="line">       ▼             ▼             ▼             ▼</span><br><span class="line">┌──────────────┐ ┌──────────────┐ ┌──────────────┐</span><br><span class="line">│EmailNotifier │ │AnalyticsTracker│ │ AuditLogger  │</span><br><span class="line">└──────────────┘ └──────────────┘ └──────────────┘</span><br></pre></td></tr></table></figure><h3 id="4-2-策略模式"><a href="#4-2-策略模式" class="headerlink" title="4.2 策略模式"></a>4.2 策略模式</h3><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><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><span class="line"><span class="keyword">from</span> typing <span class="keyword">import</span> <span class="type">Callable</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Python 中策略模式可以用函数更简洁地实现</span></span><br><span class="line">DiscountStrategy = <span class="type">Callable</span>[[<span class="built_in">float</span>], <span class="built_in">float</span>]</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">no_discount</span>(<span class="params">price: <span class="built_in">float</span></span>) -&gt; <span class="built_in">float</span>:</span><br><span class="line">    <span class="keyword">return</span> price</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">percentage_discount</span>(<span class="params">percent: <span class="built_in">float</span></span>) -&gt; DiscountStrategy:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">apply</span>(<span class="params">price: <span class="built_in">float</span></span>) -&gt; <span class="built_in">float</span>:</span><br><span class="line">        <span class="keyword">return</span> price * (<span class="number">1</span> - percent / <span class="number">100</span>)</span><br><span class="line">    <span class="keyword">return</span> apply</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">fixed_discount</span>(<span class="params">amount: <span class="built_in">float</span></span>) -&gt; DiscountStrategy:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">apply</span>(<span class="params">price: <span class="built_in">float</span></span>) -&gt; <span class="built_in">float</span>:</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">max</span>(<span class="number">0</span>, price - amount)</span><br><span class="line">    <span class="keyword">return</span> apply</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Order</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, price: <span class="built_in">float</span>, strategy: DiscountStrategy = no_discount</span>):</span><br><span class="line">        <span class="variable language_">self</span>.price = price</span><br><span class="line">        <span class="variable language_">self</span>.strategy = strategy</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">final_price</span>(<span class="params">self</span>) -&gt; <span class="built_in">float</span>:</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>.strategy(<span class="variable language_">self</span>.price)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用</span></span><br><span class="line">order1 = Order(<span class="number">100</span>, percentage_discount(<span class="number">20</span>))</span><br><span class="line">order2 = Order(<span class="number">100</span>, fixed_discount(<span class="number">30</span>))</span><br><span class="line"><span class="built_in">print</span>(order1.final_price())   <span class="comment"># 80.0</span></span><br><span class="line"><span class="built_in">print</span>(order2.final_price())   <span class="comment"># 70.0</span></span><br></pre></td></tr></table></figure><h3 id="4-3-模板方法模式"><a href="#4-3-模板方法模式" class="headerlink" title="4.3 模板方法模式"></a>4.3 模板方法模式</h3><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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> abc <span class="keyword">import</span> ABC, abstractmethod</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">DataImporter</span>(<span class="title class_ inherited__">ABC</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;模板：定义导入流程的骨架。&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">import_data</span>(<span class="params">self, filepath</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;模板方法，定义算法骨架。&quot;&quot;&quot;</span></span><br><span class="line">        raw = <span class="variable language_">self</span>._read_file(filepath)</span><br><span class="line">        parsed = <span class="variable language_">self</span>._parse(raw)</span><br><span class="line">        validated = <span class="variable language_">self</span>._validate(parsed)</span><br><span class="line">        <span class="variable language_">self</span>._save(validated)</span><br><span class="line">        <span class="variable language_">self</span>._post_process()</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">_read_file</span>(<span class="params">self, filepath</span>):</span><br><span class="line">        <span class="keyword">with</span> <span class="built_in">open</span>(filepath, <span class="string">&quot;r&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">            <span class="keyword">return</span> f.read()</span><br><span class="line"></span><br><span class="line"><span class="meta">    @abstractmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">_parse</span>(<span class="params">self, raw_data</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;子类必须实现。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">_validate</span>(<span class="params">self, data</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;钩子：默认实现，子类可覆盖。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">return</span> data</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">_save</span>(<span class="params">self, data</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;Saving <span class="subst">&#123;<span class="built_in">len</span>(data)&#125;</span> records&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">_post_process</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;钩子：可选覆盖。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">CSVImporter</span>(<span class="title class_ inherited__">DataImporter</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">_parse</span>(<span class="params">self, raw_data</span>):</span><br><span class="line">        <span class="keyword">import</span> csv</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">list</span>(csv.reader(raw_data.splitlines()))</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">JSONImporter</span>(<span class="title class_ inherited__">DataImporter</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">_parse</span>(<span class="params">self, raw_data</span>):</span><br><span class="line">        <span class="keyword">import</span> json</span><br><span class="line">        <span class="keyword">return</span> json.loads(raw_data)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用</span></span><br><span class="line">CSVImporter().import_data(<span class="string">&quot;data.csv&quot;</span>)</span><br><span class="line">JSONImporter().import_data(<span class="string">&quot;data.json&quot;</span>)</span><br></pre></td></tr></table></figure><p><strong>模板方法模式结构</strong>：</p><figure class="highlight plaintext"><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><span class="line">┌─────────────────────────────────────────────────────────────┐</span><br><span class="line">│              DataImporter（抽象模板类）                       │</span><br><span class="line">│                                                            │</span><br><span class="line">│  import_data(filepath)  ← 模板方法（固定流程）               │</span><br><span class="line">│      │                                                      │</span><br><span class="line">│      ├── _read_file()    ← 固定步骤                         │</span><br><span class="line">│      ├── _parse()        ← 抽象步骤（子类实现）              │</span><br><span class="line">│      ├── _validate()     ← 钩子（可选覆盖）                  │</span><br><span class="line">│      ├── _save()         ← 固定步骤                         │</span><br><span class="line">│      └── _post_process() ← 钩子（可选覆盖）                  │</span><br><span class="line">│                                                            │</span><br><span class="line">└────────────────────┬────────────────────────────────────────┘</span><br><span class="line">                     │</span><br><span class="line">              ┌──────┴──────┐</span><br><span class="line">              ▼             ▼</span><br><span class="line">    ┌───────────────┐  ┌───────────────┐</span><br><span class="line">    │ CSVImporter   │  │ JSONImporter  │</span><br><span class="line">    │ _parse: CSV   │  │ _parse: JSON  │</span><br><span class="line">    └───────────────┘  └───────────────┘</span><br></pre></td></tr></table></figure><h3 id="4-4-责任链模式"><a href="#4-4-责任链模式" class="headerlink" title="4.4 责任链模式"></a>4.4 责任链模式</h3><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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> typing <span class="keyword">import</span> <span class="type">Optional</span></span><br><span class="line"><span class="keyword">from</span> abc <span class="keyword">import</span> ABC, abstractmethod</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Handler</span>(<span class="title class_ inherited__">ABC</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="variable language_">self</span>._<span class="built_in">next</span>: <span class="type">Optional</span>[Handler] = <span class="literal">None</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">set_next</span>(<span class="params">self, handler: <span class="string">&quot;Handler&quot;</span></span>) -&gt; <span class="string">&quot;Handler&quot;</span>:</span><br><span class="line">        <span class="variable language_">self</span>._<span class="built_in">next</span> = handler</span><br><span class="line">        <span class="keyword">return</span> handler</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">handle</span>(<span class="params">self, request</span>):</span><br><span class="line">        <span class="keyword">if</span> <span class="variable language_">self</span>._<span class="built_in">next</span>:</span><br><span class="line">            <span class="keyword">return</span> <span class="variable language_">self</span>._<span class="built_in">next</span>.handle(request)</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">None</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">AuthHandler</span>(<span class="title class_ inherited__">Handler</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">handle</span>(<span class="params">self, request</span>):</span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> request.get(<span class="string">&quot;token&quot;</span>):</span><br><span class="line">            <span class="keyword">return</span> <span class="string">&quot;Unauthorized&quot;</span></span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;Auth passed&quot;</span>)</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">super</span>().handle(request)</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">RateLimitHandler</span>(<span class="title class_ inherited__">Handler</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="built_in">super</span>().__init__()</span><br><span class="line">        <span class="variable language_">self</span>.requests = <span class="number">0</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">handle</span>(<span class="params">self, request</span>):</span><br><span class="line">        <span class="variable language_">self</span>.requests += <span class="number">1</span></span><br><span class="line">        <span class="keyword">if</span> <span class="variable language_">self</span>.requests &gt; <span class="number">100</span>:</span><br><span class="line">            <span class="keyword">return</span> <span class="string">&quot;Rate limit exceeded&quot;</span></span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;Rate limit ok&quot;</span>)</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">super</span>().handle(request)</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">ValidationHandler</span>(<span class="title class_ inherited__">Handler</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">handle</span>(<span class="params">self, request</span>):</span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> request.get(<span class="string">&quot;data&quot;</span>):</span><br><span class="line">            <span class="keyword">return</span> <span class="string">&quot;Invalid data&quot;</span></span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;Validation passed&quot;</span>)</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">super</span>().handle(request)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 组装责任链</span></span><br><span class="line">auth = AuthHandler()</span><br><span class="line">rate = RateLimitHandler()</span><br><span class="line">validate = ValidationHandler()</span><br><span class="line"></span><br><span class="line">auth.set_next(rate).set_next(validate)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用</span></span><br><span class="line">result = auth.handle(&#123;<span class="string">&quot;token&quot;</span>: <span class="string">&quot;abc&quot;</span>, <span class="string">&quot;data&quot;</span>: &#123;<span class="string">&quot;name&quot;</span>: <span class="string">&quot;Alice&quot;</span>&#125;&#125;)</span><br><span class="line"><span class="built_in">print</span>(result)   <span class="comment"># None（所有处理通过）</span></span><br></pre></td></tr></table></figure><p><strong>责任链模式结构</strong>：</p><figure class="highlight plaintext"><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><span class="line">┌─────────────────────────────────────────────────────────────┐</span><br><span class="line">│                      客户端代码                               │</span><br><span class="line">│                                                            │</span><br><span class="line">│  auth.handle(request)                                       │</span><br><span class="line">│                                                            │</span><br><span class="line">└────────────────────┬────────────────────────────────────────┘</span><br><span class="line">                     │</span><br><span class="line">                     ▼</span><br><span class="line">┌──────────────────┐    ┌──────────────────┐    ┌──────────────────┐</span><br><span class="line">│ AuthHandler      │───→│ RateLimitHandler │───→│ ValidationHandler│</span><br><span class="line">│ 验证 token       │    │ 检查请求频率     │    │ 验证数据         │</span><br><span class="line">└──────────────────┘    └──────────────────┘    └──────────────────┘</span><br></pre></td></tr></table></figure><hr><h2 id="5-Pythonic-的架构模式"><a href="#5-Pythonic-的架构模式" class="headerlink" title="5. Pythonic 的架构模式"></a>5. Pythonic 的架构模式</h2><h3 id="5-1-依赖注入"><a href="#5-1-依赖注入" class="headerlink" title="5.1 依赖注入"></a>5.1 依赖注入</h3><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><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><span class="line"><span class="keyword">from</span> typing <span class="keyword">import</span> Protocol</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Database</span>(<span class="title class_ inherited__">Protocol</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_user</span>(<span class="params">self, user_id: <span class="built_in">int</span></span>) -&gt; <span class="built_in">dict</span>: ...</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">save_user</span>(<span class="params">self, user: <span class="built_in">dict</span></span>) -&gt; <span class="literal">None</span>: ...</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">UserService</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;依赖注入：通过构造函数传入依赖。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, db: Database, notifier</span>):</span><br><span class="line">        <span class="variable language_">self</span>.db = db</span><br><span class="line">        <span class="variable language_">self</span>.notifier = notifier</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">register_user</span>(<span class="params">self, name: <span class="built_in">str</span>, email: <span class="built_in">str</span></span>):</span><br><span class="line">        user = &#123;<span class="string">&quot;name&quot;</span>: name, <span class="string">&quot;email&quot;</span>: email&#125;</span><br><span class="line">        <span class="variable language_">self</span>.db.save_user(user)</span><br><span class="line">        <span class="variable language_">self</span>.notifier.send(email, <span class="string">&quot;Welcome!&quot;</span>)</span><br><span class="line">        <span class="keyword">return</span> user</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用</span></span><br><span class="line"><span class="keyword">from</span> unittest.mock <span class="keyword">import</span> Mock</span><br><span class="line"></span><br><span class="line"><span class="comment"># 测试时注入 Mock</span></span><br><span class="line">mock_db = Mock()</span><br><span class="line">mock_db.get_user.return_value = &#123;<span class="string">&quot;name&quot;</span>: <span class="string">&quot;Alice&quot;</span>&#125;</span><br><span class="line">mock_notifier = Mock()</span><br><span class="line"></span><br><span class="line">service = UserService(mock_db, mock_notifier)</span><br><span class="line">user = service.register_user(<span class="string">&quot;Bob&quot;</span>, <span class="string">&quot;bob@example.com&quot;</span>)</span><br><span class="line"></span><br><span class="line">mock_db.save_user.assert_called_once()</span><br><span class="line">mock_notifier.send.assert_called_once_with(<span class="string">&quot;bob@example.com&quot;</span>, <span class="string">&quot;Welcome!&quot;</span>)</span><br></pre></td></tr></table></figure><p><strong>依赖注入示意图</strong>：</p><figure class="highlight plaintext"><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><span class="line">┌─────────────────────────────────────────────────────────────┐</span><br><span class="line">│                      组装层（DI Container）                   │</span><br><span class="line">│                                                            │</span><br><span class="line">│  db = MySQLDatabase()                                       │</span><br><span class="line">│  notifier = EmailNotifier()                                 │</span><br><span class="line">│  service = UserService(db, notifier)  ← 注入依赖            │</span><br><span class="line">│                                                            │</span><br><span class="line">└─────────────────────────────────────────────────────────────┘</span><br></pre></td></tr></table></figure><h3 id="5-2-仓储模式（Repository）"><a href="#5-2-仓储模式（Repository）" class="headerlink" title="5.2 仓储模式（Repository）"></a>5.2 仓储模式（Repository）</h3><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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> abc <span class="keyword">import</span> ABC, abstractmethod</span><br><span class="line"><span class="keyword">from</span> typing <span class="keyword">import</span> <span class="type">List</span>, <span class="type">Optional</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">User</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, <span class="built_in">id</span>: <span class="built_in">int</span>, name: <span class="built_in">str</span>, email: <span class="built_in">str</span></span>):</span><br><span class="line">        <span class="variable language_">self</span>.<span class="built_in">id</span> = <span class="built_in">id</span></span><br><span class="line">        <span class="variable language_">self</span>.name = name</span><br><span class="line">        <span class="variable language_">self</span>.email = email</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">UserRepository</span>(<span class="title class_ inherited__">ABC</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;抽象仓储：定义数据访问接口。&quot;&quot;&quot;</span></span><br><span class="line"><span class="meta">    @abstractmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_by_id</span>(<span class="params">self, user_id: <span class="built_in">int</span></span>) -&gt; <span class="type">Optional</span>[User]: ...</span><br><span class="line"></span><br><span class="line"><span class="meta">    @abstractmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_all</span>(<span class="params">self</span>) -&gt; <span class="type">List</span>[User]: ...</span><br><span class="line"></span><br><span class="line"><span class="meta">    @abstractmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">save</span>(<span class="params">self, user: User</span>) -&gt; <span class="literal">None</span>: ...</span><br><span class="line"></span><br><span class="line"><span class="meta">    @abstractmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">delete</span>(<span class="params">self, user_id: <span class="built_in">int</span></span>) -&gt; <span class="literal">None</span>: ...</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">InMemoryUserRepository</span>(<span class="title class_ inherited__">UserRepository</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;内存实现：用于测试。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="variable language_">self</span>._users = &#123;&#125;</span><br><span class="line">        <span class="variable language_">self</span>._counter = <span class="number">0</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_by_id</span>(<span class="params">self, user_id: <span class="built_in">int</span></span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>._users.get(user_id)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_all</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">list</span>(<span class="variable language_">self</span>._users.values())</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">save</span>(<span class="params">self, user: User</span>):</span><br><span class="line">        <span class="keyword">if</span> user.<span class="built_in">id</span> == <span class="number">0</span>:</span><br><span class="line">            <span class="variable language_">self</span>._counter += <span class="number">1</span></span><br><span class="line">            user.<span class="built_in">id</span> = <span class="variable language_">self</span>._counter</span><br><span class="line">        <span class="variable language_">self</span>._users[user.<span class="built_in">id</span>] = user</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">delete</span>(<span class="params">self, user_id: <span class="built_in">int</span></span>):</span><br><span class="line">        <span class="variable language_">self</span>._users.pop(user_id, <span class="literal">None</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">SQLUserRepository</span>(<span class="title class_ inherited__">UserRepository</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;SQL 实现：用于生产。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, session</span>):</span><br><span class="line">        <span class="variable language_">self</span>.session = session</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_by_id</span>(<span class="params">self, user_id: <span class="built_in">int</span></span>):</span><br><span class="line">        row = <span class="variable language_">self</span>.session.execute(<span class="string">&quot;SELECT * FROM users WHERE id = ?&quot;</span>, (user_id,)).fetchone()</span><br><span class="line">        <span class="keyword">return</span> User(*row) <span class="keyword">if</span> row <span class="keyword">else</span> <span class="literal">None</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用</span></span><br><span class="line"><span class="comment"># 测试用内存版本</span></span><br><span class="line">repo = InMemoryUserRepository()</span><br><span class="line">service = UserService(repo)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 生产用 SQL 版本</span></span><br><span class="line"><span class="comment"># repo = SQLUserRepository(db_session)</span></span><br><span class="line"><span class="comment"># service = UserService(repo)</span></span><br></pre></td></tr></table></figure><h3 id="5-3-单元工作模式（Unit-of-Work）"><a href="#5-3-单元工作模式（Unit-of-Work）" class="headerlink" title="5.3 单元工作模式（Unit of Work）"></a>5.3 单元工作模式（Unit of Work）</h3><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><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><span class="line"><span class="keyword">class</span> <span class="title class_">UnitOfWork</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;管理事务边界，确保一组操作要么全成功，要么全回滚。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, session</span>):</span><br><span class="line">        <span class="variable language_">self</span>.session = session</span><br><span class="line">        <span class="variable language_">self</span>.new_objects = []</span><br><span class="line">        <span class="variable language_">self</span>.dirty_objects = []</span><br><span class="line">        <span class="variable language_">self</span>.removed_objects = []</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">register_new</span>(<span class="params">self, obj</span>):</span><br><span class="line">        <span class="variable language_">self</span>.new_objects.append(obj)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">register_dirty</span>(<span class="params">self, obj</span>):</span><br><span class="line">        <span class="variable language_">self</span>.dirty_objects.append(obj)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">register_removed</span>(<span class="params">self, obj</span>):</span><br><span class="line">        <span class="variable language_">self</span>.removed_objects.append(obj)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">commit</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">try</span>:</span><br><span class="line">            <span class="keyword">for</span> obj <span class="keyword">in</span> <span class="variable language_">self</span>.new_objects:</span><br><span class="line">                <span class="variable language_">self</span>.session.insert(obj)</span><br><span class="line">            <span class="keyword">for</span> obj <span class="keyword">in</span> <span class="variable language_">self</span>.dirty_objects:</span><br><span class="line">                <span class="variable language_">self</span>.session.update(obj)</span><br><span class="line">            <span class="keyword">for</span> obj <span class="keyword">in</span> <span class="variable language_">self</span>.removed_objects:</span><br><span class="line">                <span class="variable language_">self</span>.session.delete(obj)</span><br><span class="line">            <span class="variable language_">self</span>.session.commit()</span><br><span class="line">        <span class="keyword">except</span> Exception:</span><br><span class="line">            <span class="variable language_">self</span>.session.rollback()</span><br><span class="line">            <span class="keyword">raise</span></span><br><span class="line">        <span class="keyword">finally</span>:</span><br><span class="line">            <span class="variable language_">self</span>.new_objects.clear()</span><br><span class="line">            <span class="variable language_">self</span>.dirty_objects.clear()</span><br><span class="line">            <span class="variable language_">self</span>.removed_objects.clear()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用</span></span><br><span class="line"><span class="keyword">with</span> UnitOfWork(session) <span class="keyword">as</span> uow:</span><br><span class="line">    user = User(name=<span class="string">&quot;Alice&quot;</span>, email=<span class="string">&quot;alice@example.com&quot;</span>)</span><br><span class="line">    uow.register_new(user)</span><br><span class="line">    uow.commit()   <span class="comment"># 统一提交</span></span><br></pre></td></tr></table></figure><hr><h2 id="6-架构思维"><a href="#6-架构思维" class="headerlink" title="6. 架构思维"></a>6. 架构思维</h2><h3 id="6-1-分层架构"><a href="#6-1-分层架构" class="headerlink" title="6.1 分层架构"></a>6.1 分层架构</h3><figure class="highlight plaintext"><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><span class="line">典型的 Web 应用分层：</span><br><span class="line"></span><br><span class="line">┌─────────────────────────────────────┐</span><br><span class="line">│  表现层 (Presentation)               │  FastAPI / Django Views</span><br><span class="line">│  - API 路由、请求验证、序列化         │</span><br><span class="line">├─────────────────────────────────────┤</span><br><span class="line">│  业务层 (Business/Service)           │  Service 类</span><br><span class="line">│  - 业务逻辑、流程编排、规则校验       │</span><br><span class="line">├─────────────────────────────────────┤</span><br><span class="line">│  数据层 (Data Access)                │  Repository / ORM</span><br><span class="line">│  - 数据库操作、缓存、外部 API 调用    │</span><br><span class="line">├─────────────────────────────────────┤</span><br><span class="line">│  领域模型 (Domain)                   │  Entity / Value Object</span><br><span class="line">│  - 核心业务规则、实体定义             │</span><br><span class="line">└─────────────────────────────────────┘</span><br><span class="line"></span><br><span class="line">依赖方向：上层 → 下层（领域层不依赖任何层）</span><br></pre></td></tr></table></figure><h3 id="6-2-微服务拆分原则"><a href="#6-2-微服务拆分原则" class="headerlink" title="6.2 微服务拆分原则"></a>6.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><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><span class="line"><span class="comment"># 单体 vs 微服务的决策树</span></span><br><span class="line"></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">是否应该拆分为微服务？</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">1. 团队规模 &gt; 10 人？</span></span><br><span class="line"><span class="string">   否 → 单体足够</span></span><br><span class="line"><span class="string">   是 → 继续</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">2. 不同模块的发布节奏差异大？</span></span><br><span class="line"><span class="string">   否 → 单体 + 模块化</span></span><br><span class="line"><span class="string">   是 → 继续</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">3. 有独立扩展需求？（如订单服务压力远大于用户服务）</span></span><br><span class="line"><span class="string">   否 → 单体 + 模块化</span></span><br><span class="line"><span class="string">   是 → 考虑微服务</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">4. 团队具备 DevOps 能力？</span></span><br><span class="line"><span class="string">   否 → 先提升运维能力</span></span><br><span class="line"><span class="string">   是 → 可以拆分</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">微服务拆分粒度：</span></span><br><span class="line"><span class="string">- 按业务能力（用户服务、订单服务、支付服务）</span></span><br><span class="line"><span class="string">- 按数据所有权（每个服务独占数据库）</span></span><br><span class="line"><span class="string">- 避免过细（纳米服务反模式）</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br></pre></td></tr></table></figure><h3 id="6-3-常见架构反模式"><a href="#6-3-常见架构反模式" class="headerlink" title="6.3 常见架构反模式"></a>6.3 常见架构反模式</h3><table><thead><tr><th>反模式</th><th>症状</th><th>解决</th></tr></thead><tbody><tr><td><strong>大泥球</strong></td><td>代码耦合严重，改一处坏多处</td><td>模块化、重构、引入边界</td></tr><tr><td><strong>面条代码</strong></td><td>没有分层，业务逻辑散落在各处</td><td>引入分层架构、Service 层</td></tr><tr><td><strong>上帝类</strong></td><td>一个类几千行，什么都管</td><td>拆分职责、提取协作类</td></tr><tr><td><strong>重复代码</strong></td><td>同样的逻辑复制粘贴</td><td>提取函数&#x2F;类、使用继承或组合</td></tr><tr><td><strong>过早优化</strong></td><td>在不需要的地方引入复杂设计</td><td>YAGNI（You Ain’t Gonna Need It）</td></tr><tr><td><strong>过度工程</strong></td><td>为了用模式而用模式</td><td>简单优先，按需引入复杂度</td></tr></tbody></table><h3 id="6-4-补充模式"><a href="#6-4-补充模式" class="headerlink" title="6.4 补充模式"></a>6.4 补充模式</h3><h4 id="命令模式（Command）"><a href="#命令模式（Command）" class="headerlink" title="命令模式（Command）"></a>命令模式（Command）</h4><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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> typing <span class="keyword">import</span> <span class="type">List</span>, <span class="type">Optional</span></span><br><span class="line"><span class="keyword">from</span> abc <span class="keyword">import</span> ABC, abstractmethod</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Command</span>(<span class="title class_ inherited__">ABC</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;命令接口。&quot;&quot;&quot;</span></span><br><span class="line"><span class="meta">    @abstractmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">execute</span>(<span class="params">self</span>): ...</span><br><span class="line"></span><br><span class="line"><span class="meta">    @abstractmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">undo</span>(<span class="params">self</span>): ...</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">TextEditor</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;接收者（Receiver）。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="variable language_">self</span>.text = <span class="string">&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">insert</span>(<span class="params">self, text, pos=<span class="literal">None</span></span>):</span><br><span class="line">        <span class="keyword">if</span> pos <span class="keyword">is</span> <span class="literal">None</span>:</span><br><span class="line">            <span class="variable language_">self</span>.text += text</span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            <span class="variable language_">self</span>.text = <span class="variable language_">self</span>.text[:pos] + text + <span class="variable language_">self</span>.text[pos:]</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">delete</span>(<span class="params">self, start, end</span>):</span><br><span class="line">        <span class="variable language_">self</span>.text = <span class="variable language_">self</span>.text[:start] + <span class="variable language_">self</span>.text[end:]</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">InsertCommand</span>(<span class="title class_ inherited__">Command</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, editor, text, pos=<span class="literal">None</span></span>):</span><br><span class="line">        <span class="variable language_">self</span>.editor = editor</span><br><span class="line">        <span class="variable language_">self</span>.text = text</span><br><span class="line">        <span class="variable language_">self</span>.pos = pos</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">execute</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="variable language_">self</span>.editor.insert(<span class="variable language_">self</span>.text, <span class="variable language_">self</span>.pos)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">undo</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">if</span> <span class="variable language_">self</span>.pos <span class="keyword">is</span> <span class="literal">None</span>:</span><br><span class="line">            start = <span class="built_in">len</span>(<span class="variable language_">self</span>.editor.text) - <span class="built_in">len</span>(<span class="variable language_">self</span>.text)</span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            start = <span class="variable language_">self</span>.pos</span><br><span class="line">        <span class="variable language_">self</span>.editor.delete(start, start + <span class="built_in">len</span>(<span class="variable language_">self</span>.text))</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">CommandHistory</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;保存命令历史，支持撤销。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="variable language_">self</span>._history: <span class="type">List</span>[Command] = []</span><br><span class="line">        <span class="variable language_">self</span>._position = -<span class="number">1</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">execute</span>(<span class="params">self, cmd: Command</span>):</span><br><span class="line">        cmd.execute()</span><br><span class="line">        <span class="variable language_">self</span>._history = <span class="variable language_">self</span>._history[:<span class="variable language_">self</span>._position + <span class="number">1</span>]</span><br><span class="line">        <span class="variable language_">self</span>._history.append(cmd)</span><br><span class="line">        <span class="variable language_">self</span>._position += <span class="number">1</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">undo</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">if</span> <span class="variable language_">self</span>._position &gt;= <span class="number">0</span>:</span><br><span class="line">            cmd = <span class="variable language_">self</span>._history[<span class="variable language_">self</span>._position]</span><br><span class="line">            cmd.undo()</span><br><span class="line">            <span class="variable language_">self</span>._position -= <span class="number">1</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">redo</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">if</span> <span class="variable language_">self</span>._position + <span class="number">1</span> &lt; <span class="built_in">len</span>(<span class="variable language_">self</span>._history):</span><br><span class="line">            <span class="variable language_">self</span>._position += <span class="number">1</span></span><br><span class="line">            <span class="variable language_">self</span>._history[<span class="variable language_">self</span>._position].execute()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用</span></span><br><span class="line">editor = TextEditor()</span><br><span class="line">history = CommandHistory()</span><br><span class="line">history.execute(InsertCommand(editor, <span class="string">&quot;Hello&quot;</span>))</span><br><span class="line">history.execute(InsertCommand(editor, <span class="string">&quot; World&quot;</span>))</span><br><span class="line"><span class="built_in">print</span>(editor.text)   <span class="comment"># Hello World</span></span><br><span class="line">history.undo()</span><br><span class="line"><span class="built_in">print</span>(editor.text)   <span class="comment"># Hello</span></span><br></pre></td></tr></table></figure><h4 id="状态模式（State）"><a href="#状态模式（State）" class="headerlink" title="状态模式（State）"></a>状态模式（State）</h4><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><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><span class="line"><span class="keyword">from</span> abc <span class="keyword">import</span> ABC, abstractmethod</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">OrderState</span>(<span class="title class_ inherited__">ABC</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;订单状态接口。&quot;&quot;&quot;</span></span><br><span class="line"><span class="meta">    @abstractmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">pay</span>(<span class="params">self, order</span>): ...</span><br><span class="line"><span class="meta">    @abstractmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">ship</span>(<span class="params">self, order</span>): ...</span><br><span class="line"><span class="meta">    @abstractmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">cancel</span>(<span class="params">self, order</span>): ...</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">PendingState</span>(<span class="title class_ inherited__">OrderState</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">pay</span>(<span class="params">self, order</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;订单已支付&quot;</span>)</span><br><span class="line">        order.state = PaidState()</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">ship</span>(<span class="params">self, order</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;请先支付&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">cancel</span>(<span class="params">self, order</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;订单已取消&quot;</span>)</span><br><span class="line">        order.state = CancelledState()</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">PaidState</span>(<span class="title class_ inherited__">OrderState</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">pay</span>(<span class="params">self, order</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;订单已支付，无需重复支付&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">ship</span>(<span class="params">self, order</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;订单已发货&quot;</span>)</span><br><span class="line">        order.state = ShippedState()</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">cancel</span>(<span class="params">self, order</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;订单已取消，退款处理中&quot;</span>)</span><br><span class="line">        order.state = CancelledState()</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">ShippedState</span>(<span class="title class_ inherited__">OrderState</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">pay</span>(<span class="params">self, order</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;订单已发货，无需支付&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">ship</span>(<span class="params">self, order</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;订单已发货，请勿重复发货&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">cancel</span>(<span class="params">self, order</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;订单已发货，取消需联系客服&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">CancelledState</span>(<span class="title class_ inherited__">OrderState</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">pay</span>(<span class="params">self, order</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;订单已取消，无法支付&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">ship</span>(<span class="params">self, order</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;订单已取消，无法发货&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">cancel</span>(<span class="params">self, order</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;订单已取消，无需重复取消&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Order</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="variable language_">self</span>.state = PendingState()</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">pay</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="variable language_">self</span>.state.pay(<span class="variable language_">self</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">ship</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="variable language_">self</span>.state.ship(<span class="variable language_">self</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">cancel</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="variable language_">self</span>.state.cancel(<span class="variable language_">self</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用</span></span><br><span class="line">order = Order()</span><br><span class="line">order.pay()          <span class="comment"># 订单已支付</span></span><br><span class="line">order.ship()         <span class="comment"># 订单已发货</span></span><br><span class="line">order.cancel()       <span class="comment"># 订单已发货，取消需联系客服</span></span><br></pre></td></tr></table></figure><p><strong>状态模式转换图</strong>：</p><figure class="highlight plaintext"><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><span class="line">┌──────────────────┐</span><br><span class="line">│   PendingState   │</span><br><span class="line">└────────┬─────────┘</span><br><span class="line">         │ pay()</span><br><span class="line">         ▼</span><br><span class="line">┌──────────────────┐     cancel()      ┌──────────────────┐</span><br><span class="line">│   PaidState      │──────────────────→│  CancelledState  │</span><br><span class="line">└────────┬─────────┘                   └──────────────────┘</span><br><span class="line">         │ ship()</span><br><span class="line">         ▼</span><br><span class="line">┌──────────────────┐     cancel()      ┌──────────────────┐</span><br><span class="line">│  ShippedState    │──────────────────→│  CancelledState  │</span><br><span class="line">└──────────────────┘                   └──────────────────┘</span><br></pre></td></tr></table></figure><hr><h2 id="7-工作实战场景"><a href="#7-工作实战场景" class="headerlink" title="7. 工作实战场景"></a>7. 工作实战场景</h2><h3 id="7-1-重构大型单体应用"><a href="#7-1-重构大型单体应用" class="headerlink" title="7.1 重构大型单体应用"></a>7.1 重构大型单体应用</h3><p><strong>场景描述</strong>：接手一个 10000+ 行的单体应用，代码耦合严重，需要重构为模块化架构。</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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 问题：大泥球代码，所有逻辑混在一起</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">MegaService</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">process_order</span>(<span class="params">self, order_data</span>):</span><br><span class="line">        <span class="comment"># 验证数据（业务逻辑）</span></span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> order_data.get(<span class="string">&quot;user_id&quot;</span>):</span><br><span class="line">            <span class="keyword">raise</span> ValueError(<span class="string">&quot;Missing user_id&quot;</span>)</span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 数据库操作（数据层）</span></span><br><span class="line">        db = MySQLDatabase()</span><br><span class="line">        user = db.query(<span class="string">f&quot;SELECT * FROM users WHERE id = <span class="subst">&#123;order_data[<span class="string">&#x27;user_id&#x27;</span>]&#125;</span>&quot;</span>)</span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 发送通知（表现层）</span></span><br><span class="line">        email_service = EmailService()</span><br><span class="line">        email_service.send(user[<span class="string">&quot;email&quot;</span>], <span class="string">&quot;Order received&quot;</span>)</span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 记录日志（基础设施）</span></span><br><span class="line">        logger.info(<span class="string">f&quot;Order processed: <span class="subst">&#123;order_data&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 解决方案：分层重构</span></span><br><span class="line"><span class="comment"># 1. 领域模型层</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Order</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, user_id, items</span>):</span><br><span class="line">        <span class="variable language_">self</span>.user_id = user_id</span><br><span class="line">        <span class="variable language_">self</span>.items = items</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">validate</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> <span class="variable language_">self</span>.user_id:</span><br><span class="line">            <span class="keyword">raise</span> ValueError(<span class="string">&quot;Missing user_id&quot;</span>)</span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> <span class="variable language_">self</span>.items:</span><br><span class="line">            <span class="keyword">raise</span> ValueError(<span class="string">&quot;Empty order&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 2. 数据访问层（仓储模式）</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">OrderRepository</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, db</span>):</span><br><span class="line">        <span class="variable language_">self</span>.db = db</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">save</span>(<span class="params">self, order</span>):</span><br><span class="line">        <span class="variable language_">self</span>.db.execute(<span class="string">&quot;INSERT INTO orders ...&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 3. 业务逻辑层（服务层）</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">OrderService</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, order_repo, user_repo, notifier</span>):</span><br><span class="line">        <span class="variable language_">self</span>.order_repo = order_repo</span><br><span class="line">        <span class="variable language_">self</span>.user_repo = user_repo</span><br><span class="line">        <span class="variable language_">self</span>.notifier = notifier</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">process_order</span>(<span class="params">self, order_data</span>):</span><br><span class="line">        order = Order(order_data[<span class="string">&quot;user_id&quot;</span>], order_data[<span class="string">&quot;items&quot;</span>])</span><br><span class="line">        order.validate()</span><br><span class="line">        </span><br><span class="line">        user = <span class="variable language_">self</span>.user_repo.get_by_id(order.user_id)</span><br><span class="line">        <span class="variable language_">self</span>.order_repo.save(order)</span><br><span class="line">        <span class="variable language_">self</span>.notifier.send(user.email, <span class="string">&quot;Order received&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 4. 表现层（API 层）</span></span><br><span class="line"><span class="meta">@app.post(<span class="params"><span class="string">&quot;/orders&quot;</span></span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">create_order</span>(<span class="params">request</span>):</span><br><span class="line">    service = OrderService(order_repo, user_repo, email_notifier)</span><br><span class="line">    service.process_order(request.json())</span><br><span class="line">    <span class="keyword">return</span> &#123;<span class="string">&quot;status&quot;</span>: <span class="string">&quot;success&quot;</span>&#125;</span><br></pre></td></tr></table></figure><h3 id="7-2-构建可扩展的插件系统"><a href="#7-2-构建可扩展的插件系统" class="headerlink" title="7.2 构建可扩展的插件系统"></a>7.2 构建可扩展的插件系统</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><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><span class="line"><span class="comment"># 插件系统设计</span></span><br><span class="line"><span class="keyword">from</span> abc <span class="keyword">import</span> ABC, abstractmethod</span><br><span class="line"><span class="keyword">from</span> typing <span class="keyword">import</span> <span class="type">Dict</span>, <span class="type">Type</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Plugin</span>(<span class="title class_ inherited__">ABC</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;插件接口。&quot;&quot;&quot;</span></span><br><span class="line"><span class="meta">    @abstractmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">name</span>(<span class="params">self</span>) -&gt; <span class="built_in">str</span>: ...</span><br><span class="line">    </span><br><span class="line"><span class="meta">    @abstractmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">initialize</span>(<span class="params">self</span>): ...</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">PluginManager</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;插件管理器（工厂模式变体）。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="variable language_">self</span>._plugins: <span class="type">Dict</span>[<span class="built_in">str</span>, Plugin] = &#123;&#125;</span><br><span class="line">        <span class="variable language_">self</span>._registry: <span class="type">Dict</span>[<span class="built_in">str</span>, <span class="type">Type</span>[Plugin]] = &#123;&#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">register</span>(<span class="params">self, plugin_class: <span class="type">Type</span>[Plugin]</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;注册插件类。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="variable language_">self</span>._registry[plugin_class.name()] = plugin_class</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">load</span>(<span class="params">self, plugin_name: <span class="built_in">str</span></span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;加载并初始化插件。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">if</span> plugin_name <span class="keyword">not</span> <span class="keyword">in</span> <span class="variable language_">self</span>._registry:</span><br><span class="line">            <span class="keyword">raise</span> ValueError(<span class="string">f&quot;Unknown plugin: <span class="subst">&#123;plugin_name&#125;</span>&quot;</span>)</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">if</span> plugin_name <span class="keyword">not</span> <span class="keyword">in</span> <span class="variable language_">self</span>._plugins:</span><br><span class="line">            plugin = <span class="variable language_">self</span>._registry[plugin_name]()</span><br><span class="line">            plugin.initialize()</span><br><span class="line">            <span class="variable language_">self</span>._plugins[plugin_name] = plugin</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>._plugins[plugin_name]</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_all</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;获取所有已加载的插件。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">list</span>(<span class="variable language_">self</span>._plugins.values())</span><br><span class="line"></span><br><span class="line"><span class="comment"># 插件实现示例</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">AuthPlugin</span>(<span class="title class_ inherited__">Plugin</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">name</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;auth&quot;</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">initialize</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;Auth plugin initialized&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">PaymentPlugin</span>(<span class="title class_ inherited__">Plugin</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">name</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;payment&quot;</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">initialize</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;Payment plugin initialized&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用</span></span><br><span class="line">manager = PluginManager()</span><br><span class="line">manager.register(AuthPlugin)</span><br><span class="line">manager.register(PaymentPlugin)</span><br><span class="line"></span><br><span class="line">auth = manager.load(<span class="string">&quot;auth&quot;</span>)</span><br><span class="line">payment = manager.load(<span class="string">&quot;payment&quot;</span>)</span><br></pre></td></tr></table></figure><h3 id="7-3-实现事件驱动架构"><a href="#7-3-实现事件驱动架构" class="headerlink" title="7.3 实现事件驱动架构"></a>7.3 实现事件驱动架构</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><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 事件驱动架构（观察者模式变体）</span></span><br><span class="line"><span class="keyword">import</span> asyncio</span><br><span class="line"><span class="keyword">from</span> typing <span class="keyword">import</span> <span class="type">Callable</span>, <span class="type">Dict</span>, <span class="type">List</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">EventBus</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;事件总线：解耦事件发布者和订阅者。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="variable language_">self</span>._subscribers: <span class="type">Dict</span>[<span class="built_in">str</span>, <span class="type">List</span>[<span class="type">Callable</span>]] = &#123;&#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">subscribe</span>(<span class="params">self, event_type: <span class="built_in">str</span>, handler: <span class="type">Callable</span></span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;订阅事件。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">if</span> event_type <span class="keyword">not</span> <span class="keyword">in</span> <span class="variable language_">self</span>._subscribers:</span><br><span class="line">            <span class="variable language_">self</span>._subscribers[event_type] = []</span><br><span class="line">        <span class="variable language_">self</span>._subscribers[event_type].append(handler)</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">publish</span>(<span class="params">self, event_type: <span class="built_in">str</span>, data: <span class="built_in">dict</span></span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;发布事件（异步）。&quot;&quot;&quot;</span></span><br><span class="line">        handlers = <span class="variable language_">self</span>._subscribers.get(event_type, [])</span><br><span class="line">        tasks = [handler(data) <span class="keyword">for</span> handler <span class="keyword">in</span> handlers]</span><br><span class="line">        <span class="keyword">await</span> asyncio.gather(*tasks)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 事件处理器</span></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">send_welcome_email</span>(<span class="params">data</span>):</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;Sending email to <span class="subst">&#123;data[<span class="string">&#x27;email&#x27;</span>]&#125;</span>&quot;</span>)</span><br><span class="line">    <span class="keyword">await</span> asyncio.sleep(<span class="number">0.5</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">update_inventory</span>(<span class="params">data</span>):</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;Updating inventory for order <span class="subst">&#123;data[<span class="string">&#x27;order_id&#x27;</span>]&#125;</span>&quot;</span>)</span><br><span class="line">    <span class="keyword">await</span> asyncio.sleep(<span class="number">0.3</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">create_analytics_record</span>(<span class="params">data</span>):</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;Recording analytics: <span class="subst">&#123;data&#125;</span>&quot;</span>)</span><br><span class="line">    <span class="keyword">await</span> asyncio.sleep(<span class="number">0.2</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用</span></span><br><span class="line">event_bus = EventBus()</span><br><span class="line">event_bus.subscribe(<span class="string">&quot;order_created&quot;</span>, send_welcome_email)</span><br><span class="line">event_bus.subscribe(<span class="string">&quot;order_created&quot;</span>, update_inventory)</span><br><span class="line">event_bus.subscribe(<span class="string">&quot;order_created&quot;</span>, create_analytics_record)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 发布事件</span></span><br><span class="line">asyncio.run(event_bus.publish(</span><br><span class="line">    <span class="string">&quot;order_created&quot;</span>,</span><br><span class="line">    &#123;<span class="string">&quot;order_id&quot;</span>: <span class="number">123</span>, <span class="string">&quot;email&quot;</span>: <span class="string">&quot;alice@example.com&quot;</span>, <span class="string">&quot;items&quot;</span>: [<span class="string">&quot;item1&quot;</span>]&#125;</span><br><span class="line">))</span><br></pre></td></tr></table></figure><hr><h2 id="8-常见面试题汇总"><a href="#8-常见面试题汇总" class="headerlink" title="8. 常见面试题汇总"></a>8. 常见面试题汇总</h2><h3 id="8-1-SOLID-原则"><a href="#8-1-SOLID-原则" class="headerlink" title="8.1 SOLID 原则"></a>8.1 SOLID 原则</h3><p><strong>Q1：什么是 SOLID 原则？分别解释每个原则。</strong></p><blockquote><p><strong>A</strong>：</p><ul><li><strong>S</strong>RP（单一职责）：一个类只负责一件事</li><li><strong>O</strong>CP（开闭原则）：对扩展开放，对修改关闭</li><li><strong>L</strong>SP（里氏替换）：子类可以替换父类而不破坏功能</li><li><strong>I</strong>SP（接口隔离）：客户端不应依赖不需要的接口</li><li><strong>D</strong>IP（依赖倒置）：依赖抽象而非具体实现</li></ul></blockquote><p><strong>Q2：为什么单一职责原则很重要？</strong></p><blockquote><p><strong>A</strong>：</p><ul><li>提高代码可维护性：修改一个功能不影响其他功能</li><li>提高代码可读性：每个类职责清晰</li><li>提高可测试性：职责单一的类更容易测试</li></ul></blockquote><p><strong>Q3：开闭原则的核心思想是什么？如何实现？</strong></p><blockquote><p><strong>A</strong>：核心思想是通过<strong>抽象</strong>和<strong>多态</strong>实现扩展。实现方式：</p><ul><li>定义抽象接口&#x2F;基类</li><li>通过继承或组合扩展功能</li><li>使用依赖注入解耦</li></ul></blockquote><h3 id="8-2-创建型模式"><a href="#8-2-创建型模式" class="headerlink" title="8.2 创建型模式"></a>8.2 创建型模式</h3><p><strong>Q4：工厂模式和抽象工厂模式的区别是什么？</strong></p><blockquote><p><strong>A</strong>：</p><ul><li><strong>工厂模式</strong>：创建单一类型的对象（如不同类型的通知）</li><li><strong>抽象工厂模式</strong>：创建一组相关或依赖的对象（如 UI 组件套件）</li></ul></blockquote><p><strong>Q5：单例模式有什么优缺点？</strong></p><blockquote><p><strong>A</strong>：</p><ul><li><strong>优点</strong>：全局唯一实例，节省资源，统一管理状态</li><li><strong>缺点</strong>：难以测试（全局状态），可能导致代码耦合，并发问题需要处理</li></ul></blockquote><p><strong>Q6：建造者模式适用于什么场景？</strong></p><blockquote><p><strong>A</strong>：适用于创建<strong>复杂对象</strong>，需要分步骤构建，且构建过程可能有不同变体的场景：</p><ul><li>HTTP 请求对象（方法、URL、头、体）</li><li>配置对象（多个可选参数）</li><li>文档对象（标题、内容、格式）</li></ul></blockquote><h3 id="8-3-结构型模式"><a href="#8-3-结构型模式" class="headerlink" title="8.3 结构型模式"></a>8.3 结构型模式</h3><p><strong>Q7：装饰器模式和代理模式的区别是什么？</strong></p><blockquote><p><strong>A</strong>：</p><ul><li><strong>装饰器模式</strong>：动态添加职责，关注功能扩展</li><li><strong>代理模式</strong>：控制访问，关注权限控制、延迟加载等</li></ul></blockquote><p><strong>Q8：适配器模式的使用场景是什么？</strong></p><blockquote><p><strong>A</strong>：</p><ul><li>集成旧系统与新系统（接口不兼容）</li><li>调用第三方库（接口不符合预期）</li><li>兼容不同版本的 API</li></ul></blockquote><p><strong>Q9：组合模式的核心思想是什么？</strong></p><blockquote><p><strong>A</strong>：将对象组合成树形结构，表示”部分-整体”关系，使单个对象和组合对象具有一致的接口。</p></blockquote><h3 id="8-4-行为型模式"><a href="#8-4-行为型模式" class="headerlink" title="8.4 行为型模式"></a>8.4 行为型模式</h3><p><strong>Q10：观察者模式和发布订阅模式的区别是什么？</strong></p><blockquote><p><strong>A</strong>：</p><ul><li><strong>观察者模式</strong>：同步通知，直接调用</li><li><strong>发布订阅模式</strong>：异步通知，通过消息队列解耦</li></ul></blockquote><p><strong>Q11：策略模式在 Python 中如何简洁实现？</strong></p><blockquote><p><strong>A</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><span class="line"><span class="keyword">def</span> <span class="title function_">discount_10_percent</span>(<span class="params">price</span>):</span><br><span class="line">    <span class="keyword">return</span> price * <span class="number">0.9</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Order</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, price, discount_strategy</span>):</span><br><span class="line">        <span class="variable language_">self</span>.price = price</span><br><span class="line">        <span class="variable language_">self</span>.discount_strategy = discount_strategy</span><br></pre></td></tr></table></figure></blockquote><p><strong>Q12：责任链模式适用于什么场景？</strong></p><blockquote><p><strong>A</strong>：请求需要经过多个处理步骤，且步骤顺序可能变化：</p><ul><li>中间件链（如 Flask&#x2F;Django 的中间件）</li><li>请求处理管道（认证 → 限流 → 验证 → 处理）</li></ul></blockquote><h3 id="8-5-架构思维"><a href="#8-5-架构思维" class="headerlink" title="8.5 架构思维"></a>8.5 架构思维</h3><p><strong>Q13：什么是分层架构？各层职责是什么？</strong></p><blockquote><p><strong>A</strong>：</p><ul><li><strong>表现层</strong>：处理请求&#x2F;响应、路由、验证</li><li><strong>业务层</strong>：业务逻辑、流程编排</li><li><strong>数据层</strong>：数据库操作、缓存</li><li><strong>领域层</strong>：核心业务规则、实体定义</li></ul></blockquote><p><strong>Q14：什么时候应该拆分微服务？</strong></p><blockquote><p><strong>A</strong>：</p><ul><li>团队规模大（&gt;10 人），需要并行开发</li><li>不同模块发布节奏差异大</li><li>有独立扩展需求</li><li>具备 DevOps 能力</li></ul></blockquote><p><strong>Q15：什么是架构反模式？举几个例子。</strong></p><blockquote><p><strong>A</strong>：反模式是常见的错误设计，导致维护困难：</p><ul><li><strong>大泥球</strong>：代码耦合严重</li><li><strong>上帝类</strong>：一个类做太多事</li><li><strong>面条代码</strong>：没有分层</li><li><strong>过早优化</strong>：过度设计</li></ul></blockquote><hr><h2 id="9-实战项目：电商订单管理系统"><a href="#9-实战项目：电商订单管理系统" class="headerlink" title="9. 实战项目：电商订单管理系统"></a>9. 实战项目：电商订单管理系统</h2><h3 id="9-1-项目目标"><a href="#9-1-项目目标" class="headerlink" title="9.1 项目目标"></a>9.1 项目目标</h3><p>开发一个完整的电商订单管理系统，涵盖：</p><ol><li>订单创建与处理流程</li><li>状态管理</li><li>事件驱动通知</li><li>数据持久化</li></ol><h3 id="9-2-项目结构"><a href="#9-2-项目结构" class="headerlink" title="9.2 项目结构"></a>9.2 项目结构</h3><figure class="highlight plaintext"><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><span class="line">order_system/</span><br><span class="line">├── __init__.py</span><br><span class="line">├── domain/              # 领域模型层</span><br><span class="line">│   ├── __init__.py</span><br><span class="line">│   ├── order.py        # 订单实体</span><br><span class="line">│   └── product.py      # 产品实体</span><br><span class="line">├── repository/          # 数据访问层</span><br><span class="line">│   ├── __init__.py</span><br><span class="line">│   ├── order_repo.py   # 订单仓储</span><br><span class="line">│   └── product_repo.py # 产品仓储</span><br><span class="line">├── service/             # 业务逻辑层</span><br><span class="line">│   ├── __init__.py</span><br><span class="line">│   ├── order_service.py # 订单服务</span><br><span class="line">│   └── inventory_service.py # 库存服务</span><br><span class="line">├── event/               # 事件系统</span><br><span class="line">│   ├── __init__.py</span><br><span class="line">│   ├── event_bus.py    # 事件总线</span><br><span class="line">│   └── handlers.py     # 事件处理器</span><br><span class="line">├── state/               # 状态模式</span><br><span class="line">│   ├── __init__.py</span><br><span class="line">│   └── order_state.py  # 订单状态</span><br><span class="line">└── main.py              # 入口</span><br></pre></td></tr></table></figure><h3 id="9-3-核心代码"><a href="#9-3-核心代码" class="headerlink" title="9.3 核心代码"></a>9.3 核心代码</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><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># domain/order.py - 领域模型</span></span><br><span class="line"><span class="keyword">from</span> dataclasses <span class="keyword">import</span> dataclass</span><br><span class="line"><span class="keyword">from</span> typing <span class="keyword">import</span> <span class="type">List</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@dataclass</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">OrderItem</span>:</span><br><span class="line">    product_id: <span class="built_in">int</span></span><br><span class="line">    quantity: <span class="built_in">int</span></span><br><span class="line">    price: <span class="built_in">float</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@dataclass</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Order</span>:</span><br><span class="line">    <span class="built_in">id</span>: <span class="built_in">int</span> = <span class="number">0</span></span><br><span class="line">    user_id: <span class="built_in">int</span> = <span class="number">0</span></span><br><span class="line">    items: <span class="type">List</span>[OrderItem] = <span class="literal">None</span></span><br><span class="line">    status: <span class="built_in">str</span> = <span class="string">&quot;pending&quot;</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__post_init__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">if</span> <span class="variable language_">self</span>.items <span class="keyword">is</span> <span class="literal">None</span>:</span><br><span class="line">            <span class="variable language_">self</span>.items = []</span><br><span class="line">    </span><br><span class="line"><span class="meta">    @property</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">total_amount</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">sum</span>(item.price * item.quantity <span class="keyword">for</span> item <span class="keyword">in</span> <span class="variable language_">self</span>.items)</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">validate</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> <span class="variable language_">self</span>.user_id:</span><br><span class="line">            <span class="keyword">raise</span> ValueError(<span class="string">&quot;user_id is required&quot;</span>)</span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> <span class="variable language_">self</span>.items:</span><br><span class="line">            <span class="keyword">raise</span> ValueError(<span class="string">&quot;order must have items&quot;</span>)</span><br><span class="line">        <span class="keyword">for</span> item <span class="keyword">in</span> <span class="variable language_">self</span>.items:</span><br><span class="line">            <span class="keyword">if</span> item.quantity &lt;= <span class="number">0</span>:</span><br><span class="line">                <span class="keyword">raise</span> ValueError(<span class="string">f&quot;quantity must be positive: <span class="subst">&#123;item.product_id&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># state/order_state.py - 状态模式</span></span><br><span class="line"><span class="keyword">from</span> abc <span class="keyword">import</span> ABC, abstractmethod</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">OrderState</span>(<span class="title class_ inherited__">ABC</span>):</span><br><span class="line"><span class="meta">    @abstractmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">pay</span>(<span class="params">self, order</span>): ...</span><br><span class="line">    </span><br><span class="line"><span class="meta">    @abstractmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">ship</span>(<span class="params">self, order</span>): ...</span><br><span class="line">    </span><br><span class="line"><span class="meta">    @abstractmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">cancel</span>(<span class="params">self, order</span>): ...</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">PendingState</span>(<span class="title class_ inherited__">OrderState</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">pay</span>(<span class="params">self, order</span>):</span><br><span class="line">        order.status = <span class="string">&quot;paid&quot;</span></span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;order paid successfully&quot;</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">ship</span>(<span class="params">self, order</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;cannot ship: order not paid&quot;</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">cancel</span>(<span class="params">self, order</span>):</span><br><span class="line">        order.status = <span class="string">&quot;cancelled&quot;</span></span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;order cancelled&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">PaidState</span>(<span class="title class_ inherited__">OrderState</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">pay</span>(<span class="params">self, order</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;cannot pay: order already paid&quot;</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">ship</span>(<span class="params">self, order</span>):</span><br><span class="line">        order.status = <span class="string">&quot;shipped&quot;</span></span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;order shipped&quot;</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">cancel</span>(<span class="params">self, order</span>):</span><br><span class="line">        order.status = <span class="string">&quot;refunded&quot;</span></span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;order cancelled, refund processing&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">ShippedState</span>(<span class="title class_ inherited__">OrderState</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">pay</span>(<span class="params">self, order</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;cannot pay: order already shipped&quot;</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">ship</span>(<span class="params">self, order</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;cannot ship: order already shipped&quot;</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">cancel</span>(<span class="params">self, order</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;cannot cancel: order already shipped&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">CancelledState</span>(<span class="title class_ inherited__">OrderState</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">pay</span>(<span class="params">self, order</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;cannot pay: order cancelled&quot;</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">ship</span>(<span class="params">self, order</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;cannot ship: order cancelled&quot;</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">cancel</span>(<span class="params">self, order</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;cannot cancel: order already cancelled&quot;</span></span><br><span class="line"></span><br><span class="line">STATE_MAP = &#123;</span><br><span class="line">    <span class="string">&quot;pending&quot;</span>: PendingState(),</span><br><span class="line">    <span class="string">&quot;paid&quot;</span>: PaidState(),</span><br><span class="line">    <span class="string">&quot;shipped&quot;</span>: ShippedState(),</span><br><span class="line">    <span class="string">&quot;cancelled&quot;</span>: CancelledState(),</span><br><span class="line">    <span class="string">&quot;refunded&quot;</span>: CancelledState(),</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># repository/order_repo.py - 仓储模式</span></span><br><span class="line"><span class="keyword">from</span> abc <span class="keyword">import</span> ABC, abstractmethod</span><br><span class="line"><span class="keyword">from</span> typing <span class="keyword">import</span> <span class="type">Optional</span>, <span class="type">List</span></span><br><span class="line"><span class="keyword">from</span> domain.order <span class="keyword">import</span> Order</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">OrderRepository</span>(<span class="title class_ inherited__">ABC</span>):</span><br><span class="line"><span class="meta">    @abstractmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">save</span>(<span class="params">self, order: Order</span>) -&gt; <span class="literal">None</span>: ...</span><br><span class="line">    </span><br><span class="line"><span class="meta">    @abstractmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_by_id</span>(<span class="params">self, order_id: <span class="built_in">int</span></span>) -&gt; <span class="type">Optional</span>[Order]: ...</span><br><span class="line">    </span><br><span class="line"><span class="meta">    @abstractmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_by_user</span>(<span class="params">self, user_id: <span class="built_in">int</span></span>) -&gt; <span class="type">List</span>[Order]: ...</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">InMemoryOrderRepository</span>(<span class="title class_ inherited__">OrderRepository</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="variable language_">self</span>._orders = &#123;&#125;</span><br><span class="line">        <span class="variable language_">self</span>._counter = <span class="number">0</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">save</span>(<span class="params">self, order: Order</span>):</span><br><span class="line">        <span class="keyword">if</span> order.<span class="built_in">id</span> == <span class="number">0</span>:</span><br><span class="line">            <span class="variable language_">self</span>._counter += <span class="number">1</span></span><br><span class="line">            order.<span class="built_in">id</span> = <span class="variable language_">self</span>._counter</span><br><span class="line">        <span class="variable language_">self</span>._orders[order.<span class="built_in">id</span>] = order</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_by_id</span>(<span class="params">self, order_id: <span class="built_in">int</span></span>) -&gt; <span class="type">Optional</span>[Order]:</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>._orders.get(order_id)</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_by_user</span>(<span class="params">self, user_id: <span class="built_in">int</span></span>) -&gt; <span class="type">List</span>[Order]:</span><br><span class="line">        <span class="keyword">return</span> [o <span class="keyword">for</span> o <span class="keyword">in</span> <span class="variable language_">self</span>._orders.values() <span class="keyword">if</span> o.user_id == user_id]</span><br><span class="line"></span><br><span class="line"><span class="comment"># event/event_bus.py - 事件系统</span></span><br><span class="line"><span class="keyword">from</span> typing <span class="keyword">import</span> <span class="type">Dict</span>, <span class="type">List</span>, <span class="type">Callable</span></span><br><span class="line"><span class="keyword">import</span> asyncio</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">EventBus</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="variable language_">self</span>._subscribers: <span class="type">Dict</span>[<span class="built_in">str</span>, <span class="type">List</span>[<span class="type">Callable</span>]] = &#123;&#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">subscribe</span>(<span class="params">self, event_type: <span class="built_in">str</span>, handler: <span class="type">Callable</span></span>):</span><br><span class="line">        <span class="keyword">if</span> event_type <span class="keyword">not</span> <span class="keyword">in</span> <span class="variable language_">self</span>._subscribers:</span><br><span class="line">            <span class="variable language_">self</span>._subscribers[event_type] = []</span><br><span class="line">        <span class="variable language_">self</span>._subscribers[event_type].append(handler)</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">publish</span>(<span class="params">self, event_type: <span class="built_in">str</span>, data: <span class="built_in">dict</span></span>):</span><br><span class="line">        handlers = <span class="variable language_">self</span>._subscribers.get(event_type, [])</span><br><span class="line">        tasks = [handler(data) <span class="keyword">for</span> handler <span class="keyword">in</span> handlers]</span><br><span class="line">        <span class="keyword">await</span> asyncio.gather(*tasks)</span><br><span class="line"></span><br><span class="line"><span class="comment"># event/handlers.py - 事件处理器</span></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">handle_order_created</span>(<span class="params">event_data</span>):</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;[Notification] Order <span class="subst">&#123;event_data[<span class="string">&#x27;order_id&#x27;</span>]&#125;</span> created, sending email to <span class="subst">&#123;event_data[<span class="string">&#x27;user_email&#x27;</span>]&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">handle_order_paid</span>(<span class="params">event_data</span>):</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;[Inventory] Order <span class="subst">&#123;event_data[<span class="string">&#x27;order_id&#x27;</span>]&#125;</span> paid, updating inventory&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">handle_order_shipped</span>(<span class="params">event_data</span>):</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;[Analytics] Order <span class="subst">&#123;event_data[<span class="string">&#x27;order_id&#x27;</span>]&#125;</span> shipped, recording analytics&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># service/order_service.py - 业务服务</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">OrderService</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, order_repo, event_bus</span>):</span><br><span class="line">        <span class="variable language_">self</span>.order_repo = order_repo</span><br><span class="line">        <span class="variable language_">self</span>.event_bus = event_bus</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">create_order</span>(<span class="params">self, user_id: <span class="built_in">int</span>, items: <span class="type">List</span>[<span class="built_in">dict</span>]</span>) -&gt; Order:</span><br><span class="line">        order_items = [OrderItem(**item) <span class="keyword">for</span> item <span class="keyword">in</span> items]</span><br><span class="line">        order = Order(user_id=user_id, items=order_items)</span><br><span class="line">        order.validate()</span><br><span class="line">        <span class="variable language_">self</span>.order_repo.save(order)</span><br><span class="line">        </span><br><span class="line">        asyncio.run(<span class="variable language_">self</span>.event_bus.publish(</span><br><span class="line">            <span class="string">&quot;order_created&quot;</span>,</span><br><span class="line">            &#123;<span class="string">&quot;order_id&quot;</span>: order.<span class="built_in">id</span>, <span class="string">&quot;user_id&quot;</span>: user_id, <span class="string">&quot;user_email&quot;</span>: <span class="string">&quot;user@example.com&quot;</span>&#125;</span><br><span class="line">        ))</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> order</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">pay_order</span>(<span class="params">self, order_id: <span class="built_in">int</span></span>) -&gt; <span class="built_in">str</span>:</span><br><span class="line">        order = <span class="variable language_">self</span>.order_repo.get_by_id(order_id)</span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> order:</span><br><span class="line">            <span class="keyword">return</span> <span class="string">&quot;order not found&quot;</span></span><br><span class="line">        </span><br><span class="line">        state = STATE_MAP.get(order.status)</span><br><span class="line">        result = state.pay(order)</span><br><span class="line">        <span class="variable language_">self</span>.order_repo.save(order)</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">if</span> order.status == <span class="string">&quot;paid&quot;</span>:</span><br><span class="line">            asyncio.run(<span class="variable language_">self</span>.event_bus.publish(</span><br><span class="line">                <span class="string">&quot;order_paid&quot;</span>,</span><br><span class="line">                &#123;<span class="string">&quot;order_id&quot;</span>: order_id&#125;</span><br><span class="line">            ))</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> result</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">ship_order</span>(<span class="params">self, order_id: <span class="built_in">int</span></span>) -&gt; <span class="built_in">str</span>:</span><br><span class="line">        order = <span class="variable language_">self</span>.order_repo.get_by_id(order_id)</span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> order:</span><br><span class="line">            <span class="keyword">return</span> <span class="string">&quot;order not found&quot;</span></span><br><span class="line">        </span><br><span class="line">        state = STATE_MAP.get(order.status)</span><br><span class="line">        result = state.ship(order)</span><br><span class="line">        <span class="variable language_">self</span>.order_repo.save(order)</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">if</span> order.status == <span class="string">&quot;shipped&quot;</span>:</span><br><span class="line">            asyncio.run(<span class="variable language_">self</span>.event_bus.publish(</span><br><span class="line">                <span class="string">&quot;order_shipped&quot;</span>,</span><br><span class="line">                &#123;<span class="string">&quot;order_id&quot;</span>: order_id&#125;</span><br><span class="line">            ))</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> result</span><br><span class="line"></span><br><span class="line"><span class="comment"># main.py - 入口</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">main</span>():</span><br><span class="line">    <span class="comment"># 初始化依赖</span></span><br><span class="line">    order_repo = InMemoryOrderRepository()</span><br><span class="line">    event_bus = EventBus()</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 注册事件处理器</span></span><br><span class="line">    event_bus.subscribe(<span class="string">&quot;order_created&quot;</span>, handle_order_created)</span><br><span class="line">    event_bus.subscribe(<span class="string">&quot;order_paid&quot;</span>, handle_order_paid)</span><br><span class="line">    event_bus.subscribe(<span class="string">&quot;order_shipped&quot;</span>, handle_order_shipped)</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 创建服务</span></span><br><span class="line">    order_service = OrderService(order_repo, event_bus)</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 使用示例</span></span><br><span class="line">    order = order_service.create_order(</span><br><span class="line">        user_id=<span class="number">1</span>,</span><br><span class="line">        items=[</span><br><span class="line">            &#123;<span class="string">&quot;product_id&quot;</span>: <span class="number">101</span>, <span class="string">&quot;quantity&quot;</span>: <span class="number">2</span>, <span class="string">&quot;price&quot;</span>: <span class="number">99.99</span>&#125;,</span><br><span class="line">            &#123;<span class="string">&quot;product_id&quot;</span>: <span class="number">102</span>, <span class="string">&quot;quantity&quot;</span>: <span class="number">1</span>, <span class="string">&quot;price&quot;</span>: <span class="number">199.99</span>&#125;</span><br><span class="line">        ]</span><br><span class="line">    )</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;Created order: <span class="subst">&#123;order.<span class="built_in">id</span>&#125;</span>, total: <span class="subst">&#123;order.total_amount:<span class="number">.2</span>f&#125;</span>&quot;</span>)</span><br><span class="line">    </span><br><span class="line">    result = order_service.pay_order(order.<span class="built_in">id</span>)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;Pay result: <span class="subst">&#123;result&#125;</span>, status: <span class="subst">&#123;order.status&#125;</span>&quot;</span>)</span><br><span class="line">    </span><br><span class="line">    result = order_service.ship_order(order.<span class="built_in">id</span>)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;Ship result: <span class="subst">&#123;result&#125;</span>, status: <span class="subst">&#123;order.status&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    main()</span><br></pre></td></tr></table></figure><h3 id="9-4-使用示例"><a href="#9-4-使用示例" class="headerlink" title="9.4 使用示例"></a>9.4 使用示例</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></pre></td><td class="code"><pre><span class="line">python main.py</span><br><span class="line"></span><br><span class="line"><span class="comment"># 输出：</span></span><br><span class="line"><span class="comment"># [Notification] Order 1 created, sending email to user@example.com</span></span><br><span class="line"><span class="comment"># Created order: 1, total: 399.97</span></span><br><span class="line"><span class="comment"># [Inventory] Order 1 paid, updating inventory</span></span><br><span class="line"><span class="comment"># Pay result: order paid successfully, status: paid</span></span><br><span class="line"><span class="comment"># [Analytics] Order 1 shipped, recording analytics</span></span><br><span class="line"><span class="comment"># Ship result: order shipped, status: shipped</span></span><br></pre></td></tr></table></figure><h3 id="9-5-项目扩展"><a href="#9-5-项目扩展" class="headerlink" title="9.5 项目扩展"></a>9.5 项目扩展</h3><ol><li><strong>持久化层</strong>：集成 SQLAlchemy 或 Django ORM</li><li><strong>API 层</strong>：使用 FastAPI 提供 RESTful API</li><li><strong>缓存层</strong>：使用 Redis 缓存热门商品</li><li><strong>消息队列</strong>：使用 RabbitMQ 替代简单的事件总线</li><li><strong>事务管理</strong>：实现 Unit of Work 模式</li><li><strong>权限控制</strong>：添加认证和授权中间件</li></ol><hr><h2 id="附录：第十二阶段自检清单"><a href="#附录：第十二阶段自检清单" class="headerlink" title="附录：第十二阶段自检清单"></a>附录：第十二阶段自检清单</h2><ul><li><input disabled="" type="checkbox"> 理解并应用 SOLID 五大设计原则</li><li><input disabled="" type="checkbox"> 使用工厂模式解耦对象创建</li><li><input disabled="" type="checkbox"> 使用建造者模式构建复杂对象</li><li><input disabled="" type="checkbox"> 使用适配器模式兼容不同接口</li><li><input disabled="" type="checkbox"> 使用装饰器模式动态扩展功能</li><li><input disabled="" type="checkbox"> 使用观察者模式实现事件驱动</li><li><input disabled="" type="checkbox"> 使用模板方法模式定义算法骨架</li><li><input disabled="" type="checkbox"> 使用依赖注入提高代码可测试性</li><li><input disabled="" type="checkbox"> 使用仓储模式抽象数据访问层</li><li><input disabled="" type="checkbox"> 理解分层架构和各层职责</li><li><input disabled="" type="checkbox"> 能判断何时应该&#x2F;不应该使用某种设计模式</li><li><input disabled="" type="checkbox"> 使用状态模式管理对象生命周期</li><li><input disabled="" type="checkbox"> 使用事件驱动架构解耦系统组件</li><li><input disabled="" type="checkbox"> 识别并避免常见架构反模式</li></ul><hr><blockquote><p><strong>工程师寄语</strong>：设计模式是工具，不是教条。最好的代码是”没有模式的代码”——因为它足够简单，不需要模式。当你发现自己在生硬套用模式时，停下来想一想：是不是问题被过度复杂化了？架构的本质是<strong>管理复杂度</strong>，而不是增加复杂度。</p></blockquote><blockquote><p><strong>学习建议</strong>：</p><ol><li>从理解问题开始，而不是从模式开始</li><li>先写简单代码，遇到问题再考虑模式</li><li>在实际项目中尝试应用至少一种模式</li><li>定期回顾代码，思考是否有过度设计的地方</li><li>阅读优秀项目的源码，学习别人如何运用模式</li></ol></blockquote>]]>
    </content>
    <id>https://itdxb.cn/2026/07/24/python/document/%E3%80%90Python%E3%80%91%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E4%B8%8E%E6%9E%B6%E6%9E%84%E6%80%9D%E7%BB%B4%EF%BC%88%E7%AC%AC%E5%8D%81%E4%BA%8C%E9%98%B6%E6%AE%B5%EF%BC%89/</id>
    <link href="https://itdxb.cn/2026/07/24/python/document/%E3%80%90Python%E3%80%91%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E4%B8%8E%E6%9E%B6%E6%9E%84%E6%80%9D%E7%BB%B4%EF%BC%88%E7%AC%AC%E5%8D%81%E4%BA%8C%E9%98%B6%E6%AE%B5%EF%BC%89/"/>
    <published>2026-07-24T04:00:00.000Z</published>
    <summary>深入理解设计原则（SOLID）和常用设计模式，建立架构思维，学会在合适场景选择合适方案。</summary>
    <title>【Python】设计模式与架构思维（第十二阶段）</title>
    <updated>2026-07-24T05:54:02.572Z</updated>
  </entry>
  <entry>
    <author>
      <name>Super Bing</name>
    </author>
    <category term="Python" scheme="https://itdxb.cn/categories/Python/"/>
    <category term="教程" scheme="https://itdxb.cn/tags/%E6%95%99%E7%A8%8B/"/>
    <category term="Python" scheme="https://itdxb.cn/tags/Python/"/>
    <category term="内部原理" scheme="https://itdxb.cn/tags/%E5%86%85%E9%83%A8%E5%8E%9F%E7%90%86/"/>
    <category term="GIL" scheme="https://itdxb.cn/tags/GIL/"/>
    <category term="内存管理" scheme="https://itdxb.cn/tags/%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86/"/>
    <content>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><h1 id="十一、Python-内部原理与扩展"><a href="#十一、Python-内部原理与扩展" class="headerlink" title="十一、Python 内部原理与扩展"></a>十一、Python 内部原理与扩展</h1><blockquote><p><strong>阶段定位</strong>：理解 Python 的内部机制，能让你写出更高效的代码，更快地定位问题，并在必要时突破 Python 的性能瓶颈。本阶段从 CPython 解释器出发，讲解 GIL、内存管理、字节码等核心机制，以及如何用 C&#x2F;C++ 扩展 Python。</p></blockquote><figure class="highlight plaintext"><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><span class="line">┌──────────────────────────────────────────────────────────────────┐</span><br><span class="line">│                  Python 内部原理知识图谱                         │</span><br><span class="line">├──────────────────────────────────────────────────────────────────┤</span><br><span class="line">│                                                                  │</span><br><span class="line">│  CPython 解释器                                                   │</span><br><span class="line">│    ├── 源代码 → 词法分析 → 语法分析 → AST → 编译 → 字节码 → VM 执行│</span><br><span class="line">│    ├── 字节码指令集（LOAD_CONST / STORE_FAST / CALL_FUNCTION）    │</span><br><span class="line">│    └── dis 模块（查看字节码）                                     │</span><br><span class="line">│                                                                  │</span><br><span class="line">│  GIL（全局解释器锁）                                               │</span><br><span class="line">│    ├── 单线程执行 Python 字节码                                   │</span><br><span class="line">│    ├── CPU 密集型：多线程无效（需 multiprocessing）                │</span><br><span class="line">│    ├── I/O 密集型：线程释放 GIL（threading / asyncio 有效）       │</span><br><span class="line">│    └── Python 3.12+：无 GIL 实验性支持                            │</span><br><span class="line">│                                                                  │</span><br><span class="line">│  内存管理                                                         │</span><br><span class="line">│    ├── 引用计数（ob_refcnt）                                       │</span><br><span class="line">│    ├── 循环引用与 gc 模块                                         │</span><br><span class="line">│    ├── 分代垃圾回收（0/1/2 代）                                   │</span><br><span class="line">│    └── pymalloc 内存池（小对象优化）                               │</span><br><span class="line">│                                                                  │</span><br><span class="line">│  对象模型                                                         │</span><br><span class="line">│    ├── PyObject 结构（ob_refcnt + ob_type）                       │</span><br><span class="line">│    ├── 一切皆对象（包括 type 本身）                                │</span><br><span class="line">│    ├── interning（小整数/短字符串缓存）                           │</span><br><span class="line">│    └── 可变 vs 不可变对象                                          │</span><br><span class="line">│                                                                  │</span><br><span class="line">│  C/C++ 扩展                                                       │</span><br><span class="line">│    ├── ctypes（无需编译）                                         │</span><br><span class="line">│    ├── Cython（Python 语法编译为 C）                               │</span><br><span class="line">│    ├── C API（原生扩展模块）                                       │</span><br><span class="line">│    ├── pybind11（现代 C++ 绑定）                                  │</span><br><span class="line">│    └── Numba（JIT 编译）                                          │</span><br><span class="line">│                                                                  │</span><br><span class="line">└──────────────────────────────────────────────────────────────────┘</span><br></pre></td></tr></table></figure><hr><h2 id="1-CPython-解释器架构"><a href="#1-CPython-解释器架构" class="headerlink" title="1. CPython 解释器架构"></a>1. CPython 解释器架构</h2><h3 id="1-1-Python-执行流程"><a href="#1-1-Python-执行流程" class="headerlink" title="1.1 Python 执行流程"></a>1.1 Python 执行流程</h3><figure class="highlight plaintext"><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><span class="line">源代码 (.py)</span><br><span class="line">    |</span><br><span class="line">    v</span><br><span class="line">[词法分析] → tokens（词法单元）</span><br><span class="line">    |</span><br><span class="line">    v</span><br><span class="line">[语法分析] → AST（抽象语法树）</span><br><span class="line">    |</span><br><span class="line">    v</span><br><span class="line">[编译] → 字节码 (.pyc)</span><br><span class="line">    |</span><br><span class="line">    v</span><br><span class="line">[虚拟机 (VM)] → 执行字节码</span><br></pre></td></tr></table></figure><p><strong>执行流程图解</strong>：</p><figure class="highlight plaintext"><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><span class="line">┌──────────────┐    ┌──────────────┐    ┌──────────────┐</span><br><span class="line">│  source.py   │───→│  词法分析器   │───→│   tokens    │</span><br><span class="line">└──────────────┘    └──────────────┘    └──────┬───────┘</span><br><span class="line">                                               │</span><br><span class="line">                                               v</span><br><span class="line">┌──────────────┐    ┌──────────────┐    ┌──────────────┐</span><br><span class="line">│   .pyc 文件  │←───│   编译器     │←───│    AST      │</span><br><span class="line">└──────┬───────┘    └──────────────┘    └──────────────┘</span><br><span class="line">       │</span><br><span class="line">       v</span><br><span class="line">┌──────────────┐</span><br><span class="line">│  虚拟机 (VM) │──→ 执行字节码 → 输出结果</span><br><span class="line">└──────────────┘</span><br></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><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><span class="line"><span class="keyword">import</span> ast</span><br><span class="line"><span class="keyword">import</span> dis</span><br><span class="line"><span class="keyword">import</span> compileall</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看 AST</span></span><br><span class="line">source = <span class="string">&quot;x = 1 + 2&quot;</span></span><br><span class="line">tree = ast.parse(source)</span><br><span class="line"><span class="built_in">print</span>(ast.dump(tree, indent=<span class="number">2</span>))</span><br><span class="line"><span class="comment"># Module(</span></span><br><span class="line"><span class="comment">#   body=[</span></span><br><span class="line"><span class="comment">#     Assign(</span></span><br><span class="line"><span class="comment">#       targets=[Name(id=&#x27;x&#x27;, ctx=Store())],</span></span><br><span class="line"><span class="comment">#       value=BinOp(</span></span><br><span class="line"><span class="comment">#         left=Constant(value=1),</span></span><br><span class="line"><span class="comment">#         op=Add(),</span></span><br><span class="line"><span class="comment">#         right=Constant(value=2)))])</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看字节码</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">hello</span>():</span><br><span class="line">    x = <span class="number">1</span> + <span class="number">2</span></span><br><span class="line">    <span class="keyword">return</span> x</span><br><span class="line"></span><br><span class="line">dis.dis(hello)</span><br><span class="line"><span class="comment">#   2           0 LOAD_CONST               1 (3)</span></span><br><span class="line"><span class="comment">#               2 STORE_FAST               0 (x)</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment">#   3           4 LOAD_FAST                0 (x)</span></span><br><span class="line"><span class="comment">#               6 RETURN_VALUE</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 编译为 .pyc</span></span><br><span class="line">compileall.compile_dir(<span class="string">&quot;myproject&quot;</span>, force=<span class="literal">True</span>)</span><br></pre></td></tr></table></figure><h3 id="1-2-字节码指令集"><a href="#1-2-字节码指令集" class="headerlink" title="1.2 字节码指令集"></a>1.2 字节码指令集</h3><table><thead><tr><th>指令</th><th>含义</th><th>示例</th></tr></thead><tbody><tr><td><code>LOAD_CONST</code></td><td>加载常量到栈</td><td><code>LOAD_CONST 1 (3)</code></td></tr><tr><td><code>LOAD_FAST</code></td><td>加载局部变量</td><td><code>LOAD_FAST 0 (x)</code></td></tr><tr><td><code>LOAD_GLOBAL</code></td><td>加载全局变量</td><td><code>LOAD_GLOBAL 0 (print)</code></td></tr><tr><td><code>STORE_FAST</code></td><td>将栈顶存入局部变量</td><td><code>STORE_FAST 0 (x)</code></td></tr><tr><td><code>BINARY_ADD</code></td><td>弹出两数，相加后压栈</td><td><code>1 + 2</code></td></tr><tr><td><code>BINARY_SUBTRACT</code></td><td>弹出两数，相减后压栈</td><td><code>1 - 2</code></td></tr><tr><td><code>CALL_FUNCTION</code></td><td>调用函数</td><td><code>print()</code></td></tr><tr><td><code>RETURN_VALUE</code></td><td>返回栈顶值</td><td><code>return x</code></td></tr><tr><td><code>JUMP_ABSOLUTE</code></td><td>无条件跳转</td><td><code>while True:</code></td></tr><tr><td><code>POP_JUMP_IF_FALSE</code></td><td>条件跳转</td><td><code>if condition:</code></td></tr></tbody></table><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><span class="line"><span class="keyword">def</span> <span class="title function_">add</span>(<span class="params">a, b</span>):</span><br><span class="line">    <span class="keyword">return</span> a + b</span><br><span class="line"></span><br><span class="line">dis.dis(add)</span><br><span class="line"><span class="comment">#   2           0 LOAD_FAST                0 (a)</span></span><br><span class="line"><span class="comment">#               2 LOAD_FAST                1 (b)</span></span><br><span class="line"><span class="comment">#               4 BINARY_ADD</span></span><br><span class="line"><span class="comment">#               6 RETURN_VALUE</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 执行过程：</span></span><br><span class="line"><span class="comment"># 1. LOAD_FAST 0 → 栈: [a]</span></span><br><span class="line"><span class="comment"># 2. LOAD_FAST 1 → 栈: [a, b]</span></span><br><span class="line"><span class="comment"># 3. BINARY_ADD → 栈: [a+b]</span></span><br><span class="line"><span class="comment"># 4. RETURN_VALUE → 返回 a+b</span></span><br></pre></td></tr></table></figure><hr><h2 id="2-全局解释器锁（GIL）"><a href="#2-全局解释器锁（GIL）" class="headerlink" title="2. 全局解释器锁（GIL）"></a>2. 全局解释器锁（GIL）</h2><h3 id="2-1-什么是-GIL"><a href="#2-1-什么是-GIL" class="headerlink" title="2.1 什么是 GIL"></a>2.1 什么是 GIL</h3><p>GIL（Global Interpreter Lock）是 CPython 的一个互斥锁，确保<strong>同一时刻只有一个线程执行 Python 字节码</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><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> threading</span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">cpu_bound_task</span>(<span class="params">n</span>):</span><br><span class="line">    count = <span class="number">0</span></span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(n):</span><br><span class="line">        count += i</span><br><span class="line">    <span class="keyword">return</span> count</span><br><span class="line"></span><br><span class="line"><span class="comment"># 单线程</span></span><br><span class="line">start = time.time()</span><br><span class="line">cpu_bound_task(<span class="number">50_000_000</span>)</span><br><span class="line">cpu_bound_task(<span class="number">50_000_000</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;单线程: <span class="subst">&#123;time.time() - start:<span class="number">.2</span>f&#125;</span>s&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 多线程（由于 GIL，不会更快！）</span></span><br><span class="line">start = time.time()</span><br><span class="line">t1 = threading.Thread(target=cpu_bound_task, args=(<span class="number">50_000_000</span>,))</span><br><span class="line">t2 = threading.Thread(target=cpu_bound_task, args=(<span class="number">50_000_000</span>,))</span><br><span class="line">t1.start(); t2.start()</span><br><span class="line">t1.join(); t2.join()</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;多线程: <span class="subst">&#123;time.time() - start:<span class="number">.2</span>f&#125;</span>s&quot;</span>)   <span class="comment"># 可能更慢！</span></span><br></pre></td></tr></table></figure><p><strong>GIL 工作原理</strong>：</p><figure class="highlight plaintext"><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><span class="line">┌──────────────────────────────────────────────────────┐</span><br><span class="line">│              Python 解释器                           │</span><br><span class="line">├──────────────────────────────────────────────────────┤</span><br><span class="line">│                                                      │</span><br><span class="line">│   ┌──────────────────────────────────────────────┐   │</span><br><span class="line">│   │              GIL（全局解释器锁）              │   │</span><br><span class="line">│   └─────────────────────┬────────────────────────┘   │</span><br><span class="line">│                         │                            │</span><br><span class="line">│   ┌─────────────────────┴─────────────────────┐      │</span><br><span class="line">│   │                                           │      │</span><br><span class="line">│   │  Thread 1       │  Thread 2               │      │</span><br><span class="line">│   │  Python 字节码   │  Python 字节码          │      │</span><br><span class="line">│   │                 │                         │      │</span><br><span class="line">│   └─────────────────┴─────────────────────────┘      │</span><br><span class="line">│                                                      │</span><br><span class="line">│   同一时刻只有一个线程获得 GIL 执行字节码               │</span><br><span class="line">│   I/O 操作时会释放 GIL，其他线程可获得执行机会          │</span><br><span class="line">└──────────────────────────────────────────────────────┘</span><br></pre></td></tr></table></figure><h3 id="2-2-GIL-的影响与应对"><a href="#2-2-GIL-的影响与应对" class="headerlink" title="2.2 GIL 的影响与应对"></a>2.2 GIL 的影响与应对</h3><table><thead><tr><th>场景</th><th>GIL 影响</th><th>解决方案</th></tr></thead><tbody><tr><td>CPU 密集型（计算）</td><td>多线程无效</td><td><code>multiprocessing</code>、<code>ProcessPoolExecutor</code>、Cython</td></tr><tr><td>I&#x2F;O 密集型（网络&#x2F;文件）</td><td>影响小，线程会释放 GIL</td><td><code>threading</code>、<code>asyncio</code></td></tr><tr><td>混合场景</td><td>需具体分析</td><td>合理拆分任务类型</td></tr></tbody></table><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><span class="line"><span class="comment"># 方案 1：使用多进程绕过 GIL</span></span><br><span class="line"><span class="keyword">from</span> multiprocessing <span class="keyword">import</span> Pool</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">cpu_bound</span>(<span class="params">n</span>):</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">sum</span>(<span class="built_in">range</span>(n))</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    <span class="keyword">with</span> Pool(<span class="number">4</span>) <span class="keyword">as</span> p:</span><br><span class="line">        results = p.<span class="built_in">map</span>(cpu_bound, [<span class="number">50_000_000</span>] * <span class="number">4</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 方案 2：使用 asyncio（单线程协程，不依赖多线程）</span></span><br><span class="line"><span class="keyword">import</span> asyncio</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">io_bound</span>():</span><br><span class="line">    <span class="keyword">await</span> asyncio.sleep(<span class="number">1</span>)   <span class="comment"># 自动让出控制权</span></span><br></pre></td></tr></table></figure><h3 id="2-3-GIL-的移除趋势"><a href="#2-3-GIL-的移除趋势" class="headerlink" title="2.3 GIL 的移除趋势"></a>2.3 GIL 的移除趋势</h3><ul><li>Python 3.12 引入了<strong>无 GIL 的实验性版本</strong>（<code>--disable-gil</code>）</li><li>目标是在 Python 3.13+ 中提供正式的 nogil 支持</li><li>在此之前，I&#x2F;O 密集型用 <code>asyncio</code>，CPU 密集型用 <code>multiprocessing</code></li></ul><p><strong>GIL 移除进度</strong>：</p><figure class="highlight plaintext"><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><span class="line">Python 3.10: GIL 仍然存在</span><br><span class="line">    ↓</span><br><span class="line">Python 3.11: 改进 GIL 释放策略</span><br><span class="line">    ↓</span><br><span class="line">Python 3.12: 实验性 --disable-gil 标志</span><br><span class="line">    ↓</span><br><span class="line">Python 3.13: 预计提供正式 nogil 支持</span><br><span class="line">    ↓</span><br><span class="line">未来: 完全移除 GIL（目标）</span><br></pre></td></tr></table></figure><hr><h2 id="3-内存管理机制"><a href="#3-内存管理机制" class="headerlink" title="3. 内存管理机制"></a>3. 内存管理机制</h2><h3 id="3-1-引用计数"><a href="#3-1-引用计数" class="headerlink" title="3.1 引用计数"></a>3.1 引用计数</h3><p>Python 主要使用<strong>引用计数</strong>管理内存，每个对象维护一个 <code>ob_refcnt</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> sys</span><br><span class="line"></span><br><span class="line">a = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]</span><br><span class="line"><span class="built_in">print</span>(sys.getrefcount(a))   <span class="comment"># 2（a 的引用 + getrefcount 的参数引用）</span></span><br><span class="line"></span><br><span class="line">b = a</span><br><span class="line"><span class="built_in">print</span>(sys.getrefcount(a))   <span class="comment"># 3</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">del</span> b</span><br><span class="line"><span class="built_in">print</span>(sys.getrefcount(a))   <span class="comment"># 2</span></span><br></pre></td></tr></table></figure><p><strong>引用计数增减时机</strong>：</p><table><thead><tr><th>操作</th><th>引用计数变化</th></tr></thead><tbody><tr><td>对象被赋值给变量</td><td>+1</td></tr><tr><td>变量被删除（<code>del</code>）或重新赋值</td><td>-1</td></tr><tr><td>对象被传递给函数</td><td>+1（函数返回后 -1）</td></tr><tr><td>对象被放入容器（列表、字典）</td><td>+1</td></tr><tr><td>对象从容器中移除</td><td>-1</td></tr></tbody></table><h3 id="3-2-循环引用与垃圾回收"><a href="#3-2-循环引用与垃圾回收" class="headerlink" title="3.2 循环引用与垃圾回收"></a>3.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><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><span class="line"><span class="keyword">import</span> gc</span><br><span class="line"></span><br><span class="line"><span class="comment"># 循环引用：引用计数无法处理</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Node</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="variable language_">self</span>.<span class="built_in">next</span> = <span class="literal">None</span></span><br><span class="line"></span><br><span class="line">a = Node()</span><br><span class="line">b = Node()</span><br><span class="line">a.<span class="built_in">next</span> = b</span><br><span class="line">b.<span class="built_in">next</span> = a</span><br><span class="line"></span><br><span class="line"><span class="keyword">del</span> a</span><br><span class="line"><span class="keyword">del</span> b</span><br><span class="line"><span class="comment"># a 和 b 相互引用，引用计数永不为 0，内存泄漏！</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Python 的 gc 模块通过&quot;分代垃圾回收&quot;检测循环引用</span></span><br><span class="line">gc.collect()   <span class="comment"># 强制回收</span></span><br><span class="line"><span class="built_in">print</span>(gc.garbage)   <span class="comment"># 不可达对象列表</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 监控垃圾回收</span></span><br><span class="line"><span class="built_in">print</span>(gc.get_count())   <span class="comment"># (young, middle, old) 代计数</span></span><br><span class="line"><span class="built_in">print</span>(gc.get_threshold())   <span class="comment"># 回收阈值</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 禁用/启用 GC（某些性能敏感场景）</span></span><br><span class="line">gc.disable()</span><br><span class="line"><span class="comment"># ... 执行关键代码 ...</span></span><br><span class="line">gc.enable()</span><br></pre></td></tr></table></figure><p><strong>循环引用问题</strong>：</p><figure class="highlight plaintext"><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><span class="line">引用计数的局限：</span><br><span class="line">┌──────────┐     ┌──────────┐</span><br><span class="line">│   Node A │────→│   Node B │</span><br><span class="line">└────┬─────┘     └────┬─────┘</span><br><span class="line">     │               │</span><br><span class="line">     └───────────────┘</span><br><span class="line">          ←（反向引用）</span><br><span class="line"></span><br><span class="line">即使删除了外部引用（del a, del b），</span><br><span class="line">A 和 B 的引用计数仍然为 1（相互引用），</span><br><span class="line">引用计数永远不会降到 0！</span><br><span class="line"></span><br><span class="line">解决方案：分代垃圾回收（gc 模块）</span><br></pre></td></tr></table></figure><h3 id="3-3-分代垃圾回收"><a href="#3-3-分代垃圾回收" class="headerlink" title="3.3 分代垃圾回收"></a>3.3 分代垃圾回收</h3><p>Python 将对象分为 3 代：</p><table><thead><tr><th>代</th><th>描述</th><th>回收频率</th></tr></thead><tbody><tr><td><strong>第 0 代</strong></td><td>新创建的对象</td><td>最高</td></tr><tr><td><strong>第 1 代</strong></td><td>经历过一次回收存活的对象</td><td>中等</td></tr><tr><td><strong>第 2 代</strong></td><td>经历过多次回收存活的对象（长期存活）</td><td>最低</td></tr></tbody></table><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><span class="line"><span class="comment"># 调整阈值</span></span><br><span class="line">gc.set_threshold(<span class="number">700</span>, <span class="number">10</span>, <span class="number">10</span>)</span><br><span class="line"><span class="comment"># 第 0 代超过 700 个对象触发回收</span></span><br><span class="line"><span class="comment"># 第 0 代回收 10 次，触发第 1 代回收</span></span><br><span class="line"><span class="comment"># 第 1 代回收 10 次，触发第 2 代回收</span></span><br></pre></td></tr></table></figure><p><strong>分代回收流程图</strong>：</p><figure class="highlight plaintext"><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><span class="line">新对象创建</span><br><span class="line">    │</span><br><span class="line">    ▼</span><br><span class="line">第 0 代对象池</span><br><span class="line">    │</span><br><span class="line">    ├── 超过阈值（默认 700）</span><br><span class="line">    │       │</span><br><span class="line">    │       ▼</span><br><span class="line">    │   执行第 0 代回收</span><br><span class="line">    │       │</span><br><span class="line">    │       ├── 存活对象 → 晋升到第 1 代</span><br><span class="line">    │       └── 不可达对象 → 释放内存</span><br><span class="line">    │</span><br><span class="line">    └── 第 0 代回收次数达到阈值（默认 10）</span><br><span class="line">            │</span><br><span class="line">            ▼</span><br><span class="line">        执行第 1 代回收</span><br><span class="line">            │</span><br><span class="line">            ├── 存活对象 → 晋升到第 2 代</span><br><span class="line">            └── 不可达对象 → 释放内存</span><br></pre></td></tr></table></figure><h3 id="3-4-del-的陷阱"><a href="#3-4-del-的陷阱" class="headerlink" title="3.4 __del__ 的陷阱"></a>3.4 <code>__del__</code> 的陷阱</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><span class="line"><span class="keyword">class</span> <span class="title class_">Resource</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__del__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;Resource 被释放&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 陷阱 1：循环引用中的 __del__ 可能导致对象无法回收</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">BadNode</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__del__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;BadNode 被释放&quot;</span>)</span><br><span class="line"></span><br><span class="line">a = BadNode()</span><br><span class="line">b = BadNode()</span><br><span class="line">a.ref = b</span><br><span class="line">b.ref = a</span><br><span class="line"><span class="keyword">del</span> a, b</span><br><span class="line">gc.collect()   <span class="comment"># 可能无法回收！因为 __del__ 增加了复杂度</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 解决方案：使用 weakref</span></span><br><span class="line"><span class="keyword">import</span> weakref</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">GoodNode</span>:</span><br><span class="line">    <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line">a = GoodNode()</span><br><span class="line">b = GoodNode()</span><br><span class="line">a.ref = weakref.ref(b)   <span class="comment"># 弱引用，不增加引用计数</span></span><br><span class="line">b.ref = weakref.ref(a)</span><br></pre></td></tr></table></figure><h3 id="3-5-内存池：pymalloc"><a href="#3-5-内存池：pymalloc" class="headerlink" title="3.5 内存池：pymalloc"></a>3.5 内存池：pymalloc</h3><figure class="highlight plaintext"><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><span class="line">Python 内存分配层次：</span><br><span class="line">1. 用户代码请求内存</span><br><span class="line">2. pymalloc（小对象 &lt; 512 bytes）：从内存池分配</span><br><span class="line">3. C malloc（大对象）：直接向操作系统申请</span><br><span class="line">4. 操作系统：管理物理内存</span><br><span class="line"></span><br><span class="line">pymalloc 优势：</span><br><span class="line">- 减少系统调用开销</span><br><span class="line">- 减少内存碎片</span><br><span class="line">- 提高小对象分配速度</span><br></pre></td></tr></table></figure><p><strong>内存池结构</strong>：</p><figure class="highlight plaintext"><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><span class="line">┌─────────────────────────────────────────────────────────────┐</span><br><span class="line">│                      Python 进程                            │</span><br><span class="line">├─────────────────────────────────────────────────────────────┤</span><br><span class="line">│                                                             │</span><br><span class="line">│   ┌───────────────────────────────────────────────────────┐  │</span><br><span class="line">│   │                   pymalloc 内存池                    │  │</span><br><span class="line">│   │                                                       │  │</span><br><span class="line">│   │   ┌─────────┬─────────┬─────────┬─────────┐          │  │</span><br><span class="line">│   │   │ Arena 1 │ Arena 2 │ Arena 3 │ ...     │          │  │</span><br><span class="line">│   │   └────┬────┴────┬────┴────┬────┴────┬────┘          │  │</span><br><span class="line">│   │        │         │         │         │                 │  │</span><br><span class="line">│   │   ┌────▼────┐ ┌──▼──┐ ┌──▼──┐ ┌──▼──┐              │  │</span><br><span class="line">│   │   │ 256KB   │ │8KB  │ │4KB  │ │2KB  │   ...         │  │</span><br><span class="line">│   │   │ 大对象  │ │128B │ │64B  │ │32B  │              │  │</span><br><span class="line">│   │   │ 区域    │ │块   │ │块   │ │块   │              │  │</span><br><span class="line">│   │   └─────────┘ └─────┘ └─────┘ └─────┘              │  │</span><br><span class="line">│   └───────────────────────────────────────────────────────┘  │</span><br><span class="line">│                                                             │</span><br><span class="line">│   ┌───────────────────────────────────────────────────────┐  │</span><br><span class="line">│   │           C malloc（大对象直接分配）                   │  │</span><br><span class="line">│   └───────────────────────────────────────────────────────┘  │</span><br><span class="line">│                                                             │</span><br><span class="line">└─────────────────────────────────────────────────────────────┘</span><br></pre></td></tr></table></figure><hr><h2 id="4-对象模型"><a href="#4-对象模型" class="headerlink" title="4. 对象模型"></a>4. 对象模型</h2><h3 id="4-1-一切皆对象"><a href="#4-1-一切皆对象" class="headerlink" title="4.1 一切皆对象"></a>4.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></pre></td><td class="code"><pre><span class="line"><span class="comment"># Python 中一切皆为对象，包括类型本身</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">type</span>(<span class="number">1</span>))          <span class="comment"># &lt;class &#x27;int&#x27;&gt;</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">type</span>(<span class="built_in">int</span>))        <span class="comment"># &lt;class &#x27;type&#x27;&gt;</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">type</span>(<span class="built_in">type</span>))       <span class="comment"># &lt;class &#x27;type&#x27;&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 类型和对象的关系</span></span><br><span class="line"><span class="comment"># 1 是 int 的实例</span></span><br><span class="line"><span class="comment"># int 是 type 的实例</span></span><br><span class="line"><span class="comment"># type 是 type 自身的实例</span></span><br></pre></td></tr></table></figure><p><strong>类型层次结构</strong>：</p><figure class="highlight plaintext"><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><span class="line">type（元类）</span><br><span class="line">    │</span><br><span class="line">    ├── int（类型）</span><br><span class="line">    │   └── 1, 2, 3（实例）</span><br><span class="line">    │</span><br><span class="line">    ├── str（类型）</span><br><span class="line">    │   └── &quot;hello&quot;（实例）</span><br><span class="line">    │</span><br><span class="line">    ├── list（类型）</span><br><span class="line">    │   └── [1, 2]（实例）</span><br><span class="line">    │</span><br><span class="line">    └── type（类型）← 自身的实例</span><br><span class="line">        └── type（实例）</span><br></pre></td></tr></table></figure><h3 id="4-2-PyObject-结构"><a href="#4-2-PyObject-结构" class="headerlink" title="4.2 PyObject 结构"></a>4.2 <code>PyObject</code> 结构</h3><p>CPython 中所有对象的 C 结构体都包含：</p><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 简化的 PyObject 结构</span></span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> _<span class="title">object</span> &#123;</span></span><br><span class="line">    _PyObject_HEAD_EXTRA   <span class="comment">// 双向链表指针（调试/GC 用）</span></span><br><span class="line">    Py_ssize_t ob_refcnt;  <span class="comment">// 引用计数</span></span><br><span class="line">    PyTypeObject *ob_type; <span class="comment">// 类型指针</span></span><br><span class="line">&#125; PyObject;</span><br></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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 从 Python 层面观察对象身份</span></span><br><span class="line">a = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]</span><br><span class="line">b = a</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">id</span>(a))        <span class="comment"># 对象内存地址</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">id</span>(b))        <span class="comment"># 与 a 相同</span></span><br><span class="line"><span class="built_in">print</span>(a <span class="keyword">is</span> b)       <span class="comment"># True</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># interning：小整数和短字符串的缓存</span></span><br><span class="line">a = <span class="number">256</span></span><br><span class="line">b = <span class="number">256</span></span><br><span class="line"><span class="built_in">print</span>(a <span class="keyword">is</span> b)       <span class="comment"># True（-5 ~ 256 被缓存）</span></span><br><span class="line"></span><br><span class="line">a = <span class="number">257</span></span><br><span class="line">b = <span class="number">257</span></span><br><span class="line"><span class="built_in">print</span>(a <span class="keyword">is</span> b)       <span class="comment"># False（交互式中可能为 True，编译优化）</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 字符串驻留</span></span><br><span class="line">a = <span class="string">&quot;hello&quot;</span></span><br><span class="line">b = <span class="string">&quot;hello&quot;</span></span><br><span class="line"><span class="built_in">print</span>(a <span class="keyword">is</span> b)       <span class="comment"># True（编译期驻留）</span></span><br></pre></td></tr></table></figure><h3 id="4-3-可变与不可变对象"><a href="#4-3-可变与不可变对象" class="headerlink" title="4.3 可变与不可变对象"></a>4.3 可变与不可变对象</h3><table><thead><tr><th>类型</th><th>示例</th><th>特性</th></tr></thead><tbody><tr><td>不可变</td><td><code>int</code>, <code>float</code>, <code>str</code>, <code>tuple</code>, <code>frozenset</code></td><td>hashable，可作为 dict 键</td></tr><tr><td>可变</td><td><code>list</code>, <code>dict</code>, <code>set</code></td><td>不可哈希，有 <code>__hash__ = None</code></td></tr></tbody></table><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><span class="line"><span class="comment"># 不可变对象的操作创建新对象</span></span><br><span class="line">a = <span class="string">&quot;hello&quot;</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">id</span>(a))        <span class="comment"># 地址 1</span></span><br><span class="line">a = a + <span class="string">&quot; world&quot;</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">id</span>(a))        <span class="comment"># 地址 2（新对象）</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 可变对象原地修改</span></span><br><span class="line">b = [<span class="number">1</span>, <span class="number">2</span>]</span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">id</span>(b))        <span class="comment"># 地址 3</span></span><br><span class="line">b.append(<span class="number">3</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">id</span>(b))        <span class="comment"># 地址 3（不变）</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 注意：tuple 包含可变元素时的陷阱</span></span><br><span class="line">t = ([<span class="number">1</span>, <span class="number">2</span>], <span class="number">3</span>)</span><br><span class="line">t[<span class="number">0</span>].append(<span class="number">4</span>)      <span class="comment"># 可以修改！tuple 本身没变，但内容变了</span></span><br><span class="line"><span class="built_in">print</span>(t)           <span class="comment"># ([1, 2, 4], 3)</span></span><br></pre></td></tr></table></figure><hr><h2 id="5-C-C-扩展-Python"><a href="#5-C-C-扩展-Python" class="headerlink" title="5. C&#x2F;C++ 扩展 Python"></a>5. C&#x2F;C++ 扩展 Python</h2><h3 id="5-1-为什么需要-C-扩展"><a href="#5-1-为什么需要-C-扩展" class="headerlink" title="5.1 为什么需要 C 扩展"></a>5.1 为什么需要 C 扩展</h3><ol><li><strong>性能</strong>：绕过 GIL，利用 C 的高性能</li><li><strong>复用</strong>：调用现有的 C&#x2F;C++ 库</li><li><strong>系统访问</strong>：操作系统底层接口</li></ol><h3 id="5-2-ctypes：无需编译的-C-调用"><a href="#5-2-ctypes：无需编译的-C-调用" class="headerlink" title="5.2 ctypes：无需编译的 C 调用"></a>5.2 ctypes：无需编译的 C 调用</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><span class="line"><span class="keyword">import</span> ctypes</span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"></span><br><span class="line"><span class="comment"># 加载 C 标准库</span></span><br><span class="line"><span class="keyword">if</span> os.name == <span class="string">&quot;nt&quot;</span>:</span><br><span class="line">    libc = ctypes.CDLL(<span class="string">&quot;msvcrt&quot;</span>)</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line">    libc = ctypes.CDLL(<span class="string">&quot;libc.so.6&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 调用 C 函数</span></span><br><span class="line">libc.printf(<span class="string">b&quot;Hello from C!\n&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 设置参数和返回值类型</span></span><br><span class="line">libc.rand.argtypes = []</span><br><span class="line">libc.rand.restype = ctypes.c_int</span><br><span class="line"><span class="built_in">print</span>(libc.rand())</span><br><span class="line"></span><br><span class="line"><span class="comment"># 加载自定义动态库</span></span><br><span class="line"><span class="comment"># mylib = ctypes.CDLL(&quot;./mylib.so&quot;)</span></span><br><span class="line"><span class="comment"># mylib.add.argtypes = [ctypes.c_int, ctypes.c_int]</span></span><br><span class="line"><span class="comment"># mylib.add.restype = ctypes.c_int</span></span><br><span class="line"><span class="comment"># result = mylib.add(3, 4)</span></span><br></pre></td></tr></table></figure><h3 id="5-3-Cython：Python-语法的-C-扩展"><a href="#5-3-Cython：Python-语法的-C-扩展" class="headerlink" title="5.3 Cython：Python 语法的 C 扩展"></a>5.3 Cython：Python 语法的 C 扩展</h3><figure class="highlight plaintext"><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><span class="line"># example.pyx</span><br><span class="line"># 文件扩展名为 .pyx</span><br><span class="line"></span><br><span class="line">def fib(int n):</span><br><span class="line">    &quot;&quot;&quot;Cython 版斐波那契（声明类型后编译为 C）。&quot;&quot;&quot;</span><br><span class="line">    cdef int a = 0</span><br><span class="line">    cdef int b = 1</span><br><span class="line">    cdef int i</span><br><span class="line">    for i in range(n):</span><br><span class="line">        a, b = b, a + b</span><br><span class="line">    return a</span><br><span class="line"></span><br><span class="line"># setup.py</span><br><span class="line">from setuptools import setup</span><br><span class="line">from Cython.Build import cythonize</span><br><span class="line"></span><br><span class="line">setup(</span><br><span class="line">    ext_modules=cythonize(&quot;example.pyx&quot;),</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"># 编译：python setup.py build_ext --inplace</span><br></pre></td></tr></table></figure><h3 id="5-4-C-API：编写原生扩展模块"><a href="#5-4-C-API：编写原生扩展模块" class="headerlink" title="5.4 C API：编写原生扩展模块"></a>5.4 C API：编写原生扩展模块</h3><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><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// mymodule.c</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> PY_SSIZE_T_CLEAN</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;Python.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">static</span> PyObject* <span class="title function_">mymodule_add</span><span class="params">(PyObject* self, PyObject* args)</span> &#123;</span><br><span class="line">    <span class="type">int</span> a, b;</span><br><span class="line">    <span class="keyword">if</span> (!PyArg_ParseTuple(args, <span class="string">&quot;ii&quot;</span>, &amp;a, &amp;b))</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">    <span class="keyword">return</span> PyLong_FromLong(a + b);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">static</span> PyObject* <span class="title function_">mymodule_fib</span><span class="params">(PyObject* self, PyObject* args)</span> &#123;</span><br><span class="line">    <span class="type">int</span> n;</span><br><span class="line">    <span class="keyword">if</span> (!PyArg_ParseTuple(args, <span class="string">&quot;i&quot;</span>, &amp;n))</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">    <span class="type">long</span> a = <span class="number">0</span>, b = <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; n; i++) &#123;</span><br><span class="line">        <span class="type">long</span> temp = a;</span><br><span class="line">        a = b;</span><br><span class="line">        b = temp + b;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> PyLong_FromLong(a);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">static</span> PyMethodDef MyModuleMethods[] = &#123;</span><br><span class="line">    &#123;<span class="string">&quot;add&quot;</span>, mymodule_add, METH_VARARGS, <span class="string">&quot;Add two integers.&quot;</span>&#125;,</span><br><span class="line">    &#123;<span class="string">&quot;fib&quot;</span>, mymodule_fib, METH_VARARGS, <span class="string">&quot;Compute Fibonacci number.&quot;</span>&#125;,</span><br><span class="line">    &#123;<span class="literal">NULL</span>, <span class="literal">NULL</span>, <span class="number">0</span>, <span class="literal">NULL</span>&#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="class"><span class="keyword">struct</span> <span class="title">PyModuleDef</span> <span class="title">mymodule</span> =</span> &#123;</span><br><span class="line">    PyModuleDef_HEAD_INIT,</span><br><span class="line">    <span class="string">&quot;mymodule&quot;</span>,</span><br><span class="line">    <span class="string">&quot;Example module&quot;</span>,</span><br><span class="line">    <span class="number">-1</span>,</span><br><span class="line">    MyModuleMethods</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">PyMODINIT_FUNC <span class="title function_">PyInit_mymodule</span><span class="params">(<span class="type">void</span>)</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> PyModule_Create(&amp;mymodule);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="5-5-pybind11：现代的-C-绑定"><a href="#5-5-pybind11：现代的-C-绑定" class="headerlink" title="5.5 pybind11：现代的 C++ 绑定"></a>5.5 pybind11：现代的 C++ 绑定</h3><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// example.cpp</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;pybind11/pybind11.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">add</span><span class="params">(<span class="type">int</span> a, <span class="type">int</span> b)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> a + b;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">double</span> <span class="title">compute</span><span class="params">(<span class="type">double</span> x, <span class="type">double</span> y)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> x * x + y * y;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">namespace</span> py = pybind11;</span><br><span class="line"></span><br><span class="line"><span class="built_in">PYBIND11_MODULE</span>(example, m) &#123;</span><br><span class="line">    m.<span class="built_in">doc</span>() = <span class="string">&quot;pybind11 example plugin&quot;</span>;</span><br><span class="line">    m.<span class="built_in">def</span>(<span class="string">&quot;add&quot;</span>, &amp;add, <span class="string">&quot;A function that adds two numbers&quot;</span>);</span><br><span class="line">    m.<span class="built_in">def</span>(<span class="string">&quot;compute&quot;</span>, &amp;compute, <span class="string">&quot;Compute x^2 + y^2&quot;</span>);</span><br><span class="line">&#125;</span><br></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></pre></td><td class="code"><pre><span class="line"><span class="comment"># setup.py</span></span><br><span class="line"><span class="keyword">from</span> pybind11.setup_helpers <span class="keyword">import</span> Pybind11Extension</span><br><span class="line"><span class="keyword">from</span> setuptools <span class="keyword">import</span> setup</span><br><span class="line"></span><br><span class="line">ext_modules = [</span><br><span class="line">    Pybind11Extension(<span class="string">&quot;example&quot;</span>, [<span class="string">&quot;example.cpp&quot;</span>]),</span><br><span class="line">]</span><br><span class="line"></span><br><span class="line">setup(ext_modules=ext_modules)</span><br></pre></td></tr></table></figure><h3 id="5-6-扩展方案选择"><a href="#5-6-扩展方案选择" class="headerlink" title="5.6 扩展方案选择"></a>5.6 扩展方案选择</h3><table><thead><tr><th>方案</th><th>难度</th><th>性能</th><th>适用场景</th></tr></thead><tbody><tr><td><code>ctypes</code></td><td>低</td><td>中</td><td>快速调用现有 DLL&#x2F;SO</td></tr><tr><td><code>Cython</code></td><td>中</td><td>高</td><td>算法加速、调用 C 库</td></tr><tr><td><code>C API</code></td><td>高</td><td>最高</td><td>深度集成、自定义类型</td></tr><tr><td><code>pybind11</code></td><td>中</td><td>高</td><td>C++ 库绑定（推荐）</td></tr><tr><td><code>cffi</code></td><td>低</td><td>中</td><td>替代 ctypes，更现代</td></tr><tr><td><code>Numba</code></td><td>低</td><td>高</td><td>数值计算 JIT</td></tr></tbody></table><p><strong>选择决策树</strong>：</p><figure class="highlight plaintext"><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><span class="line">需要扩展 Python？</span><br><span class="line">    │</span><br><span class="line">    ├── 调用现有 C/C++ 库？</span><br><span class="line">    │       │</span><br><span class="line">    │       ├── Yes → ctypes（简单）或 pybind11（C++）</span><br><span class="line">    │       │</span><br><span class="line">    │       └── No → 优化 Python 代码性能？</span><br><span class="line">    │                   │</span><br><span class="line">    │                   ├── Yes → Numba（数值）或 Cython（通用）</span><br><span class="line">    │                   │</span><br><span class="line">    │                   └── No → 需要自定义类型？</span><br><span class="line">    │                               │</span><br><span class="line">    │                               ├── Yes → C API</span><br><span class="line">    │                               │</span><br><span class="line">    │                               └── No → 检查是否真的需要扩展</span><br></pre></td></tr></table></figure><hr><h2 id="6-性能剖析与优化底层"><a href="#6-性能剖析与优化底层" class="headerlink" title="6. 性能剖析与优化底层"></a>6. 性能剖析与优化底层</h2><h3 id="6-1-sys-setrecursionlimit"><a href="#6-1-sys-setrecursionlimit" class="headerlink" title="6.1 sys.setrecursionlimit"></a>6.1 <code>sys.setrecursionlimit</code></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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> sys</span><br><span class="line"></span><br><span class="line"><span class="comment"># 默认递归深度限制 1000</span></span><br><span class="line"><span class="built_in">print</span>(sys.getrecursionlimit())   <span class="comment"># 1000</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 谨慎调整（可能导致栈溢出）</span></span><br><span class="line">sys.setrecursionlimit(<span class="number">2000</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 尾递归优化（Python 不支持，但可以用循环模拟）</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">factorial_iterative</span>(<span class="params">n</span>):</span><br><span class="line">    result = <span class="number">1</span></span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">2</span>, n + <span class="number">1</span>):</span><br><span class="line">        result *= i</span><br><span class="line">    <span class="keyword">return</span> result</span><br></pre></td></tr></table></figure><h3 id="6-2-slots-内存优化"><a href="#6-2-slots-内存优化" class="headerlink" title="6.2 __slots__ 内存优化"></a>6.2 <code>__slots__</code> 内存优化</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><span class="line"><span class="keyword">class</span> <span class="title class_">Point</span>:</span><br><span class="line">    __slots__ = [<span class="string">&quot;x&quot;</span>, <span class="string">&quot;y&quot;</span>]   <span class="comment"># 不创建 __dict__，节省内存</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, x, y</span>):</span><br><span class="line">        <span class="variable language_">self</span>.x = x</span><br><span class="line">        <span class="variable language_">self</span>.y = y</span><br><span class="line"></span><br><span class="line"><span class="comment"># 效果对比</span></span><br><span class="line"><span class="keyword">import</span> sys</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">NormalPoint</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, x, y</span>):</span><br><span class="line">        <span class="variable language_">self</span>.x = x</span><br><span class="line">        <span class="variable language_">self</span>.y = y</span><br><span class="line"></span><br><span class="line">normal = NormalPoint(<span class="number">1</span>, <span class="number">2</span>)</span><br><span class="line">slotted = Point(<span class="number">1</span>, <span class="number">2</span>)</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(sys.getsizeof(normal) + sys.getsizeof(normal.__dict__))   <span class="comment"># ~152 bytes</span></span><br><span class="line"><span class="built_in">print</span>(sys.getsizeof(slotted))   <span class="comment"># ~56 bytes（节省约 63%）</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 注意事项：</span></span><br><span class="line"><span class="comment"># 1. 无法动态添加属性</span></span><br><span class="line"><span class="comment"># 2. 多重继承时需谨慎</span></span><br><span class="line"><span class="comment"># 3. 需要弱引用时要加 &quot;__weakref__&quot;</span></span><br></pre></td></tr></table></figure><h3 id="6-3-字符串驻留（Interning）"><a href="#6-3-字符串驻留（Interning）" class="headerlink" title="6.3 字符串驻留（Interning）"></a>6.3 字符串驻留（Interning）</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><span class="line"><span class="keyword">import</span> sys</span><br><span class="line"></span><br><span class="line"><span class="comment"># 手动驻留字符串</span></span><br><span class="line">a = sys.intern(<span class="string">&quot;a very long string that might be repeated&quot;</span>)</span><br><span class="line">b = sys.intern(<span class="string">&quot;a very long string that might be repeated&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(a <span class="keyword">is</span> b)   <span class="comment"># True</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 用途：大量重复字符串时节省内存（如解析 CSV、日志处理）</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 自动驻留规则：</span></span><br><span class="line"><span class="comment"># - 标识符（变量名、函数名等）</span></span><br><span class="line"><span class="comment"># - 短字符串（通常 &lt; 20 字符）</span></span><br><span class="line"><span class="comment"># - 常量字符串（编译期优化）</span></span><br></pre></td></tr></table></figure><h3 id="6-4-sys-settrace：代码追踪（调试器原理）"><a href="#6-4-sys-settrace：代码追踪（调试器原理）" class="headerlink" title="6.4 sys.settrace：代码追踪（调试器原理）"></a>6.4 <code>sys.settrace</code>：代码追踪（调试器原理）</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><span class="line"><span class="keyword">import</span> sys</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">trace_calls</span>(<span class="params">frame, event, arg</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;追踪函数调用/返回事件。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">if</span> event == <span class="string">&#x27;call&#x27;</span>:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;调用函数: <span class="subst">&#123;frame.f_code.co_name&#125;</span> 在 <span class="subst">&#123;frame.f_code.co_filename&#125;</span>:<span class="subst">&#123;frame.f_lineno&#125;</span>&quot;</span>)</span><br><span class="line">    <span class="keyword">elif</span> event == <span class="string">&#x27;return&#x27;</span>:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;返回: <span class="subst">&#123;frame.f_code.co_name&#125;</span> -&gt; <span class="subst">&#123;arg&#125;</span>&quot;</span>)</span><br><span class="line">    <span class="keyword">return</span> trace_calls   <span class="comment"># 返回自身则追踪此函数内部</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">trace_lines</span>(<span class="params">frame, event, arg</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;追踪每一行代码执行。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">if</span> event == <span class="string">&#x27;line&#x27;</span>:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;执行行: <span class="subst">&#123;frame.f_lineno&#125;</span>: <span class="subst">&#123;frame.f_code.co_filename&#125;</span>&quot;</span>)</span><br><span class="line">    <span class="keyword">return</span> trace_lines</span><br><span class="line"></span><br><span class="line"><span class="comment"># 设置追踪</span></span><br><span class="line">sys.settrace(trace_calls)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">factorial</span>(<span class="params">n</span>):</span><br><span class="line">    <span class="keyword">if</span> n &lt;= <span class="number">1</span>:</span><br><span class="line">        <span class="keyword">return</span> <span class="number">1</span></span><br><span class="line">    <span class="keyword">return</span> n * factorial(n - <span class="number">1</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># factorial(3)   # 会打印详细的调用追踪</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 重置追踪</span></span><br><span class="line">sys.settrace(<span class="literal">None</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 工程场景：自定义调试器、性能分析工具、代码覆盖率工具</span></span><br><span class="line"><span class="comment"># 注意：启用追踪会显著降低性能，生产环境不要使用</span></span><br></pre></td></tr></table></figure><hr><h2 id="7-工作实战场景"><a href="#7-工作实战场景" class="headerlink" title="7. 工作实战场景"></a>7. 工作实战场景</h2><h3 id="7-1-性能优化实战"><a href="#7-1-性能优化实战" class="headerlink" title="7.1 性能优化实战"></a>7.1 性能优化实战</h3><p><strong>场景描述</strong>：你的数据处理脚本需要处理百万级数据，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><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><span class="line"><span class="comment"># 问题：CPU 密集型任务在 Python 中运行缓慢</span></span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">process_data</span>(<span class="params">data</span>):</span><br><span class="line">    result = []</span><br><span class="line">    <span class="keyword">for</span> item <span class="keyword">in</span> data:</span><br><span class="line">        <span class="comment"># 复杂计算</span></span><br><span class="line">        processed = item * <span class="number">2</span> + item ** <span class="number">2</span> + item ** <span class="number">3</span></span><br><span class="line">        result.append(processed)</span><br><span class="line">    <span class="keyword">return</span> result</span><br><span class="line"></span><br><span class="line"><span class="comment"># 解决方案 1：使用多进程</span></span><br><span class="line"><span class="keyword">from</span> multiprocessing <span class="keyword">import</span> Pool</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">optimize_with_multiprocessing</span>(<span class="params">data, num_workers=<span class="number">4</span></span>):</span><br><span class="line">    chunk_size = <span class="built_in">len</span>(data) // num_workers</span><br><span class="line">    chunks = [data[i:i+chunk_size] <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">0</span>, <span class="built_in">len</span>(data), chunk_size)]</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">with</span> Pool(num_workers) <span class="keyword">as</span> p:</span><br><span class="line">        results = p.<span class="built_in">map</span>(process_data, chunks)</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> [item <span class="keyword">for</span> sublist <span class="keyword">in</span> results <span class="keyword">for</span> item <span class="keyword">in</span> sublist]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 解决方案 2：使用 Numba JIT</span></span><br><span class="line"><span class="keyword">from</span> numba <span class="keyword">import</span> jit</span><br><span class="line"></span><br><span class="line"><span class="meta">@jit(<span class="params">nopython=<span class="literal">True</span></span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">process_data_jit</span>(<span class="params">data</span>):</span><br><span class="line">    result = []</span><br><span class="line">    <span class="keyword">for</span> item <span class="keyword">in</span> data:</span><br><span class="line">        processed = item * <span class="number">2</span> + item ** <span class="number">2</span> + item ** <span class="number">3</span></span><br><span class="line">        result.append(processed)</span><br><span class="line">    <span class="keyword">return</span> result</span><br></pre></td></tr></table></figure><h3 id="7-2-内存泄漏排查"><a href="#7-2-内存泄漏排查" class="headerlink" title="7.2 内存泄漏排查"></a>7.2 内存泄漏排查</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><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><span class="line"><span class="keyword">import</span> gc</span><br><span class="line"><span class="keyword">import</span> weakref</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">debug_memory_leaks</span>():</span><br><span class="line">    <span class="string">&quot;&quot;&quot;内存泄漏排查工具。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="comment"># 启用 GC 调试</span></span><br><span class="line">    gc.set_debug(gc.DEBUG_LEAK)</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 记录创建的对象</span></span><br><span class="line">    objects_before = <span class="built_in">set</span>(<span class="built_in">map</span>(<span class="built_in">id</span>, gc.get_objects()))</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 执行疑似泄漏的代码</span></span><br><span class="line">    <span class="comment"># ... your code ...</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 强制回收</span></span><br><span class="line">    gc.collect()</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 找出新增对象</span></span><br><span class="line">    objects_after = <span class="built_in">set</span>(<span class="built_in">map</span>(<span class="built_in">id</span>, gc.get_objects()))</span><br><span class="line">    new_objects = objects_after - objects_before</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 分析新增对象类型</span></span><br><span class="line">    type_counts = &#123;&#125;</span><br><span class="line">    <span class="keyword">for</span> obj_id <span class="keyword">in</span> new_objects:</span><br><span class="line">        obj = <span class="literal">None</span></span><br><span class="line">        <span class="keyword">try</span>:</span><br><span class="line">            obj = gc.get_objects()[<span class="built_in">list</span>(gc.get_objects()).index(gc.get_objects()[<span class="number">0</span>])]</span><br><span class="line">            obj_type = <span class="built_in">type</span>(obj).__name__</span><br><span class="line">            type_counts[obj_type] = type_counts.get(obj_type, <span class="number">0</span>) + <span class="number">1</span></span><br><span class="line">        <span class="keyword">except</span>:</span><br><span class="line">            <span class="keyword">pass</span></span><br><span class="line">    </span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;新增对象类型统计:&quot;</span>, type_counts)</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 关闭调试</span></span><br><span class="line">    gc.set_debug(<span class="number">0</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用 weakref 追踪对象生命周期</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">ResourceManager</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="variable language_">self</span>._resources = weakref.WeakValueDictionary()</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">add_resource</span>(<span class="params">self, name, resource</span>):</span><br><span class="line">        <span class="variable language_">self</span>._resources[name] = resource</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_resource</span>(<span class="params">self, name</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>._resources.get(name)</span><br></pre></td></tr></table></figure><h3 id="7-3-字节码分析优化"><a href="#7-3-字节码分析优化" class="headerlink" title="7.3 字节码分析优化"></a>7.3 字节码分析优化</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><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><span class="line"><span class="keyword">import</span> dis</span><br><span class="line"><span class="keyword">import</span> timeit</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">analyze_performance</span>(<span class="params">func</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;分析函数的字节码和执行时间。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;\n=== 分析函数: <span class="subst">&#123;func.__name__&#125;</span> ===&quot;</span>)</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 查看字节码</span></span><br><span class="line">    dis.dis(func)</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 统计指令数量</span></span><br><span class="line">    bytecode = dis.Bytecode(func)</span><br><span class="line">    instructions = <span class="built_in">list</span>(bytecode)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;\n指令总数: <span class="subst">&#123;<span class="built_in">len</span>(instructions)&#125;</span>&quot;</span>)</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 分析指令类型分布</span></span><br><span class="line">    op_counts = &#123;&#125;</span><br><span class="line">    <span class="keyword">for</span> instr <span class="keyword">in</span> instructions:</span><br><span class="line">        op_name = instr.opname</span><br><span class="line">        op_counts[op_name] = op_counts.get(op_name, <span class="number">0</span>) + <span class="number">1</span></span><br><span class="line">    </span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;指令类型分布:&quot;</span>, op_counts)</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 性能测试</span></span><br><span class="line">    time_taken = timeit.timeit(func, number=<span class="number">100000</span>)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;执行 100000 次耗时: <span class="subst">&#123;time_taken:<span class="number">.4</span>f&#125;</span>s&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 示例：优化前的函数</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">slow_function</span>():</span><br><span class="line">    result = []</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">100</span>):</span><br><span class="line">        result.append(i * <span class="number">2</span>)</span><br><span class="line">    <span class="keyword">return</span> result</span><br><span class="line"></span><br><span class="line"><span class="comment"># 优化后的函数</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">fast_function</span>():</span><br><span class="line">    <span class="keyword">return</span> [i * <span class="number">2</span> <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">100</span>)]</span><br><span class="line"></span><br><span class="line">analyze_performance(slow_function)</span><br><span class="line">analyze_performance(fast_function)</span><br></pre></td></tr></table></figure><hr><h2 id="8-常见面试题汇总"><a href="#8-常见面试题汇总" class="headerlink" title="8. 常见面试题汇总"></a>8. 常见面试题汇总</h2><h3 id="8-1-GIL-相关"><a href="#8-1-GIL-相关" class="headerlink" title="8.1 GIL 相关"></a>8.1 GIL 相关</h3><p><strong>Q1：什么是 GIL？它对 Python 多线程有什么影响？</strong></p><blockquote><p><strong>A</strong>：GIL（Global Interpreter Lock）是 CPython 的全局解释器锁，确保同一时刻只有一个线程执行 Python 字节码。影响：</p><ul><li>CPU 密集型任务：多线程无法利用多核，反而可能更慢（锁切换开销）</li><li>I&#x2F;O 密集型任务：影响较小，I&#x2F;O 操作时线程会释放 GIL</li></ul></blockquote><p><strong>Q2：如何绕过 GIL 实现真正的并行？</strong></p><blockquote><p><strong>A</strong>：</p><ol><li><code>multiprocessing</code>：多进程，每个进程有独立的解释器和 GIL</li><li><code>concurrent.futures.ProcessPoolExecutor</code>：进程池</li><li>C&#x2F;C++ 扩展：在 C 代码中释放 GIL</li><li>asyncio：单线程协程，不依赖多线程</li></ol></blockquote><p><strong>Q3：Python 3.12 的无 GIL 实验有什么意义？</strong></p><blockquote><p><strong>A</strong>：无 GIL（<code>--disable-gil</code>）是 Python 社区期待已久的功能，意义：</p><ul><li>多线程 CPU 密集型任务可直接利用多核</li><li>减少多进程的内存开销和进程间通信成本</li><li>使 Python 在高性能计算领域更具竞争力</li></ul></blockquote><h3 id="8-2-内存管理"><a href="#8-2-内存管理" class="headerlink" title="8.2 内存管理"></a>8.2 内存管理</h3><p><strong>Q4：Python 的内存管理机制是什么？</strong></p><blockquote><p><strong>A</strong>：Python 采用<strong>引用计数</strong>为主、<strong>分代垃圾回收</strong>为辅的机制：</p><ol><li>引用计数：每个对象维护 <code>ob_refcnt</code>，计数为 0 时立即释放</li><li>分代垃圾回收：处理循环引用问题，将对象分为 0&#x2F;1&#x2F;2 代</li></ol></blockquote><p><strong>Q5：循环引用为什么会导致内存泄漏？如何解决？</strong></p><blockquote><p><strong>A</strong>：循环引用中，对象相互引用，引用计数永远不会降到 0。解决方案：</p><ol><li>使用 <code>weakref</code> 创建弱引用（不增加引用计数）</li><li>避免在 <code>__del__</code> 方法中创建循环引用</li><li>手动调用 <code>gc.collect()</code> 强制回收</li></ol></blockquote><p><strong>Q6：<code>__slots__</code> 的作用是什么？什么时候使用？</strong></p><blockquote><p><strong>A</strong>：<code>__slots__</code> 指定类的属性列表，不创建 <code>__dict__</code>：</p><ul><li>节省内存（每个实例约节省 40-60%）</li><li>禁止动态添加属性</li><li>使用场景：创建大量实例的类（如数据点、配置对象）</li></ul></blockquote><h3 id="8-3-对象模型"><a href="#8-3-对象模型" class="headerlink" title="8.3 对象模型"></a>8.3 对象模型</h3><p><strong>Q7：Python 中 <code>is</code> 和 <code>==</code> 的区别是什么？</strong></p><blockquote><p><strong>A</strong>：</p><ul><li><code>is</code>：比较对象身份（内存地址），检查是否为同一对象</li><li><code>==</code>：比较对象值，调用 <code>__eq__</code> 方法</li></ul></blockquote><p><strong>Q8：为什么小整数和短字符串会被缓存？</strong></p><blockquote><p><strong>A</strong>：这是 CPython 的 <strong>interning</strong>（驻留）机制：</p><ul><li>小整数（-5 ~ 256）和短字符串在编译期缓存</li><li>避免重复创建相同值的对象</li><li>提高内存效率和访问速度</li></ul></blockquote><p><strong>Q9：什么是 PyObject？它的结构是什么？</strong></p><blockquote><p><strong>A</strong>：PyObject 是 CPython 中所有对象的基类，包含：</p><ul><li><code>ob_refcnt</code>：引用计数</li><li><code>ob_type</code>：类型指针</li><li><code>_PyObject_HEAD_EXTRA</code>：双向链表指针（调试&#x2F;GC 用）</li></ul></blockquote><h3 id="8-4-C-扩展"><a href="#8-4-C-扩展" class="headerlink" title="8.4 C 扩展"></a>8.4 C 扩展</h3><p><strong>Q10：Python 有哪些扩展方式？如何选择？</strong></p><blockquote><p><strong>A</strong>：</p><ul><li><code>ctypes</code>：无需编译，调用现有 DLL&#x2F;SO，适合快速集成</li><li><code>Cython</code>：Python 语法编译为 C，适合算法加速</li><li><code>C API</code>：编写原生扩展模块，适合深度集成</li><li><code>pybind11</code>：现代 C++ 绑定，适合 C++ 库集成</li><li><code>Numba</code>：JIT 编译，适合数值计算</li></ul></blockquote><p><strong>Q11：什么时候需要用 C 扩展？</strong></p><blockquote><p><strong>A</strong>：</p><ol><li>Python 代码性能无法满足要求</li><li>需要调用现有的 C&#x2F;C++ 库</li><li>需要访问操作系统底层接口</li><li>需要绕过 GIL 实现真正的并行</li></ol></blockquote><hr><h2 id="9-实战项目：内存分析与优化工具"><a href="#9-实战项目：内存分析与优化工具" class="headerlink" title="9. 实战项目：内存分析与优化工具"></a>9. 实战项目：内存分析与优化工具</h2><h3 id="9-1-项目目标"><a href="#9-1-项目目标" class="headerlink" title="9.1 项目目标"></a>9.1 项目目标</h3><p>开发一个轻量级的 Python 内存分析工具，能够：</p><ol><li>监控对象创建和销毁</li><li>检测内存泄漏</li><li>提供优化建议</li></ol><h3 id="9-2-项目结构"><a href="#9-2-项目结构" class="headerlink" title="9.2 项目结构"></a>9.2 项目结构</h3><figure class="highlight plaintext"><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><span class="line">memory_analyzer/</span><br><span class="line">├── __init__.py</span><br><span class="line">├── tracker.py        # 对象追踪模块</span><br><span class="line">├── leak_detector.py  # 内存泄漏检测</span><br><span class="line">├── profiler.py       # 性能分析器</span><br><span class="line">└── cli.py            # 命令行接口</span><br></pre></td></tr></table></figure><h3 id="9-3-核心代码"><a href="#9-3-核心代码" class="headerlink" title="9.3 核心代码"></a>9.3 核心代码</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><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># tracker.py - 对象追踪模块</span></span><br><span class="line"><span class="keyword">import</span> gc</span><br><span class="line"><span class="keyword">import</span> weakref</span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"><span class="keyword">from</span> collections <span class="keyword">import</span> defaultdict</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">ObjectTracker</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="variable language_">self</span>._object_counts = defaultdict(<span class="built_in">int</span>)</span><br><span class="line">        <span class="variable language_">self</span>._object_sizes = defaultdict(<span class="built_in">int</span>)</span><br><span class="line">        <span class="variable language_">self</span>._creation_times = &#123;&#125;</span><br><span class="line">        <span class="variable language_">self</span>._enabled = <span class="literal">False</span></span><br><span class="line">        <span class="variable language_">self</span>._original_gc_callback = <span class="literal">None</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">_gc_callback</span>(<span class="params">self, phase, info</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;GC 事件回调。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">if</span> phase == <span class="string">&quot;start&quot;</span>:</span><br><span class="line">            <span class="keyword">pass</span></span><br><span class="line">        <span class="keyword">elif</span> phase == <span class="string">&quot;stop&quot;</span>:</span><br><span class="line">            <span class="variable language_">self</span>._update_counts()</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">_update_counts</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;更新对象计数和大小。&quot;&quot;&quot;</span></span><br><span class="line">        objects = gc.get_objects()</span><br><span class="line">        <span class="variable language_">self</span>._object_counts.clear()</span><br><span class="line">        <span class="variable language_">self</span>._object_sizes.clear()</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">for</span> obj <span class="keyword">in</span> objects:</span><br><span class="line">            obj_type = <span class="built_in">type</span>(obj).__name__</span><br><span class="line">            <span class="variable language_">self</span>._object_counts[obj_type] += <span class="number">1</span></span><br><span class="line">            <span class="keyword">try</span>:</span><br><span class="line">                <span class="variable language_">self</span>._object_sizes[obj_type] += <span class="built_in">len</span>(obj) <span class="keyword">if</span> <span class="built_in">hasattr</span>(obj, <span class="string">&#x27;__len__&#x27;</span>) <span class="keyword">else</span> <span class="number">0</span></span><br><span class="line">            <span class="keyword">except</span>:</span><br><span class="line">                <span class="keyword">pass</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">start</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;开始追踪。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="variable language_">self</span>._enabled = <span class="literal">True</span></span><br><span class="line">        gc.callbacks.append(<span class="variable language_">self</span>._gc_callback)</span><br><span class="line">        <span class="variable language_">self</span>._update_counts()</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">stop</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;停止追踪。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="variable language_">self</span>._enabled = <span class="literal">False</span></span><br><span class="line">        <span class="keyword">if</span> <span class="variable language_">self</span>._gc_callback <span class="keyword">in</span> gc.callbacks:</span><br><span class="line">            gc.callbacks.remove(<span class="variable language_">self</span>._gc_callback)</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_report</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;生成分析报告。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">return</span> &#123;</span><br><span class="line">            <span class="string">&quot;object_counts&quot;</span>: <span class="built_in">dict</span>(<span class="variable language_">self</span>._object_counts),</span><br><span class="line">            <span class="string">&quot;object_sizes&quot;</span>: <span class="built_in">dict</span>(<span class="variable language_">self</span>._object_sizes),</span><br><span class="line">            <span class="string">&quot;total_objects&quot;</span>: <span class="built_in">sum</span>(<span class="variable language_">self</span>._object_counts.values()),</span><br><span class="line">            <span class="string">&quot;gc_stats&quot;</span>: gc.get_stats()</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># leak_detector.py - 内存泄漏检测</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">LeakDetector</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="variable language_">self</span>._snapshots = []</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">take_snapshot</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;拍摄对象快照。&quot;&quot;&quot;</span></span><br><span class="line">        snapshot = &#123;</span><br><span class="line">            <span class="string">&quot;timestamp&quot;</span>: time.time(),</span><br><span class="line">            <span class="string">&quot;objects&quot;</span>: <span class="built_in">set</span>(<span class="built_in">id</span>(obj) <span class="keyword">for</span> obj <span class="keyword">in</span> gc.get_objects())</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="variable language_">self</span>._snapshots.append(snapshot)</span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 保留最近 10 个快照</span></span><br><span class="line">        <span class="keyword">if</span> <span class="built_in">len</span>(<span class="variable language_">self</span>._snapshots) &gt; <span class="number">10</span>:</span><br><span class="line">            <span class="variable language_">self</span>._snapshots.pop(<span class="number">0</span>)</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">detect_leaks</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;检测潜在泄漏。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">if</span> <span class="built_in">len</span>(<span class="variable language_">self</span>._snapshots) &lt; <span class="number">2</span>:</span><br><span class="line">            <span class="keyword">return</span> []</span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 找出持续存在的对象</span></span><br><span class="line">        persistent_objects = <span class="built_in">set</span>.intersection(</span><br><span class="line">            *[s[<span class="string">&quot;objects&quot;</span>] <span class="keyword">for</span> s <span class="keyword">in</span> <span class="variable language_">self</span>._snapshots]</span><br><span class="line">        )</span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 分析这些对象</span></span><br><span class="line">        leaks = []</span><br><span class="line">        <span class="keyword">for</span> obj_id <span class="keyword">in</span> persistent_objects:</span><br><span class="line">            <span class="keyword">try</span>:</span><br><span class="line">                obj = gc.get_objects()[<span class="built_in">list</span>(<span class="built_in">map</span>(<span class="built_in">id</span>, gc.get_objects())).index(obj_id)]</span><br><span class="line">                leaks.append(&#123;</span><br><span class="line">                    <span class="string">&quot;type&quot;</span>: <span class="built_in">type</span>(obj).__name__,</span><br><span class="line">                    <span class="string">&quot;id&quot;</span>: obj_id,</span><br><span class="line">                    <span class="string">&quot;repr&quot;</span>: <span class="built_in">str</span>(obj)[:<span class="number">100</span>]</span><br><span class="line">                &#125;)</span><br><span class="line">            <span class="keyword">except</span> (ValueError, IndexError):</span><br><span class="line">                <span class="keyword">pass</span></span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> leaks[:<span class="number">20</span>]  <span class="comment"># 返回前 20 个疑似泄漏</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># profiler.py - 性能分析器</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">MemoryProfiler</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="variable language_">self</span>._tracker = ObjectTracker()</span><br><span class="line">        <span class="variable language_">self</span>._detector = LeakDetector()</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">profile</span>(<span class="params">self, func, *args, **kwargs</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;分析函数执行过程中的内存变化。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="variable language_">self</span>._tracker.start()</span><br><span class="line">        <span class="variable language_">self</span>._detector.take_snapshot()</span><br><span class="line">        </span><br><span class="line">        start_memory = <span class="variable language_">self</span>._tracker.get_report()</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">try</span>:</span><br><span class="line">            result = func(*args, **kwargs)</span><br><span class="line">        <span class="keyword">finally</span>:</span><br><span class="line">            <span class="variable language_">self</span>._detector.take_snapshot()</span><br><span class="line">            end_memory = <span class="variable language_">self</span>._tracker.get_report()</span><br><span class="line">            <span class="variable language_">self</span>._tracker.stop()</span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 计算变化</span></span><br><span class="line">        changes = &#123;&#125;</span><br><span class="line">        <span class="keyword">for</span> obj_type <span class="keyword">in</span> <span class="built_in">set</span>(start_memory[<span class="string">&quot;object_counts&quot;</span>].keys()) | <span class="built_in">set</span>(end_memory[<span class="string">&quot;object_counts&quot;</span>].keys()):</span><br><span class="line">            start_count = start_memory[<span class="string">&quot;object_counts&quot;</span>].get(obj_type, <span class="number">0</span>)</span><br><span class="line">            end_count = end_memory[<span class="string">&quot;object_counts&quot;</span>].get(obj_type, <span class="number">0</span>)</span><br><span class="line">            <span class="keyword">if</span> end_count != start_count:</span><br><span class="line">                changes[obj_type] = end_count - start_count</span><br><span class="line">        </span><br><span class="line">        leaks = <span class="variable language_">self</span>._detector.detect_leaks()</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> &#123;</span><br><span class="line">            <span class="string">&quot;result&quot;</span>: result,</span><br><span class="line">            <span class="string">&quot;memory_changes&quot;</span>: changes,</span><br><span class="line">            <span class="string">&quot;potential_leaks&quot;</span>: leaks,</span><br><span class="line">            <span class="string">&quot;start_memory&quot;</span>: start_memory,</span><br><span class="line">            <span class="string">&quot;end_memory&quot;</span>: end_memory</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># cli.py - 命令行接口</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">main</span>():</span><br><span class="line">    <span class="keyword">import</span> argparse</span><br><span class="line">    parser = argparse.ArgumentParser(description=<span class="string">&quot;Python 内存分析工具&quot;</span>)</span><br><span class="line">    parser.add_argument(<span class="string">&quot;--profile&quot;</span>, <span class="built_in">help</span>=<span class="string">&quot;要分析的 Python 文件&quot;</span>)</span><br><span class="line">    parser.add_argument(<span class="string">&quot;--detect-leaks&quot;</span>, action=<span class="string">&quot;store_true&quot;</span>, <span class="built_in">help</span>=<span class="string">&quot;检测内存泄漏&quot;</span>)</span><br><span class="line">    </span><br><span class="line">    args = parser.parse_args()</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">if</span> args.profile:</span><br><span class="line">        profiler = MemoryProfiler()</span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 读取并执行文件</span></span><br><span class="line">        <span class="keyword">with</span> <span class="built_in">open</span>(args.profile, <span class="string">&#x27;r&#x27;</span>) <span class="keyword">as</span> f:</span><br><span class="line">            code = f.read()</span><br><span class="line">        </span><br><span class="line">        namespace = &#123;&#125;</span><br><span class="line">        result = profiler.profile(<span class="built_in">exec</span>, code, namespace)</span><br><span class="line">        </span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;=== 内存分析报告 ===&quot;</span>)</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;内存变化:&quot;</span>, result[<span class="string">&quot;memory_changes&quot;</span>])</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;\n疑似泄漏:&quot;</span>, result[<span class="string">&quot;potential_leaks&quot;</span>])</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">elif</span> args.detect_leaks:</span><br><span class="line">        detector = LeakDetector()</span><br><span class="line">        detector.take_snapshot()</span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 运行一些代码</span></span><br><span class="line">        <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">1000</span>):</span><br><span class="line">            _ = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]</span><br><span class="line">        </span><br><span class="line">        detector.take_snapshot()</span><br><span class="line">        leaks = detector.detect_leaks()</span><br><span class="line">        </span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;检测到的疑似泄漏:&quot;</span>, leaks)</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    main()</span><br></pre></td></tr></table></figure><h3 id="9-4-使用示例"><a href="#9-4-使用示例" class="headerlink" title="9.4 使用示例"></a>9.4 使用示例</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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 分析 Python 文件</span></span><br><span class="line">python -m memory_analyzer.cli --profile my_script.py</span><br><span class="line"></span><br><span class="line"><span class="comment"># 检测内存泄漏</span></span><br><span class="line">python -m memory_analyzer.cli --detect-leaks</span><br></pre></td></tr></table></figure><h3 id="9-5-项目扩展"><a href="#9-5-项目扩展" class="headerlink" title="9.5 项目扩展"></a>9.5 项目扩展</h3><ol><li><strong>可视化报告</strong>：生成 HTML 报告，展示对象分布和内存变化趋势</li><li><strong>实时监控</strong>：使用 <code>watchdog</code> 监控文件变化，自动分析</li><li><strong>集成测试</strong>：在 CI&#x2F;CD 流程中自动检测内存泄漏</li><li><strong>优化建议</strong>：根据分析结果提供针对性的优化建议</li></ol><hr><h2 id="附录：第十一阶段自检清单"><a href="#附录：第十一阶段自检清单" class="headerlink" title="附录：第十一阶段自检清单"></a>附录：第十一阶段自检清单</h2><ul><li><input disabled="" type="checkbox"> 解释 Python 代码从源码到执行的完整流程</li><li><input disabled="" type="checkbox"> 能阅读简单的 Python 字节码（<code>dis.dis</code>）</li><li><input disabled="" type="checkbox"> 解释 GIL 的作用、影响和绕过方法</li><li><input disabled="" type="checkbox"> 解释引用计数的工作原理和循环引用问题</li><li><input disabled="" type="checkbox"> 了解分代垃圾回收的机制</li><li><input disabled="" type="checkbox"> 理解 <code>is</code> 和 <code>==</code> 在不可变对象上的区别</li><li><input disabled="" type="checkbox"> 使用 <code>__slots__</code> 优化内存</li><li><input disabled="" type="checkbox"> 使用 ctypes 或 Cython 调用 C 代码</li><li><input disabled="" type="checkbox"> 了解 pybind11 的基本用法</li><li><input disabled="" type="checkbox"> 了解 GIL 移除的最新进展</li><li><input disabled="" type="checkbox"> 使用内存分析工具检测泄漏</li><li><input disabled="" type="checkbox"> 根据字节码分析优化函数性能</li></ul><hr><blockquote><p><strong>工程师寄语</strong>：理解底层不是为了炫技，而是为了在遇到性能瓶颈、内存泄漏、诡异 Bug 时有足够的工具去分析和解决。大多数场景不需要写 C 扩展，但知道”可以做”和”怎么做”是高级工程师的标志。</p></blockquote><blockquote><p><strong>学习建议</strong>：</p><ol><li>先用 <code>dis</code> 模块理解字节码执行过程</li><li>通过 <code>sys.getrefcount</code> 观察引用计数变化</li><li>在实际项目中遇到性能问题时再考虑 C 扩展</li><li>关注 Python 官方关于 GIL 移除的进展</li><li>尝试用 <code>ctypes</code> 或 <code>pybind11</code> 调用一个简单的 C&#x2F;C++ 函数</li></ol></blockquote>]]>
    </content>
    <id>https://itdxb.cn/2026/07/24/python/document/%E3%80%90Python%E3%80%91%E5%86%85%E9%83%A8%E5%8E%9F%E7%90%86%E4%B8%8E%E6%89%A9%E5%B1%95%EF%BC%88%E7%AC%AC%E5%8D%81%E4%B8%80%E9%98%B6%E6%AE%B5%EF%BC%89/</id>
    <link href="https://itdxb.cn/2026/07/24/python/document/%E3%80%90Python%E3%80%91%E5%86%85%E9%83%A8%E5%8E%9F%E7%90%86%E4%B8%8E%E6%89%A9%E5%B1%95%EF%BC%88%E7%AC%AC%E5%8D%81%E4%B8%80%E9%98%B6%E6%AE%B5%EF%BC%89/"/>
    <published>2026-07-24T03:00:00.000Z</published>
    <summary>深入 Python 内部机制，理解 CPython 解释器、GIL、内存管理、字节码等核心原理，以及如何用 C/C++ 扩展 Python。</summary>
    <title>【Python】内部原理与扩展（第十一阶段）</title>
    <updated>2026-07-24T05:51:07.191Z</updated>
  </entry>
  <entry>
    <author>
      <name>Super Bing</name>
    </author>
    <category term="Python" scheme="https://itdxb.cn/categories/Python/"/>
    <category term="教程" scheme="https://itdxb.cn/tags/%E6%95%99%E7%A8%8B/"/>
    <category term="Python" scheme="https://itdxb.cn/tags/Python/"/>
    <category term="应用领域" scheme="https://itdxb.cn/tags/%E5%BA%94%E7%94%A8%E9%A2%86%E5%9F%9F/"/>
    <category term="Web开发" scheme="https://itdxb.cn/tags/Web%E5%BC%80%E5%8F%91/"/>
    <category term="数据科学" scheme="https://itdxb.cn/tags/%E6%95%B0%E6%8D%AE%E7%A7%91%E5%AD%A6/"/>
    <content>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><h1 id="十、主流应用领域"><a href="#十、主流应用领域" class="headerlink" title="十、主流应用领域"></a>十、主流应用领域</h1><blockquote><p><strong>阶段定位</strong>：Python 的应用生态极其丰富。本阶段不是深入讲解每个领域的细节，而是建立”领域地图”——了解各方向的核心技术栈、常用工具和入门路径，帮助你根据兴趣选择深入方向。</p></blockquote><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">┌──────────────────────────────────────────────────────────────────┐</span><br><span class="line">│                    Python 应用领域全景图                         │</span><br><span class="line">├──────────────────────────────────────────────────────────────────┤</span><br><span class="line">│                                                                  │</span><br><span class="line">│  🌐 Web 后端开发                                                 │</span><br><span class="line">│     ├── FastAPI（现代异步，高性能）                              │</span><br><span class="line">│     ├── Django（全功能，batteries included）                    │</span><br><span class="line">│     ├── Flask（微框架，灵活轻量）                                │</span><br><span class="line">│     └── ORM：SQLAlchemy / Django ORM                           │</span><br><span class="line">│                                                                  │</span><br><span class="line">│  📊 数据科学与分析                                               │</span><br><span class="line">│     ├── NumPy（数值计算）                                        │</span><br><span class="line">│     ├── Pandas（数据处理）                                       │</span><br><span class="line">│     ├── Matplotlib / Seaborn（可视化）                          │</span><br><span class="line">│     ├── Scikit-learn（机器学习）                                 │</span><br><span class="line">│     └── PySpark（大数据）                                        │</span><br><span class="line">│                                                                  │</span><br><span class="line">│  🕷️ 网络爬虫                                                     │</span><br><span class="line">│     ├── requests（HTTP 请求）                                    │</span><br><span class="line">│     ├── BeautifulSoup（HTML 解析）                              │</span><br><span class="line">│     ├── Scrapy（爬虫框架）                                       │</span><br><span class="line">│     └── Selenium / Playwright（动态页面）                       │</span><br><span class="line">│                                                                  │</span><br><span class="line">│  🤖 人工智能与机器学习                                           │</span><br><span class="line">│     ├── Scikit-learn（传统 ML）                                 │</span><br><span class="line">│     ├── PyTorch / TensorFlow（深度学习）                        │</span><br><span class="line">│     ├── LangChain（LLM 应用）                                   │</span><br><span class="line">│     └── Transformers（预训练模型）                               │</span><br><span class="line">│                                                                  │</span><br><span class="line">│  🚀 自动化运维与 DevOps                                          │</span><br><span class="line">│     ├── subprocess（系统命令）                                   │</span><br><span class="line">│     ├── Fabric（远程执行）                                       │</span><br><span class="line">│     ├── Docker / K8s（容器编排）                                │</span><br><span class="line">│     └── boto3 / kubernetes（云原生）                            │</span><br><span class="line">│                                                                  │</span><br><span class="line">│  💻 桌面应用与 GUI                                               │</span><br><span class="line">│     ├── Tkinter（标准库）                                        │</span><br><span class="line">│     ├── PyQt / PySide（专业应用）                                │</span><br><span class="line">│     ├── Kivy（跨平台移动）                                       │</span><br><span class="line">│     └── Flet（类 Flutter）                                       │</span><br><span class="line">│                                                                  │</span><br><span class="line">│  🎮 游戏开发                                                     │</span><br><span class="line">│     ├── Pygame（2D 学习）                                        │</span><br><span class="line">│     ├── Arcade（现代 2D）                                        │</span><br><span class="line">│     ├── Panda3D（3D）                                           │</span><br><span class="line">│     └── Godot + Python（游戏引擎）                               │</span><br><span class="line">│                                                                  │</span><br><span class="line">│  🔌 物联网与嵌入式                                               │</span><br><span class="line">│     ├── MicroPython（ESP8266/ESP32）                            │</span><br><span class="line">│     ├── CircuitPython（Adafruit）                               │</span><br><span class="line">│     └── Raspberry Pi（树莓派）                                   │</span><br><span class="line">│                                                                  │</span><br><span class="line">└──────────────────────────────────────────────────────────────────┘</span><br></pre></td></tr></table></figure><hr><h2 id="1-Web-后端开发"><a href="#1-Web-后端开发" class="headerlink" title="1. Web 后端开发"></a>1. Web 后端开发</h2><blockquote><p>Web 后端开发是 Python 应用最广泛、岗位需求最大的方向。本节不仅列出技术栈，更带你<strong>从零跑通第一个项目</strong>。</p></blockquote><h3 id="1-1-主流框架对比"><a href="#1-1-主流框架对比" class="headerlink" title="1.1 主流框架对比"></a>1.1 主流框架对比</h3><table><thead><tr><th>框架</th><th>定位</th><th>异步支持</th><th>内置 ORM</th><th>内置 Admin</th><th>自动文档</th><th>学习曲线</th><th>性能</th><th>适用场景</th></tr></thead><tbody><tr><td><strong>Django</strong></td><td>全功能</td><td>3.0+ 支持</td><td>✅ Django ORM</td><td>✅</td><td>❌</td><td>中等</td><td>中</td><td>快速开发、CMS、电商、内容平台</td></tr><tr><td><strong>Flask</strong></td><td>微框架</td><td>❌（需扩展）</td><td>❌</td><td>❌</td><td>❌</td><td>低</td><td>中</td><td>中小型项目、API 原型、微服务</td></tr><tr><td><strong>FastAPI</strong></td><td>现代异步</td><td>✅ 原生</td><td>❌（配 SQLAlchemy）</td><td>❌</td><td>✅ Swagger&#x2F;ReDoc</td><td>低</td><td><strong>高</strong></td><td>高性能 API、微服务、实时应用</td></tr><tr><td><strong>Tornado</strong></td><td>异步网络库</td><td>✅ 原生</td><td>❌</td><td>❌</td><td>❌</td><td>中高</td><td>高</td><td>长连接、WebSocket、聊天室</td></tr><tr><td><strong>Sanic</strong></td><td>异步框架</td><td>✅ 原生</td><td>❌</td><td>❌</td><td>❌</td><td>低</td><td>高</td><td>高并发 API</td></tr></tbody></table><p><strong>框架选择决策树</strong>：</p><figure class="highlight plaintext"><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><span class="line">需要快速开发全功能网站？</span><br><span class="line">    │</span><br><span class="line">    ├── Yes → Django（内置 ORM、Admin、认证，开箱即用）</span><br><span class="line">    │</span><br><span class="line">    └── No → 需要高性能异步 API？</span><br><span class="line">                │</span><br><span class="line">                ├── Yes → FastAPI（现代异步，自动生成文档，类型安全）</span><br><span class="line">                │</span><br><span class="line">                └── No → 需要极简和灵活？</span><br><span class="line">                            │</span><br><span class="line">                            ├── Yes → Flask（轻量，扩展丰富，适合学习和小项目）</span><br><span class="line">                            │</span><br><span class="line">                            └── No → 需要长连接/WebSocket？</span><br><span class="line">                                        │</span><br><span class="line">                                        └── Yes → Tornado</span><br></pre></td></tr></table></figure><p><strong>新手建议</strong>：先学 <strong>FastAPI</strong>（类型提示 + 自动文档，调试最方便），再学 <strong>Django</strong>（理解全功能框架的设计哲学），Flask 可作为兴趣了解。</p><hr><h3 id="1-2-从零开始：FastAPI-完整项目"><a href="#1-2-从零开始：FastAPI-完整项目" class="headerlink" title="1.2 从零开始：FastAPI 完整项目"></a>1.2 从零开始：FastAPI 完整项目</h3><blockquote><p>目标：搭建一个带<strong>数据库持久化 + JWT 认证</strong>的完整 API 项目，跑通后你就有了一个可以继续扩展的后端骨架。</p></blockquote><h4 id="第一步：环境搭建"><a href="#第一步：环境搭建" class="headerlink" title="第一步：环境搭建"></a>第一步：环境搭建</h4><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><span class="line"><span class="comment"># 1. 创建项目目录</span></span><br><span class="line"><span class="built_in">mkdir</span> fastapi-demo &amp;&amp; <span class="built_in">cd</span> fastapi-demo</span><br><span class="line"></span><br><span class="line"><span class="comment"># 2. 创建虚拟环境（强烈建议每个项目都用虚拟环境，避免依赖冲突）</span></span><br><span class="line">python -m venv .venv</span><br><span class="line"></span><br><span class="line"><span class="comment"># 3. 激活虚拟环境</span></span><br><span class="line"><span class="comment"># Windows：</span></span><br><span class="line">.venv\Scripts\activate</span><br><span class="line"><span class="comment"># macOS / Linux：</span></span><br><span class="line"><span class="built_in">source</span> .venv/bin/activate</span><br><span class="line"></span><br><span class="line"><span class="comment"># 4. 安装依赖</span></span><br><span class="line">pip install fastapi uvicorn[standard] sqlalchemy alembic pyjwt passlib[bcrypt] python-multipart</span><br><span class="line"></span><br><span class="line"><span class="comment"># 5. （可选）把依赖固化到 requirements.txt</span></span><br><span class="line">pip freeze &gt; requirements.txt</span><br></pre></td></tr></table></figure><p><strong>依赖说明</strong>：</p><table><thead><tr><th>包名</th><th>作用</th></tr></thead><tbody><tr><td><code>fastapi</code></td><td>Web 框架</td></tr><tr><td><code>uvicorn[standard]</code></td><td>ASGI 服务器，<code>[standard]</code> 包含 uvloop 等性能优化</td></tr><tr><td><code>sqlalchemy</code></td><td>ORM，用 Python 对象操作数据库</td></tr><tr><td><code>alembic</code></td><td>数据库迁移工具（类似 Django Migrations）</td></tr><tr><td><code>pyjwt</code></td><td>JWT 令牌生成与验证</td></tr><tr><td><code>passlib[bcrypt]</code></td><td>密码哈希（bcrypt 算法）</td></tr><tr><td><code>python-multipart</code></td><td>处理表单上传（FastAPI 登录表单需要）</td></tr></tbody></table><h4 id="第二步：项目结构"><a href="#第二步：项目结构" class="headerlink" title="第二步：项目结构"></a>第二步：项目结构</h4><figure class="highlight plaintext"><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><span class="line">fastapi-demo/</span><br><span class="line">├── .venv/                  # 虚拟环境（不提交到 Git）</span><br><span class="line">├── app/</span><br><span class="line">│   ├── __init__.py</span><br><span class="line">│   ├── main.py             # 入口：创建 FastAPI 实例、注册路由和中间件</span><br><span class="line">│   ├── config.py           # 配置：数据库 URL、密钥等</span><br><span class="line">│   ├── database.py         # 数据库引擎和会话</span><br><span class="line">│   ├── models.py           # SQLAlchemy ORM 模型（表结构）</span><br><span class="line">│   ├── schemas.py          # Pydantic 模型（请求/响应的数据验证）</span><br><span class="line">│   ├── auth.py             # JWT 认证逻辑</span><br><span class="line">│   └── routers/</span><br><span class="line">│       ├── __init__.py</span><br><span class="line">│       ├── users.py        # 用户相关路由</span><br><span class="line">│       └── items.py        # 示例业务路由（商品/待办等）</span><br><span class="line">├── requirements.txt</span><br><span class="line">└── README.md</span><br></pre></td></tr></table></figure><h4 id="第三步：完整代码"><a href="#第三步：完整代码" class="headerlink" title="第三步：完整代码"></a>第三步：完整代码</h4><p><strong><code>app/config.py</code></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></pre></td><td class="code"><pre><span class="line"><span class="string">&quot;&quot;&quot;项目配置：集中管理所有配置项，方便统一修改。&quot;&quot;&quot;</span></span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"><span class="keyword">from</span> pathlib <span class="keyword">import</span> Path</span><br><span class="line"></span><br><span class="line"><span class="comment"># 项目根目录</span></span><br><span class="line">BASE_DIR = Path(__file__).resolve().parent.parent</span><br><span class="line"></span><br><span class="line"><span class="comment"># 数据库：使用 SQLite（零配置，适合学习；生产环境换 PostgreSQL）</span></span><br><span class="line">SQLALCHEMY_DATABASE_URL = <span class="string">f&quot;sqlite:///<span class="subst">&#123;BASE_DIR / <span class="string">&#x27;app.db&#x27;</span>&#125;</span>&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># JWT 密钥：生产环境务必从环境变量读取，不要硬编码！</span></span><br><span class="line"><span class="comment"># 生成方法：python -c &quot;import secrets; print(secrets.token_urlsafe(32))&quot;</span></span><br><span class="line">SECRET_KEY = os.getenv(<span class="string">&quot;SECRET_KEY&quot;</span>, <span class="string">&quot;dev-only-secret-key-do-not-use-in-production&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># JWT 令牌过期时间（分钟）</span></span><br><span class="line">ACCESS_TOKEN_EXPIRE_MINUTES = <span class="number">60</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 算法</span></span><br><span class="line">ALGORITHM = <span class="string">&quot;HS256&quot;</span></span><br></pre></td></tr></table></figure><p><strong><code>app/database.py</code></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><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><span class="line"><span class="string">&quot;&quot;&quot;数据库引擎和会话管理。&quot;&quot;&quot;</span></span><br><span class="line"><span class="keyword">from</span> sqlalchemy <span class="keyword">import</span> create_engine</span><br><span class="line"><span class="keyword">from</span> sqlalchemy.orm <span class="keyword">import</span> sessionmaker, declarative_base</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> app.config <span class="keyword">import</span> SQLALCHEMY_DATABASE_URL</span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建数据库引擎</span></span><br><span class="line"><span class="comment"># SQLite 需要 check_same_thread=False，因为 FastAPI 的异步请求可能在不同线程</span></span><br><span class="line">engine = create_engine(</span><br><span class="line">    SQLALCHEMY_DATABASE_URL,</span><br><span class="line">    connect_args=&#123;<span class="string">&quot;check_same_thread&quot;</span>: <span class="literal">False</span>&#125;,  <span class="comment"># 仅 SQLite 需要此参数</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 会话工厂：每次请求创建一个独立会话，请求结束自动关闭</span></span><br><span class="line">SessionLocal = sessionmaker(autocommit=<span class="literal">False</span>, autoflush=<span class="literal">False</span>, bind=engine)</span><br><span class="line"></span><br><span class="line"><span class="comment"># ORM 基类：所有模型类都继承自 Base</span></span><br><span class="line">Base = declarative_base()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">get_db</span>():</span><br><span class="line">    <span class="string">&quot;&quot;&quot;依赖注入：获取数据库会话。FastAPI 会自动在请求结束后调用 finally 关闭会话。&quot;&quot;&quot;</span></span><br><span class="line">    db = SessionLocal()</span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        <span class="keyword">yield</span> db</span><br><span class="line">    <span class="keyword">finally</span>:</span><br><span class="line">        db.close()</span><br></pre></td></tr></table></figure><p><strong><code>app/models.py</code></strong> — ORM 模型（数据库表结构）：</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></pre></td><td class="code"><pre><span class="line"><span class="string">&quot;&quot;&quot;数据库表结构定义（SQLAlchemy ORM 模型）。每个类对应数据库中的一张表。&quot;&quot;&quot;</span></span><br><span class="line"><span class="keyword">from</span> sqlalchemy <span class="keyword">import</span> Column, Integer, String, Boolean, ForeignKey</span><br><span class="line"><span class="keyword">from</span> sqlalchemy.orm <span class="keyword">import</span> relationship</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> app.database <span class="keyword">import</span> Base</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">User</span>(<span class="title class_ inherited__">Base</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;用户表：存储账号信息。&quot;&quot;&quot;</span></span><br><span class="line">    __tablename__ = <span class="string">&quot;users&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="built_in">id</span> = Column(Integer, primary_key=<span class="literal">True</span>, index=<span class="literal">True</span>, autoincrement=<span class="literal">True</span>)</span><br><span class="line">    username = Column(String(<span class="number">50</span>), unique=<span class="literal">True</span>, index=<span class="literal">True</span>, nullable=<span class="literal">False</span>, comment=<span class="string">&quot;用户名，唯一&quot;</span>)</span><br><span class="line">    email = Column(String(<span class="number">120</span>), unique=<span class="literal">True</span>, index=<span class="literal">True</span>, nullable=<span class="literal">False</span>, comment=<span class="string">&quot;邮箱，唯一&quot;</span>)</span><br><span class="line">    hashed_password = Column(String(<span class="number">128</span>), nullable=<span class="literal">False</span>, comment=<span class="string">&quot;密码哈希值，绝不存明文！&quot;</span>)</span><br><span class="line">    is_active = Column(Boolean, default=<span class="literal">True</span>, comment=<span class="string">&quot;是否激活&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 关联：一个用户可以拥有多个 Item</span></span><br><span class="line">    items = relationship(<span class="string">&quot;Item&quot;</span>, back_populates=<span class="string">&quot;owner&quot;</span>, lazy=<span class="string">&quot;selectin&quot;</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Item</span>(<span class="title class_ inherited__">Base</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;业务表：示例&quot;物品/待办&quot;表，演示外键关联。&quot;&quot;&quot;</span></span><br><span class="line">    __tablename__ = <span class="string">&quot;items&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="built_in">id</span> = Column(Integer, primary_key=<span class="literal">True</span>, index=<span class="literal">True</span>, autoincrement=<span class="literal">True</span>)</span><br><span class="line">    title = Column(String(<span class="number">200</span>), nullable=<span class="literal">False</span>, comment=<span class="string">&quot;标题&quot;</span>)</span><br><span class="line">    description = Column(String(<span class="number">500</span>), nullable=<span class="literal">True</span>, comment=<span class="string">&quot;描述&quot;</span>)</span><br><span class="line">    owner_id = Column(Integer, ForeignKey(<span class="string">&quot;users.id&quot;</span>), nullable=<span class="literal">False</span>, comment=<span class="string">&quot;所属用户 ID&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 关联：指向 User</span></span><br><span class="line">    owner = relationship(<span class="string">&quot;User&quot;</span>, back_populates=<span class="string">&quot;items&quot;</span>)</span><br></pre></td></tr></table></figure><p><strong><code>app/schemas.py</code></strong> — Pydantic 模型（请求&#x2F;响应数据验证）：</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></pre></td><td class="code"><pre><span class="line"><span class="string">&quot;&quot;&quot;Pydantic 模型：定义 API 的请求体和响应体格式，FastAPI 会自动做数据校验。&quot;&quot;&quot;</span></span><br><span class="line"><span class="keyword">from</span> pydantic <span class="keyword">import</span> BaseModel, ConfigDict</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># ── 用户相关 ──────────────────────────────────────────</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">UserCreate</span>(<span class="title class_ inherited__">BaseModel</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;创建用户的请求体。&quot;&quot;&quot;</span></span><br><span class="line">    username: <span class="built_in">str</span></span><br><span class="line">    email: <span class="built_in">str</span>  <span class="comment"># 安装 pydantic[email] 后可改为 EmailStr</span></span><br><span class="line">    password: <span class="built_in">str</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">UserUpdate</span>(<span class="title class_ inherited__">BaseModel</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;更新用户的请求体（所有字段可选）。&quot;&quot;&quot;</span></span><br><span class="line">    username: <span class="built_in">str</span> | <span class="literal">None</span> = <span class="literal">None</span></span><br><span class="line">    email: <span class="built_in">str</span> | <span class="literal">None</span> = <span class="literal">None</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">UserOut</span>(<span class="title class_ inherited__">BaseModel</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;用户响应体：绝不返回密码！&quot;&quot;&quot;</span></span><br><span class="line">    model_config = ConfigDict(from_attributes=<span class="literal">True</span>)  <span class="comment"># 允许从 ORM 对象读取</span></span><br><span class="line"></span><br><span class="line">    <span class="built_in">id</span>: <span class="built_in">int</span></span><br><span class="line">    username: <span class="built_in">str</span></span><br><span class="line">    email: <span class="built_in">str</span></span><br><span class="line">    is_active: <span class="built_in">bool</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># ── Token 相关 ──────────────────────────────────────────</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Token</span>(<span class="title class_ inherited__">BaseModel</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;JWT 令牌响应。&quot;&quot;&quot;</span></span><br><span class="line">    access_token: <span class="built_in">str</span></span><br><span class="line">    token_type: <span class="built_in">str</span> = <span class="string">&quot;bearer&quot;</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">TokenData</span>(<span class="title class_ inherited__">BaseModel</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;JWT 令牌中携带的数据。&quot;&quot;&quot;</span></span><br><span class="line">    username: <span class="built_in">str</span> | <span class="literal">None</span> = <span class="literal">None</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># ── Item 相关 ──────────────────────────────────────────</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">ItemCreate</span>(<span class="title class_ inherited__">BaseModel</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;创建 Item 的请求体。&quot;&quot;&quot;</span></span><br><span class="line">    title: <span class="built_in">str</span></span><br><span class="line">    description: <span class="built_in">str</span> | <span class="literal">None</span> = <span class="literal">None</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">ItemOut</span>(<span class="title class_ inherited__">BaseModel</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;Item 响应体。&quot;&quot;&quot;</span></span><br><span class="line">    model_config = ConfigDict(from_attributes=<span class="literal">True</span>)</span><br><span class="line"></span><br><span class="line">    <span class="built_in">id</span>: <span class="built_in">int</span></span><br><span class="line">    title: <span class="built_in">str</span></span><br><span class="line">    description: <span class="built_in">str</span> | <span class="literal">None</span> = <span class="literal">None</span></span><br><span class="line">    owner_id: <span class="built_in">int</span></span><br></pre></td></tr></table></figure><p><strong><code>app/auth.py</code></strong> — JWT 认证：</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><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></pre></td><td class="code"><pre><span class="line"><span class="string">&quot;&quot;&quot;JWT 认证：签发令牌、验证令牌、获取当前用户。&quot;&quot;&quot;</span></span><br><span class="line"><span class="keyword">from</span> datetime <span class="keyword">import</span> datetime, timedelta, timezone</span><br><span class="line"><span class="keyword">from</span> typing <span class="keyword">import</span> Annotated</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> fastapi <span class="keyword">import</span> Depends, HTTPException, status</span><br><span class="line"><span class="keyword">from</span> fastapi.security <span class="keyword">import</span> OAuth2PasswordBearer, OAuth2PasswordRequestForm</span><br><span class="line"><span class="keyword">from</span> jose <span class="keyword">import</span> JWTError, jwt  <span class="comment"># pip install python-jose[cryptopy]</span></span><br><span class="line"><span class="keyword">from</span> passlib.context <span class="keyword">import</span> CryptContext</span><br><span class="line"><span class="keyword">from</span> sqlalchemy.orm <span class="keyword">import</span> Session</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> app.config <span class="keyword">import</span> SECRET_KEY, ALGORITHM, ACCESS_TOKEN_EXPIRE_MINUTES</span><br><span class="line"><span class="keyword">from</span> app.database <span class="keyword">import</span> get_db</span><br><span class="line"><span class="keyword">from</span> app.models <span class="keyword">import</span> User</span><br><span class="line"><span class="keyword">from</span> app.schemas <span class="keyword">import</span> Token, TokenData</span><br><span class="line"></span><br><span class="line"><span class="comment"># OAuth2 密码模式：客户端发送用户名+密码，服务端返回 access_token</span></span><br><span class="line">oauth2_scheme = OAuth2PasswordBearer(tokenUrl=<span class="string">&quot;auth/token&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 密码哈希上下文（使用 bcrypt 算法）</span></span><br><span class="line">pwd_context = CryptContext(schemes=[<span class="string">&quot;bcrypt&quot;</span>], deprecated=<span class="string">&quot;auto&quot;</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># ── 密码工具函数 ────────────────────────────────────────</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">hash_password</span>(<span class="params">plain_password: <span class="built_in">str</span></span>) -&gt; <span class="built_in">str</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;将明文密码哈希化，存入数据库。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">return</span> pwd_context.<span class="built_in">hash</span>(plain_password)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">verify_password</span>(<span class="params">plain_password: <span class="built_in">str</span>, hashed_password: <span class="built_in">str</span></span>) -&gt; <span class="built_in">bool</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;验证明文密码是否与哈希匹配。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">return</span> pwd_context.verify(plain_password, hashed_password)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># ── JWT 工具函数 ────────────────────────────────────────</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">create_access_token</span>(<span class="params">data: <span class="built_in">dict</span>, expires_delta: timedelta | <span class="literal">None</span> = <span class="literal">None</span></span>) -&gt; <span class="built_in">str</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;签发 JWT 令牌。data 中通常包含 &#123;&quot;sub&quot;: username&#125;。&quot;&quot;&quot;</span></span><br><span class="line">    to_encode = data.copy()</span><br><span class="line">    expire = datetime.now(timezone.utc) + (</span><br><span class="line">        expires_delta <span class="keyword">or</span> timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)</span><br><span class="line">    )</span><br><span class="line">    to_encode.update(&#123;<span class="string">&quot;exp&quot;</span>: expire&#125;)</span><br><span class="line">    <span class="keyword">return</span> jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">decode_token</span>(<span class="params">token: <span class="built_in">str</span></span>) -&gt; TokenData:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;解码并验证 JWT 令牌，返回其中携带的数据。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])</span><br><span class="line">        username: <span class="built_in">str</span> | <span class="literal">None</span> = payload.get(<span class="string">&quot;sub&quot;</span>)</span><br><span class="line">        <span class="keyword">if</span> username <span class="keyword">is</span> <span class="literal">None</span>:</span><br><span class="line">            <span class="keyword">raise</span> JWTError()</span><br><span class="line">        <span class="keyword">return</span> TokenData(username=username)</span><br><span class="line">    <span class="keyword">except</span> JWTError:</span><br><span class="line">        <span class="keyword">raise</span> HTTPException(</span><br><span class="line">            status_code=status.HTTP_401_UNAUTHORIZED,</span><br><span class="line">            detail=<span class="string">&quot;无法验证凭据&quot;</span>,</span><br><span class="line">            headers=&#123;<span class="string">&quot;WWW-Authenticate&quot;</span>: <span class="string">&quot;Bearer&quot;</span>&#125;,</span><br><span class="line">        )</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># ── 依赖注入：获取当前用户 ─────────────────────────────</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">get_current_user</span>(<span class="params"></span></span><br><span class="line"><span class="params">    token: Annotated[<span class="built_in">str</span>, Depends(<span class="params">oauth2_scheme</span>)],</span></span><br><span class="line"><span class="params">    db: Annotated[Session, Depends(<span class="params">get_db</span>)],</span></span><br><span class="line"><span class="params"></span>) -&gt; User:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;从请求头中提取 token，验证后返回当前用户对象。需要登录的路由用 Depends() 注入。&quot;&quot;&quot;</span></span><br><span class="line">    token_data = decode_token(token)</span><br><span class="line">    user = db.query(User).<span class="built_in">filter</span>(User.username == token_data.username).first()</span><br><span class="line">    <span class="keyword">if</span> user <span class="keyword">is</span> <span class="literal">None</span>:</span><br><span class="line">        <span class="keyword">raise</span> HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=<span class="string">&quot;用户不存在&quot;</span>)</span><br><span class="line">    <span class="keyword">return</span> user</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">get_current_active_user</span>(<span class="params"></span></span><br><span class="line"><span class="params">    current_user: Annotated[User, Depends(<span class="params">get_current_user</span>)],</span></span><br><span class="line"><span class="params"></span>) -&gt; User:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;获取当前活跃用户：如果用户被禁用则拒绝访问。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> current_user.is_active:</span><br><span class="line">        <span class="keyword">raise</span> HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=<span class="string">&quot;用户已被禁用&quot;</span>)</span><br><span class="line">    <span class="keyword">return</span> current_user</span><br></pre></td></tr></table></figure><p><strong><code>app/routers/users.py</code></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><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></pre></td><td class="code"><pre><span class="line"><span class="string">&quot;&quot;&quot;用户路由：注册、登录、查询、修改。&quot;&quot;&quot;</span></span><br><span class="line"><span class="keyword">from</span> typing <span class="keyword">import</span> Annotated</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> fastapi <span class="keyword">import</span> APIRouter, Depends, HTTPException, status</span><br><span class="line"><span class="keyword">from</span> fastapi.security <span class="keyword">import</span> OAuth2PasswordRequestForm</span><br><span class="line"><span class="keyword">from</span> sqlalchemy.orm <span class="keyword">import</span> Session</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> app.database <span class="keyword">import</span> get_db</span><br><span class="line"><span class="keyword">from</span> app.models <span class="keyword">import</span> User</span><br><span class="line"><span class="keyword">from</span> app.schemas <span class="keyword">import</span> UserCreate, UserUpdate, UserOut, Token</span><br><span class="line"><span class="keyword">from</span> app.auth <span class="keyword">import</span> (</span><br><span class="line">    hash_password,</span><br><span class="line">    verify_password,</span><br><span class="line">    create_access_token,</span><br><span class="line">    get_current_active_user,</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line">router = APIRouter(prefix=<span class="string">&quot;/users&quot;</span>, tags=[<span class="string">&quot;用户管理&quot;</span>])</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">@router.post(<span class="params"><span class="string">&quot;/register&quot;</span>, response_model=UserOut, status_code=status.HTTP_201_CREATED</span>)</span></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">register</span>(<span class="params">user_in: UserCreate, db: Annotated[Session, Depends(<span class="params">get_db</span>)]</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;注册新用户：检查用户名和邮箱是否已存在，密码哈希后存入数据库。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="comment"># 检查用户名是否已存在</span></span><br><span class="line">    <span class="keyword">if</span> db.query(User).<span class="built_in">filter</span>(User.username == user_in.username).first():</span><br><span class="line">        <span class="keyword">raise</span> HTTPException(status_code=<span class="number">400</span>, detail=<span class="string">&quot;用户名已存在&quot;</span>)</span><br><span class="line">    <span class="comment"># 检查邮箱是否已存在</span></span><br><span class="line">    <span class="keyword">if</span> db.query(User).<span class="built_in">filter</span>(User.email == user_in.email).first():</span><br><span class="line">        <span class="keyword">raise</span> HTTPException(status_code=<span class="number">400</span>, detail=<span class="string">&quot;邮箱已存在&quot;</span>)</span><br><span class="line">    <span class="comment"># 创建用户（密码必须哈希，绝不能存明文！）</span></span><br><span class="line">    db_user = User(</span><br><span class="line">        username=user_in.username,</span><br><span class="line">        email=user_in.email,</span><br><span class="line">        hashed_password=hash_password(user_in.password),</span><br><span class="line">    )</span><br><span class="line">    db.add(db_user)</span><br><span class="line">    db.commit()</span><br><span class="line">    db.refresh(db_user)  <span class="comment"># 刷新以获取自增 id</span></span><br><span class="line">    <span class="keyword">return</span> db_user</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">@router.post(<span class="params"><span class="string">&quot;/token&quot;</span>, response_model=Token</span>)</span></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">login</span>(<span class="params"></span></span><br><span class="line"><span class="params">    form_data: Annotated[OAuth2PasswordRequestForm, Depends(<span class="params"></span>)],</span></span><br><span class="line"><span class="params">    db: Annotated[Session, Depends(<span class="params">get_db</span>)],</span></span><br><span class="line"><span class="params"></span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;登录获取 JWT 令牌：OAuth2PasswordRequestForm 会自动解析 username + password。&quot;&quot;&quot;</span></span><br><span class="line">    user = db.query(User).<span class="built_in">filter</span>(User.username == form_data.username).first()</span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> user <span class="keyword">or</span> <span class="keyword">not</span> verify_password(form_data.password, user.hashed_password):</span><br><span class="line">        <span class="keyword">raise</span> HTTPException(</span><br><span class="line">            status_code=status.HTTP_401_UNAUTHORIZED,</span><br><span class="line">            detail=<span class="string">&quot;用户名或密码错误&quot;</span>,</span><br><span class="line">            headers=&#123;<span class="string">&quot;WWW-Authenticate&quot;</span>: <span class="string">&quot;Bearer&quot;</span>&#125;,</span><br><span class="line">        )</span><br><span class="line">    <span class="comment"># 签发令牌，sub（subject）字段存用户名</span></span><br><span class="line">    access_token = create_access_token(data=&#123;<span class="string">&quot;sub&quot;</span>: user.username&#125;)</span><br><span class="line">    <span class="keyword">return</span> Token(access_token=access_token)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">@router.get(<span class="params"><span class="string">&quot;/me&quot;</span>, response_model=UserOut</span>)</span></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">read_current_user</span>(<span class="params">current_user: Annotated[User, Depends(<span class="params">get_current_active_user</span>)]</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;获取当前登录用户信息（需要认证）。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">return</span> current_user</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">@router.put(<span class="params"><span class="string">&quot;/me&quot;</span>, response_model=UserOut</span>)</span></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">update_current_user</span>(<span class="params"></span></span><br><span class="line"><span class="params">    user_in: UserUpdate,</span></span><br><span class="line"><span class="params">    current_user: Annotated[User, Depends(<span class="params">get_current_active_user</span>)],</span></span><br><span class="line"><span class="params">    db: Annotated[Session, Depends(<span class="params">get_db</span>)],</span></span><br><span class="line"><span class="params"></span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;更新当前用户信息（需要认证）。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">if</span> user_in.username <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">None</span>:</span><br><span class="line">        current_user.username = user_in.username</span><br><span class="line">    <span class="keyword">if</span> user_in.email <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">None</span>:</span><br><span class="line">        current_user.email = user_in.email</span><br><span class="line">    db.commit()</span><br><span class="line">    db.refresh(current_user)</span><br><span class="line">    <span class="keyword">return</span> current_user</span><br></pre></td></tr></table></figure><p><strong><code>app/routers/items.py</code></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><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></pre></td><td class="code"><pre><span class="line"><span class="string">&quot;&quot;&quot;业务路由：Item 的增删改查，演示需要登录才能操作的路由。&quot;&quot;&quot;</span></span><br><span class="line"><span class="keyword">from</span> typing <span class="keyword">import</span> Annotated</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> fastapi <span class="keyword">import</span> APIRouter, Depends, HTTPException, status</span><br><span class="line"><span class="keyword">from</span> sqlalchemy.orm <span class="keyword">import</span> Session</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> app.database <span class="keyword">import</span> get_db</span><br><span class="line"><span class="keyword">from</span> app.models <span class="keyword">import</span> User, Item</span><br><span class="line"><span class="keyword">from</span> app.schemas <span class="keyword">import</span> ItemCreate, ItemOut</span><br><span class="line"><span class="keyword">from</span> app.auth <span class="keyword">import</span> get_current_active_user</span><br><span class="line"></span><br><span class="line">router = APIRouter(prefix=<span class="string">&quot;/items&quot;</span>, tags=[<span class="string">&quot;物品管理&quot;</span>])</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">@router.get(<span class="params"><span class="string">&quot;/&quot;</span>, response_model=<span class="built_in">list</span>[ItemOut]</span>)</span></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">list_items</span>(<span class="params"></span></span><br><span class="line"><span class="params">    skip: <span class="built_in">int</span> = <span class="number">0</span>,</span></span><br><span class="line"><span class="params">    limit: <span class="built_in">int</span> = <span class="number">100</span>,</span></span><br><span class="line"><span class="params">    db: Annotated[Session, Depends(<span class="params">get_db</span>)] = <span class="literal">None</span>,</span></span><br><span class="line"><span class="params"></span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;获取物品列表（公开接口，无需登录）。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">return</span> db.query(Item).offset(skip).limit(limit).<span class="built_in">all</span>()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">@router.post(<span class="params"><span class="string">&quot;/&quot;</span>, response_model=ItemOut, status_code=status.HTTP_201_CREATED</span>)</span></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">create_item</span>(<span class="params"></span></span><br><span class="line"><span class="params">    item_in: ItemCreate,</span></span><br><span class="line"><span class="params">    current_user: Annotated[User, Depends(<span class="params">get_current_active_user</span>)],</span></span><br><span class="line"><span class="params">    db: Annotated[Session, Depends(<span class="params">get_db</span>)],</span></span><br><span class="line"><span class="params"></span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;创建物品（需要登录）。物品自动归属当前用户。&quot;&quot;&quot;</span></span><br><span class="line">    db_item = Item(**item_in.model_dump(), owner_id=current_user.<span class="built_in">id</span>)</span><br><span class="line">    db.add(db_item)</span><br><span class="line">    db.commit()</span><br><span class="line">    db.refresh(db_item)</span><br><span class="line">    <span class="keyword">return</span> db_item</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">@router.get(<span class="params"><span class="string">&quot;/&#123;item_id&#125;&quot;</span>, response_model=ItemOut</span>)</span></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">read_item</span>(<span class="params">item_id: <span class="built_in">int</span>, db: Annotated[Session, Depends(<span class="params">get_db</span>)]</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;获取单个物品详情（公开接口）。&quot;&quot;&quot;</span></span><br><span class="line">    item = db.query(Item).<span class="built_in">filter</span>(Item.<span class="built_in">id</span> == item_id).first()</span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> item:</span><br><span class="line">        <span class="keyword">raise</span> HTTPException(status_code=<span class="number">404</span>, detail=<span class="string">&quot;物品不存在&quot;</span>)</span><br><span class="line">    <span class="keyword">return</span> item</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">@router.delete(<span class="params"><span class="string">&quot;/&#123;item_id&#125;&quot;</span>, status_code=status.HTTP_204_NO_CONTENT</span>)</span></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">delete_item</span>(<span class="params"></span></span><br><span class="line"><span class="params">    item_id: <span class="built_in">int</span>,</span></span><br><span class="line"><span class="params">    current_user: Annotated[User, Depends(<span class="params">get_current_active_user</span>)],</span></span><br><span class="line"><span class="params">    db: Annotated[Session, Depends(<span class="params">get_db</span>)],</span></span><br><span class="line"><span class="params"></span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;删除物品（需要登录，且只能删除自己的物品）。&quot;&quot;&quot;</span></span><br><span class="line">    item = db.query(Item).<span class="built_in">filter</span>(Item.<span class="built_in">id</span> == item_id).first()</span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> item:</span><br><span class="line">        <span class="keyword">raise</span> HTTPException(status_code=<span class="number">404</span>, detail=<span class="string">&quot;物品不存在&quot;</span>)</span><br><span class="line">    <span class="keyword">if</span> item.owner_id != current_user.<span class="built_in">id</span>:</span><br><span class="line">        <span class="keyword">raise</span> HTTPException(status_code=<span class="number">403</span>, detail=<span class="string">&quot;无权删除他人的物品&quot;</span>)</span><br><span class="line">    db.delete(item)</span><br><span class="line">    db.commit()</span><br></pre></td></tr></table></figure><p><strong><code>app/main.py</code></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><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></pre></td><td class="code"><pre><span class="line"><span class="string">&quot;&quot;&quot;FastAPI 应用入口：创建实例、注册路由、配置中间件和启动事件。&quot;&quot;&quot;</span></span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"><span class="keyword">from</span> contextlib <span class="keyword">import</span> asynccontextmanager</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> fastapi <span class="keyword">import</span> FastAPI, Request</span><br><span class="line"><span class="keyword">from</span> fastapi.middleware.cors <span class="keyword">import</span> CORSMiddleware</span><br><span class="line"><span class="keyword">from</span> fastapi.responses <span class="keyword">import</span> JSONResponse</span><br><span class="line"><span class="keyword">from</span> sqlalchemy.orm <span class="keyword">import</span> Session</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> app.database <span class="keyword">import</span> engine, Base, get_db</span><br><span class="line"><span class="keyword">from</span> app.models <span class="keyword">import</span> User, Item  <span class="comment"># 导入所有模型，确保建表时能识别</span></span><br><span class="line"><span class="keyword">from</span> app.routers <span class="keyword">import</span> users, items</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># ── 生命周期事件：应用启动时建表 ─────────────────────────</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@asynccontextmanager</span></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">lifespan</span>(<span class="params">app: FastAPI</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;应用启动时自动创建所有数据库表（仅用于开发，生产用 Alembic 迁移）。&quot;&quot;&quot;</span></span><br><span class="line">    Base.metadata.create_all(bind=engine)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;✅ 数据库表已创建（如不存在）&quot;</span>)</span><br><span class="line">    <span class="keyword">yield</span>  <span class="comment"># 应用运行中...</span></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;👋 应用正在关闭&quot;</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># ── 创建 FastAPI 实例 ────────────────────────────────────</span></span><br><span class="line"></span><br><span class="line">app = FastAPI(</span><br><span class="line">    title=<span class="string">&quot;FastAPI 完整示例&quot;</span>,</span><br><span class="line">    description=<span class="string">&quot;带数据库 + JWT 认证的完整项目骨架&quot;</span>,</span><br><span class="line">    version=<span class="string">&quot;1.0.0&quot;</span>,</span><br><span class="line">    lifespan=lifespan,</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># ── 中间件 ─────────────────────────────────────────────</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 1. CORS 中间件：允许前端跨域请求（前后端分离必备）</span></span><br><span class="line">app.add_middleware(</span><br><span class="line">    CORSMiddleware,</span><br><span class="line">    allow_origins=[<span class="string">&quot;*&quot;</span>],        <span class="comment"># 生产环境应改为具体域名，如 [&quot;https://yourdomain.com&quot;]</span></span><br><span class="line">    allow_credentials=<span class="literal">True</span>,</span><br><span class="line">    allow_methods=[<span class="string">&quot;*&quot;</span>],        <span class="comment"># 允许所有 HTTP 方法</span></span><br><span class="line">    allow_headers=[<span class="string">&quot;*&quot;</span>],        <span class="comment"># 允许所有请求头</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># 2. 自定义中间件：请求耗时日志（方便排查慢接口）</span></span><br><span class="line"><span class="meta">@app.middleware(<span class="params"><span class="string">&quot;http&quot;</span></span>)</span></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">log_request_time</span>(<span class="params">request: Request, call_next</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;记录每个请求的处理耗时。&quot;&quot;&quot;</span></span><br><span class="line">    start = time.time()</span><br><span class="line">    response = <span class="keyword">await</span> call_next(request)  <span class="comment"># 继续处理请求</span></span><br><span class="line">    elapsed = time.time() - start</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;⏱  <span class="subst">&#123;request.method&#125;</span> <span class="subst">&#123;request.url.path&#125;</span> → <span class="subst">&#123;elapsed:<span class="number">.3</span>f&#125;</span>s&quot;</span>)</span><br><span class="line">    <span class="keyword">return</span> response</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># ── 全局异常处理 ────────────────────────────────────────</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@app.exception_handler(<span class="params">Exception</span>)</span></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">global_exception_handler</span>(<span class="params">request: Request, exc: Exception</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;全局异常兜底：防止未捕获的异常返回 500 时泄露堆栈信息。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;❌ 未处理异常: <span class="subst">&#123;exc&#125;</span>&quot;</span>)</span><br><span class="line">    <span class="keyword">return</span> JSONResponse(</span><br><span class="line">        status_code=<span class="number">500</span>,</span><br><span class="line">        content=&#123;<span class="string">&quot;detail&quot;</span>: <span class="string">&quot;服务器内部错误，请稍后重试&quot;</span>&#125;,</span><br><span class="line">    )</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># ── 注册路由 ─────────────────────────────────────────────</span></span><br><span class="line"></span><br><span class="line">app.include_router(users.router)</span><br><span class="line">app.include_router(items.router)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># ── 健康检查 ─────────────────────────────────────────────</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@app.get(<span class="params"><span class="string">&quot;/&quot;</span>, tags=[<span class="string">&quot;系统&quot;</span>]</span>)</span></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">health_check</span>():</span><br><span class="line">    <span class="string">&quot;&quot;&quot;健康检查接口，部署后用于负载均衡器探测服务是否存活。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">return</span> &#123;<span class="string">&quot;status&quot;</span>: <span class="string">&quot;ok&quot;</span>, <span class="string">&quot;message&quot;</span>: <span class="string">&quot;FastAPI 服务运行中&quot;</span>&#125;</span><br></pre></td></tr></table></figure><h4 id="第四步：启动和测试"><a href="#第四步：启动和测试" class="headerlink" title="第四步：启动和测试"></a>第四步：启动和测试</h4><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><span class="line"><span class="comment"># 确保在项目根目录（fastapi-demo/），且虚拟环境已激活</span></span><br><span class="line">uvicorn app.main:app --reload --host 0.0.0.0 --port 8000</span><br><span class="line"></span><br><span class="line"><span class="comment"># --reload   ：代码修改后自动重启（仅开发用）</span></span><br><span class="line"><span class="comment"># --host     ：监听所有网卡（Docker 部署需要）</span></span><br><span class="line"><span class="comment"># --port     ：端口号</span></span><br></pre></td></tr></table></figure><p>启动后打开浏览器：</p><table><thead><tr><th>地址</th><th>说明</th></tr></thead><tbody><tr><td><code>http://localhost:8000/docs</code></td><td><strong>Swagger UI</strong>：可视化交互式 API 文档，可直接在页面测试接口</td></tr><tr><td><code>http://localhost:8000/redoc</code></td><td><strong>ReDoc</strong>：另一种风格的 API 文档</td></tr><tr><td><code>http://localhost:8000/</code></td><td>健康检查，返回 <code>{&quot;status&quot;: &quot;ok&quot;}</code></td></tr></tbody></table><p><strong>测试流程</strong>（在 Swagger UI 中操作最方便）：</p><figure class="highlight plaintext"><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><span class="line">1. POST /users/register  → 注册一个用户（填 username, email, password）</span><br><span class="line">2. POST /users/token     → 登录获取 JWT 令牌（点页面上的 🔒 Authorize 按钮填 token）</span><br><span class="line">3. GET  /users/me        → 查看当前用户信息（需登录）</span><br><span class="line">4. POST /items/           → 创建一个物品（需登录）</span><br><span class="line">5. GET  /items/           → 查看所有物品（无需登录）</span><br></pre></td></tr></table></figure><p><strong>补充安装</strong>：如果 <code>auth.py</code> 中用了 <code>python-jose</code>，请额外安装：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pip install python-jose[cryptopy]</span><br></pre></td></tr></table></figure><hr><h3 id="1-3-从零开始：Django-快速入门"><a href="#1-3-从零开始：Django-快速入门" class="headerlink" title="1.3 从零开始：Django 快速入门"></a>1.3 从零开始：Django 快速入门</h3><blockquote><p>Django 是”batteries included”的全功能框架——ORM、Admin 后台、认证系统、表单、模板引擎全部内置，适合快速搭建完整网站。</p></blockquote><h4 id="第一步：安装和创建项目"><a href="#第一步：安装和创建项目" class="headerlink" title="第一步：安装和创建项目"></a>第一步：安装和创建项目</h4><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><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><span class="line"><span class="comment"># 1. 安装 Django</span></span><br><span class="line">pip install django</span><br><span class="line"></span><br><span class="line"><span class="comment"># 2. 创建项目（myproject 是项目名，末尾的 . 表示在当前目录创建，不额外套一层）</span></span><br><span class="line">django-admin startproject myproject .</span><br><span class="line"></span><br><span class="line"><span class="comment"># 3. 创建应用（一个项目可以有多个应用，每个应用是一个功能模块）</span></span><br><span class="line">python manage.py startapp blog</span><br><span class="line"></span><br><span class="line"><span class="comment"># 4. 此时目录结构如下：</span></span><br><span class="line"><span class="comment"># myproject/</span></span><br><span class="line"><span class="comment"># ├── manage.py              # Django 命令行入口</span></span><br><span class="line"><span class="comment"># ├── myproject/</span></span><br><span class="line"><span class="comment"># │   ├── __init__.py</span></span><br><span class="line"><span class="comment"># │   ├── settings.py        # 项目配置（数据库、安装的应用、中间件等）</span></span><br><span class="line"><span class="comment"># │   ├── urls.py            # 根路由</span></span><br><span class="line"><span class="comment"># │   ├── asgi.py</span></span><br><span class="line"><span class="comment"># │   └── wsgi.py</span></span><br><span class="line"><span class="comment"># ├── blog/</span></span><br><span class="line"><span class="comment"># │   ├── __init__.py</span></span><br><span class="line"><span class="comment"># │   ├── models.py          # 数据模型</span></span><br><span class="line"><span class="comment"># │   ├── views.py           # 视图（处理请求的逻辑）</span></span><br><span class="line"><span class="comment"># │   ├── urls.py            # 应用路由（需手动创建）</span></span><br><span class="line"><span class="comment"># │   ├── admin.py           # Admin 后台配置</span></span><br><span class="line"><span class="comment"># │   ├── apps.py</span></span><br><span class="line"><span class="comment"># │   ├── tests.py</span></span><br><span class="line"><span class="comment"># │   └── migrations/</span></span><br><span class="line"><span class="comment"># └── db.sqlite3             # 默认 SQLite 数据库（执行 migrate 后自动生成）</span></span><br></pre></td></tr></table></figure><h4 id="第二步：注册应用"><a href="#第二步：注册应用" class="headerlink" title="第二步：注册应用"></a>第二步：注册应用</h4><p>编辑 <code>myproject/settings.py</code>，在 <code>INSTALLED_APPS</code> 中添加 <code>&#39;blog&#39;</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></pre></td><td class="code"><pre><span class="line"><span class="comment"># myproject/settings.py</span></span><br><span class="line"></span><br><span class="line">INSTALLED_APPS = [</span><br><span class="line">    <span class="string">&#x27;django.contrib.admin&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;django.contrib.auth&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;django.contrib.contenttypes&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;django.contrib.sessions&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;django.contrib.messages&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;django.contrib.staticfiles&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;blog&#x27;</span>,  <span class="comment"># ← 添加我们创建的应用</span></span><br><span class="line">]</span><br></pre></td></tr></table></figure><h4 id="第三步：定义-Model（数据库表）"><a href="#第三步：定义-Model（数据库表）" class="headerlink" title="第三步：定义 Model（数据库表）"></a>第三步：定义 Model（数据库表）</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></pre></td><td class="code"><pre><span class="line"><span class="comment"># blog/models.py</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> django.db <span class="keyword">import</span> models</span><br><span class="line"><span class="keyword">from</span> django.contrib.auth.models <span class="keyword">import</span> User</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Post</span>(models.Model):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;博客文章模型：对应数据库中的 blog_post 表。&quot;&quot;&quot;</span></span><br><span class="line">    title = models.CharField(<span class="string">&quot;标题&quot;</span>, max_length=<span class="number">200</span>)</span><br><span class="line">    content = models.TextField(<span class="string">&quot;正文&quot;</span>)</span><br><span class="line">    author = models.ForeignKey(</span><br><span class="line">        User,                    <span class="comment"># 关联 Django 内置用户模型</span></span><br><span class="line">        on_delete=models.CASCADE,  <span class="comment"># 作者被删除时，其文章也全部删除</span></span><br><span class="line">        verbose_name=<span class="string">&quot;作者&quot;</span>,</span><br><span class="line">    )</span><br><span class="line">    created_at = models.DateTimeField(<span class="string">&quot;创建时间&quot;</span>, auto_now_add=<span class="literal">True</span>)  <span class="comment"># 自动记录创建时间</span></span><br><span class="line">    updated_at = models.DateTimeField(<span class="string">&quot;更新时间&quot;</span>, auto_now=<span class="literal">True</span>)      <span class="comment"># 每次保存自动更新</span></span><br><span class="line">    is_published = models.BooleanField(<span class="string">&quot;是否发布&quot;</span>, default=<span class="literal">False</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">class</span> <span class="title class_">Meta</span>:</span><br><span class="line">        ordering = [<span class="string">&#x27;-created_at&#x27;</span>]  <span class="comment"># 默认按创建时间倒序排列（最新的在前）</span></span><br><span class="line">        verbose_name = <span class="string">&quot;文章&quot;</span></span><br><span class="line">        verbose_name_plural = verbose_name</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__str__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;在 Admin 后台和打印时显示的名称。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>.title</span><br></pre></td></tr></table></figure><p><strong>生成并执行数据库迁移</strong>：</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><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 根据模型变更生成迁移文件</span></span><br><span class="line">python manage.py makemigrations</span><br><span class="line"></span><br><span class="line"><span class="comment"># 执行迁移，实际创建/修改数据库表</span></span><br><span class="line">python manage.py migrate</span><br><span class="line"></span><br><span class="line"><span class="comment"># 输出示例：</span></span><br><span class="line"><span class="comment">#   Creating table blog_post ...</span></span><br><span class="line"><span class="comment">#   Running migrate: OK</span></span><br></pre></td></tr></table></figure><h4 id="第四步：Admin-后台"><a href="#第四步：Admin-后台" class="headerlink" title="第四步：Admin 后台"></a>第四步：Admin 后台</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></pre></td><td class="code"><pre><span class="line"><span class="comment"># blog/admin.py</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> django.contrib <span class="keyword">import</span> admin</span><br><span class="line"><span class="keyword">from</span> .models <span class="keyword">import</span> Post</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">@admin.register(<span class="params">Post</span>)</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">PostAdmin</span>(admin.ModelAdmin):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;文章的 Admin 后台配置：控制列表显示、搜索、筛选等功能。&quot;&quot;&quot;</span></span><br><span class="line">    list_display = [<span class="string">&#x27;title&#x27;</span>, <span class="string">&#x27;author&#x27;</span>, <span class="string">&#x27;is_published&#x27;</span>, <span class="string">&#x27;created_at&#x27;</span>]  <span class="comment"># 列表页显示的列</span></span><br><span class="line">    list_filter = [<span class="string">&#x27;is_published&#x27;</span>, <span class="string">&#x27;created_at&#x27;</span>]   <span class="comment"># 右侧筛选栏</span></span><br><span class="line">    search_fields = [<span class="string">&#x27;title&#x27;</span>, <span class="string">&#x27;content&#x27;</span>]           <span class="comment"># 搜索框可搜索的字段</span></span><br><span class="line">    list_editable = [<span class="string">&#x27;is_published&#x27;</span>]               <span class="comment"># 可在列表页直接编辑的字段</span></span><br><span class="line">    date_hierarchy = <span class="string">&#x27;created_at&#x27;</span>                  <span class="comment"># 按日期层级导航</span></span><br></pre></td></tr></table></figure><p><strong>创建超级管理员</strong>：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">python manage.py createsuperuser</span><br><span class="line"><span class="comment"># 按提示输入用户名、邮箱、密码</span></span><br></pre></td></tr></table></figure><p><strong>启动开发服务器</strong>：</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></pre></td><td class="code"><pre><span class="line">python manage.py runserver</span><br><span class="line"><span class="comment"># 访问 http://127.0.0.1:8000/admin/ → 进入 Admin 后台</span></span><br><span class="line"><span class="comment"># 访问 http://127.0.0.1:8000/ → 欢迎页（还没写视图，会 404）</span></span><br></pre></td></tr></table></figure><h4 id="第五步：视图和路由"><a href="#第五步：视图和路由" class="headerlink" title="第五步：视图和路由"></a>第五步：视图和路由</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></pre></td><td class="code"><pre><span class="line"><span class="comment"># blog/views.py</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> django.shortcuts <span class="keyword">import</span> render, get_object_or_404</span><br><span class="line"><span class="keyword">from</span> django.http <span class="keyword">import</span> JsonResponse</span><br><span class="line"><span class="keyword">from</span> .models <span class="keyword">import</span> Post</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">post_list</span>(<span class="params">request</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;文章列表页：渲染 HTML 模板。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="comment"># 只查询已发布的文章，按创建时间倒序</span></span><br><span class="line">    posts = Post.objects.<span class="built_in">filter</span>(is_published=<span class="literal">True</span>).order_by(<span class="string">&#x27;-created_at&#x27;</span>)</span><br><span class="line">    <span class="keyword">return</span> render(request, <span class="string">&#x27;blog/post_list.html&#x27;</span>, &#123;<span class="string">&#x27;posts&#x27;</span>: posts&#125;)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">post_detail</span>(<span class="params">request, pk</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;文章详情页：根据主键查询单篇文章。&quot;&quot;&quot;</span></span><br><span class="line">    post = get_object_or_404(Post, pk=pk, is_published=<span class="literal">True</span>)</span><br><span class="line">    <span class="keyword">return</span> render(request, <span class="string">&#x27;blog/post_detail.html&#x27;</span>, &#123;<span class="string">&#x27;post&#x27;</span>: post&#125;)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">post_list_api</span>(<span class="params">request</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;API 视图：返回 JSON 格式的文章列表（演示 JsonResponse 用法）。&quot;&quot;&quot;</span></span><br><span class="line">    posts = Post.objects.<span class="built_in">filter</span>(is_published=<span class="literal">True</span>).values(<span class="string">&#x27;id&#x27;</span>, <span class="string">&#x27;title&#x27;</span>, <span class="string">&#x27;author__username&#x27;</span>, <span class="string">&#x27;created_at&#x27;</span>)</span><br><span class="line">    <span class="keyword">return</span> JsonResponse(<span class="built_in">list</span>(posts), safe=<span class="literal">False</span>)  <span class="comment"># safe=False 允许返回列表</span></span><br></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><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><span class="line"><span class="comment"># blog/urls.py（手动创建此文件）</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> django.urls <span class="keyword">import</span> path</span><br><span class="line"><span class="keyword">from</span> . <span class="keyword">import</span> views</span><br><span class="line"></span><br><span class="line">app_name = <span class="string">&#x27;blog&#x27;</span>  <span class="comment"># 命名空间，模板中可以用 &#123;% url &#x27;blog:post_list&#x27; %&#125;</span></span><br><span class="line"></span><br><span class="line">urlpatterns = [</span><br><span class="line">    path(<span class="string">&#x27;&#x27;</span>, views.post_list, name=<span class="string">&#x27;post_list&#x27;</span>),           <span class="comment"># /blog/</span></span><br><span class="line">    path(<span class="string">&#x27;&lt;int:pk&gt;/&#x27;</span>, views.post_detail, name=<span class="string">&#x27;post_detail&#x27;</span>),  <span class="comment"># /blog/1/</span></span><br><span class="line">    path(<span class="string">&#x27;api/&#x27;</span>, views.post_list_api, name=<span class="string">&#x27;post_list_api&#x27;</span>),   <span class="comment"># /blog/api/</span></span><br><span class="line">]</span><br></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><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># myproject/urls.py</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> django.contrib <span class="keyword">import</span> admin</span><br><span class="line"><span class="keyword">from</span> django.urls <span class="keyword">import</span> path, include</span><br><span class="line"></span><br><span class="line">urlpatterns = [</span><br><span class="line">    path(<span class="string">&#x27;admin/&#x27;</span>, admin.site.urls),     <span class="comment"># Admin 后台</span></span><br><span class="line">    path(<span class="string">&#x27;blog/&#x27;</span>, include(<span class="string">&#x27;blog.urls&#x27;</span>)), <span class="comment"># 博客应用的路由</span></span><br><span class="line">]</span><br></pre></td></tr></table></figure><h4 id="第六步：模板渲染"><a href="#第六步：模板渲染" class="headerlink" title="第六步：模板渲染"></a>第六步：模板渲染</h4><p>创建模板目录和文件：</p><figure class="highlight plaintext"><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><span class="line">blog/</span><br><span class="line">└── templates/</span><br><span class="line">    └── blog/</span><br><span class="line">        ├── post_list.html     # 文章列表页</span><br><span class="line">        └── post_detail.html   # 文章详情页</span><br></pre></td></tr></table></figure><p><strong><code>blog/templates/blog/post_list.html</code></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></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;!DOCTYPE <span class="keyword">html</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">html</span> <span class="attr">lang</span>=<span class="string">&quot;zh-CN&quot;</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">head</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">&quot;UTF-8&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">title</span>&gt;</span>博客文章列表<span class="tag">&lt;/<span class="name">title</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">style</span>&gt;</span><span class="language-css"></span></span><br><span class="line"><span class="language-css">        <span class="selector-tag">body</span> &#123; <span class="attribute">font-family</span>: sans-serif; <span class="attribute">max-width</span>: <span class="number">800px</span>; <span class="attribute">margin</span>: <span class="number">0</span> auto; <span class="attribute">padding</span>: <span class="number">20px</span>; &#125;</span></span><br><span class="line"><span class="language-css">        <span class="selector-class">.post</span> &#123; <span class="attribute">border-bottom</span>: <span class="number">1px</span> solid <span class="number">#eee</span>; <span class="attribute">padding</span>: <span class="number">16px</span> <span class="number">0</span>; &#125;</span></span><br><span class="line"><span class="language-css">        <span class="selector-class">.post</span> <span class="selector-tag">h2</span> <span class="selector-tag">a</span> &#123; <span class="attribute">color</span>: <span class="number">#333</span>; <span class="attribute">text-decoration</span>: none; &#125;</span></span><br><span class="line"><span class="language-css">        <span class="selector-class">.post</span> <span class="selector-tag">h2</span> <span class="selector-tag">a</span><span class="selector-pseudo">:hover</span> &#123; <span class="attribute">color</span>: <span class="number">#0066cc</span>; &#125;</span></span><br><span class="line"><span class="language-css">        <span class="selector-class">.meta</span> &#123; <span class="attribute">color</span>: <span class="number">#999</span>; <span class="attribute">font-size</span>: <span class="number">14px</span>; &#125;</span></span><br><span class="line"><span class="language-css">    </span><span class="tag">&lt;/<span class="name">style</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">head</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">body</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">h1</span>&gt;</span>📝 博客文章<span class="tag">&lt;/<span class="name">h1</span>&gt;</span></span><br><span class="line">    &#123;% for post in posts %&#125;</span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;post&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">h2</span>&gt;</span><span class="tag">&lt;<span class="name">a</span> <span class="attr">href</span>=<span class="string">&quot;&#123;% url &#x27;blog:post_detail&#x27; post.pk %&#125;&quot;</span>&gt;</span>&#123;&#123; post.title &#125;&#125;<span class="tag">&lt;/<span class="name">a</span>&gt;</span><span class="tag">&lt;/<span class="name">h2</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">p</span> <span class="attr">class</span>=<span class="string">&quot;meta&quot;</span>&gt;</span>作者：&#123;&#123; post.author.username &#125;&#125; | 发布于：&#123;&#123; post.created_at|date:&quot;Y-m-d H:i&quot; &#125;&#125;<span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">p</span>&gt;</span>&#123;&#123; post.content|truncatewords:30 &#125;&#125;<span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">    &#123;% empty %&#125;</span><br><span class="line">    <span class="tag">&lt;<span class="name">p</span>&gt;</span>暂无文章。<span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line">    &#123;% endfor %&#125;</span><br><span class="line"><span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">html</span>&gt;</span></span><br></pre></td></tr></table></figure><p><strong><code>blog/templates/blog/post_detail.html</code></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></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;!DOCTYPE <span class="keyword">html</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">html</span> <span class="attr">lang</span>=<span class="string">&quot;zh-CN&quot;</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">head</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">&quot;UTF-8&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">title</span>&gt;</span>&#123;&#123; post.title &#125;&#125;<span class="tag">&lt;/<span class="name">title</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">style</span>&gt;</span><span class="language-css"></span></span><br><span class="line"><span class="language-css">        <span class="selector-tag">body</span> &#123; <span class="attribute">font-family</span>: sans-serif; <span class="attribute">max-width</span>: <span class="number">800px</span>; <span class="attribute">margin</span>: <span class="number">0</span> auto; <span class="attribute">padding</span>: <span class="number">20px</span>; &#125;</span></span><br><span class="line"><span class="language-css">        <span class="selector-class">.meta</span> &#123; <span class="attribute">color</span>: <span class="number">#999</span>; <span class="attribute">font-size</span>: <span class="number">14px</span>; &#125;</span></span><br><span class="line"><span class="language-css">        <span class="selector-tag">a</span><span class="selector-class">.back</span> &#123; <span class="attribute">color</span>: <span class="number">#0066cc</span>; &#125;</span></span><br><span class="line"><span class="language-css">    </span><span class="tag">&lt;/<span class="name">style</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">head</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">body</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">a</span> <span class="attr">class</span>=<span class="string">&quot;back&quot;</span> <span class="attr">href</span>=<span class="string">&quot;&#123;% url &#x27;blog:post_list&#x27; %&#125;&quot;</span>&gt;</span>← 返回列表<span class="tag">&lt;/<span class="name">a</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">h1</span>&gt;</span>&#123;&#123; post.title &#125;&#125;<span class="tag">&lt;/<span class="name">h1</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">p</span> <span class="attr">class</span>=<span class="string">&quot;meta&quot;</span>&gt;</span>作者：&#123;&#123; post.author.username &#125;&#125; | 发布于：&#123;&#123; post.created_at|date:&quot;Y-m-d H:i&quot; &#125;&#125;<span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">hr</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span>&gt;</span>&#123;&#123; post.content|linebreaks &#125;&#125;<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">html</span>&gt;</span></span><br></pre></td></tr></table></figure><p><strong>完整运行流程</strong>：</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><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><span class="line">python manage.py makemigrations   <span class="comment"># 生成迁移</span></span><br><span class="line">python manage.py migrate          <span class="comment"># 执行迁移</span></span><br><span class="line">python manage.py createsuperuser  <span class="comment"># 创建管理员</span></span><br><span class="line">python manage.py runserver        <span class="comment"># 启动服务器</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 然后访问：</span></span><br><span class="line"><span class="comment"># http://127.0.0.1:8000/admin/   → Admin 后台，先创建几篇文章</span></span><br><span class="line"><span class="comment"># http://127.0.0.1:8000/blog/    → 前台文章列表</span></span><br><span class="line"><span class="comment"># http://127.0.0.1:8000/blog/1/  → 文章详情</span></span><br><span class="line"><span class="comment"># http://127.0.0.1:8000/blog/api/ → JSON API</span></span><br></pre></td></tr></table></figure><hr><h3 id="1-4-Web-开发核心概念入门"><a href="#1-4-Web-开发核心概念入门" class="headerlink" title="1.4 Web 开发核心概念入门"></a>1.4 Web 开发核心概念入门</h3><blockquote><p>如果你刚接触 Web 开发，这些概念是理解后端工作的基础。</p></blockquote><table><thead><tr><th>概念</th><th>一句话解释</th><th>类比</th></tr></thead><tbody><tr><td><strong>HTTP</strong></td><td>浏览器和服务器之间的通信协议，定义了请求方法（GET&#x2F;POST&#x2F;PUT&#x2F;DELETE）和状态码（200&#x2F;404&#x2F;500）</td><td>客户和服务员的对话规则</td></tr><tr><td><strong>RESTful</strong></td><td>一种 API 设计风格：用 URL 表示资源，用 HTTP 方法表示操作。<code>GET /users</code> 查所有用户，<code>POST /users</code> 创建用户</td><td>图书馆的检索规则</td></tr><tr><td><strong>MVC &#x2F; MTV</strong></td><td>Model-View-Controller：模型管数据、视图管展示、控制器管逻辑。Django 用 MTV（Model-Template-View），本质一样</td><td>餐厅的后厨&#x2F;菜单&#x2F;服务员</td></tr><tr><td><strong>ORM</strong></td><td>Object-Relational Mapping：用 Python 对象操作数据库，不用手写 SQL。<code>User.objects.filter(age__gt=18)</code> 等价于 <code>SELECT * FROM users WHERE age &gt; 18</code></td><td>翻译官：Python ↔ SQL</td></tr><tr><td><strong>ASGI &#x2F; WSGI</strong></td><td>Python Web 服务器接口标准。WSGI 是同步的，ASGI 是异步的（支持 WebSocket）。FastAPI 用 ASGI，Django 两种都支持</td><td>餐厅的前台接单方式</td></tr><tr><td><strong>JWT</strong></td><td>JSON Web Token：无状态认证方案。用户登录后服务器签发一个加密令牌，客户端每次请求带上令牌证明身份</td><td>电影票：买一次，进场查验</td></tr><tr><td><strong>CORS</strong></td><td>Cross-Origin Resource Sharing：浏览器安全策略，默认禁止跨域请求。前后端分离时必须配置</td><td>小区的门禁：外人需要登记才能进</td></tr><tr><td><strong>中间件</strong></td><td>在请求到达路由之前&#x2F;响应返回之后执行的通用逻辑（如日志、认证、CORS），类似管道</td><td>机场安检：所有人都必须经过</td></tr></tbody></table><p><strong>HTTP 请求方法与 CRUD 对应</strong>：</p><figure class="highlight plaintext"><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><span class="line">HTTP 方法   │ CRUD 操作  │ 示例                │ 幂等性</span><br><span class="line">────────────┼────────────┼─────────────────────┼────────</span><br><span class="line">GET         │ 读取 Read  │ GET /users/1        │ ✅ 多次调用结果不变</span><br><span class="line">POST        │ 创建 Create│ POST /users         │ ❌ 每次创建一个新资源</span><br><span class="line">PUT         │ 全量更新   │ PUT /users/1        │ ✅</span><br><span class="line">PATCH       │ 部分更新   │ PATCH /users/1      │ ✅</span><br><span class="line">DELETE      │ 删除 Delete│ DELETE /users/1     │ ✅</span><br></pre></td></tr></table></figure><p><strong>RESTful API 设计示例</strong>：</p><figure class="highlight plaintext"><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><span class="line">资源：用户（users）</span><br><span class="line"></span><br><span class="line">GET    /users           → 获取用户列表</span><br><span class="line">GET    /users/1         → 获取 ID=1 的用户详情</span><br><span class="line">POST   /users           → 创建新用户</span><br><span class="line">PUT    /users/1         → 全量更新 ID=1 的用户</span><br><span class="line">PATCH  /users/1         → 部分更新（如只改邮箱）</span><br><span class="line">DELETE /users/1         → 删除 ID=1 的用户</span><br><span class="line">GET    /users/1/items   → 获取 ID=1 用户的所有物品（嵌套资源）</span><br></pre></td></tr></table></figure><p><strong>常见 HTTP 状态码</strong>：</p><figure class="highlight plaintext"><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><span class="line">2xx 成功：</span><br><span class="line">  200 OK              - 请求成功</span><br><span class="line">  201 Created         - 资源创建成功（POST 请求）</span><br><span class="line">  204 No Content      - 成功但无返回内容（DELETE 请求）</span><br><span class="line"></span><br><span class="line">4xx 客户端错误：</span><br><span class="line">  400 Bad Request     - 请求参数有误</span><br><span class="line">  401 Unauthorized    - 未认证（没登录或 token 无效）</span><br><span class="line">  403 Forbidden       - 已认证但无权限</span><br><span class="line">  404 Not Found       - 资源不存在</span><br><span class="line">  422 Unprocessable   - 数据验证失败（FastAPI 常用）</span><br><span class="line"></span><br><span class="line">5xx 服务端错误：</span><br><span class="line">  500 Internal Error  - 服务器内部错误</span><br><span class="line">  502 Bad Gateway     - 网关/代理无法连接上游</span><br><span class="line">  503 Service Unavail - 服务暂不可用（过载或维护）</span><br></pre></td></tr></table></figure><hr><h3 id="1-5-部署实战"><a href="#1-5-部署实战" class="headerlink" title="1.5 部署实战"></a>1.5 部署实战</h3><blockquote><p>开发环境跑通了，怎么部署到服务器？最主流的方式是 <strong>Docker + Nginx</strong>。</p></blockquote><h4 id="5-1-Docker-部署-FastAPI"><a href="#5-1-Docker-部署-FastAPI" class="headerlink" title="5.1 Docker 部署 FastAPI"></a>5.1 Docker 部署 FastAPI</h4><p><strong><code>Dockerfile</code></strong>：</p><figure class="highlight dockerfile"><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><span class="line"><span class="comment"># 第一阶段：构建依赖</span></span><br><span class="line"><span class="keyword">FROM</span> python:<span class="number">3.12</span>-slim AS builder</span><br><span class="line"></span><br><span class="line"><span class="keyword">WORKDIR</span><span class="language-bash"> /app</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 先复制依赖文件，利用 Docker 缓存层（依赖不变时无需重新安装）</span></span><br><span class="line"><span class="keyword">COPY</span><span class="language-bash"> requirements.txt .</span></span><br><span class="line"><span class="keyword">RUN</span><span class="language-bash"> pip install --no-cache-dir -r requirements.txt</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 第二阶段：运行</span></span><br><span class="line"><span class="keyword">FROM</span> python:<span class="number">3.12</span>-slim</span><br><span class="line"></span><br><span class="line"><span class="keyword">WORKDIR</span><span class="language-bash"> /app</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 从构建阶段复制已安装的包</span></span><br><span class="line"><span class="keyword">COPY</span><span class="language-bash"> --from=builder /usr/local/lib/python3.12/site-packages /usr/local/lib/python3.12/site-packages</span></span><br><span class="line"><span class="keyword">COPY</span><span class="language-bash"> --from=builder /usr/local/bin /usr/local/bin</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 复制项目代码</span></span><br><span class="line"><span class="keyword">COPY</span><span class="language-bash"> app/ ./app/</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 非 root 用户运行（安全最佳实践）</span></span><br><span class="line"><span class="keyword">RUN</span><span class="language-bash"> useradd --create-home appuser</span></span><br><span class="line"><span class="keyword">USER</span> appuser</span><br><span class="line"></span><br><span class="line"><span class="comment"># 暴露端口</span></span><br><span class="line"><span class="keyword">EXPOSE</span> <span class="number">8000</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 启动命令：生产环境不用 --reload</span></span><br><span class="line"><span class="keyword">CMD</span><span class="language-bash"> [<span class="string">&quot;uvicorn&quot;</span>, <span class="string">&quot;app.main:app&quot;</span>, <span class="string">&quot;--host&quot;</span>, <span class="string">&quot;0.0.0.0&quot;</span>, <span class="string">&quot;--port&quot;</span>, <span class="string">&quot;8000&quot;</span>, <span class="string">&quot;--workers&quot;</span>, <span class="string">&quot;4&quot;</span>]</span></span><br></pre></td></tr></table></figure><p><strong><code>docker-compose.yml</code></strong>（FastAPI + PostgreSQL）：</p><figure class="highlight yaml"><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><span class="line"><span class="attr">version:</span> <span class="string">&quot;3.9&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="attr">services:</span></span><br><span class="line">  <span class="attr">db:</span></span><br><span class="line">    <span class="attr">image:</span> <span class="string">postgres:16-alpine</span></span><br><span class="line">    <span class="attr">environment:</span></span><br><span class="line">      <span class="attr">POSTGRES_DB:</span> <span class="string">myapp</span></span><br><span class="line">      <span class="attr">POSTGRES_USER:</span> <span class="string">myapp</span></span><br><span class="line">      <span class="attr">POSTGRES_PASSWORD:</span> <span class="string">changeme</span>  <span class="comment"># 生产环境用 Docker secrets 或 .env</span></span><br><span class="line">    <span class="attr">volumes:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">pgdata:/var/lib/postgresql/data</span></span><br><span class="line">    <span class="attr">healthcheck:</span></span><br><span class="line">      <span class="attr">test:</span> [<span class="string">&quot;CMD-SHELL&quot;</span>, <span class="string">&quot;pg_isready -U myapp&quot;</span>]</span><br><span class="line">      <span class="attr">interval:</span> <span class="string">5s</span></span><br><span class="line">      <span class="attr">timeout:</span> <span class="string">3s</span></span><br><span class="line">      <span class="attr">retries:</span> <span class="number">5</span></span><br><span class="line"></span><br><span class="line">  <span class="attr">api:</span></span><br><span class="line">    <span class="attr">build:</span> <span class="string">.</span></span><br><span class="line">    <span class="attr">ports:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">&quot;8000:8000&quot;</span></span><br><span class="line">    <span class="attr">environment:</span></span><br><span class="line">      <span class="attr">DATABASE_URL:</span> <span class="string">postgresql://myapp:changeme@db:5432/myapp</span></span><br><span class="line">      <span class="attr">SECRET_KEY:</span> <span class="string">$&#123;SECRET_KEY&#125;</span>  <span class="comment"># 从 .env 文件读取</span></span><br><span class="line">    <span class="attr">depends_on:</span></span><br><span class="line">      <span class="attr">db:</span></span><br><span class="line">        <span class="attr">condition:</span> <span class="string">service_healthy</span></span><br><span class="line">    <span class="attr">restart:</span> <span class="string">unless-stopped</span></span><br><span class="line"></span><br><span class="line"><span class="attr">volumes:</span></span><br><span class="line">  <span class="attr">pgdata:</span></span><br></pre></td></tr></table></figure><h4 id="5-2-Nginx-反向代理配置"><a href="#5-2-Nginx-反向代理配置" class="headerlink" title="5.2 Nginx 反向代理配置"></a>5.2 Nginx 反向代理配置</h4><figure class="highlight nginx"><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><span class="line"><span class="comment"># /etc/nginx/sites-available/myapp</span></span><br><span class="line"></span><br><span class="line"><span class="section">server</span> &#123;</span><br><span class="line">    <span class="attribute">listen</span> <span class="number">80</span>;</span><br><span class="line">    <span class="attribute">server_name</span> yourdomain.com;</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 将所有请求转发到 FastAPI（Docker 容器）</span></span><br><span class="line">    <span class="section">location</span> / &#123;</span><br><span class="line">        <span class="attribute">proxy_pass</span> http://127.0.0.1:8000;</span><br><span class="line">        <span class="attribute">proxy_set_header</span> Host <span class="variable">$host</span>;</span><br><span class="line">        <span class="attribute">proxy_set_header</span> X-Real-IP <span class="variable">$remote_addr</span>;</span><br><span class="line">        <span class="attribute">proxy_set_header</span> X-Forwarded-For <span class="variable">$proxy_add_x_forwarded_for</span>;</span><br><span class="line">        <span class="attribute">proxy_set_header</span> X-Forwarded-Proto <span class="variable">$scheme</span>;</span><br><span class="line"></span><br><span class="line">        <span class="comment"># WebSocket 支持（如果用 FastAPI WebSocket）</span></span><br><span class="line">        <span class="attribute">proxy_http_version</span> <span class="number">1</span>.<span class="number">1</span>;</span><br><span class="line">        <span class="attribute">proxy_set_header</span> Upgrade <span class="variable">$http_upgrade</span>;</span><br><span class="line">        <span class="attribute">proxy_set_header</span> Connection <span class="string">&quot;upgrade&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 静态文件直接由 Nginx 处理（更快）</span></span><br><span class="line">    <span class="section">location</span> /static/ &#123;</span><br><span class="line">        <span class="attribute">alias</span> /app/static/;</span><br><span class="line">        <span class="attribute">expires</span> <span class="number">30d</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="5-3-完整部署命令"><a href="#5-3-完整部署命令" class="headerlink" title="5.3 完整部署命令"></a>5.3 完整部署命令</h4><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><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 1. 在服务器上克隆项目</span></span><br><span class="line">git <span class="built_in">clone</span> https://github.com/yourname/fastapi-demo.git</span><br><span class="line"><span class="built_in">cd</span> fastapi-demo</span><br><span class="line"></span><br><span class="line"><span class="comment"># 2. 创建 .env 文件存放敏感配置</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;SECRET_KEY=<span class="subst">$(python -c &#x27;import secrets; print(secrets.token_urlsafe(32)</span>)&#x27;)&quot;</span> &gt; .<span class="built_in">env</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 3. 构建并启动容器</span></span><br><span class="line">docker compose up -d --build</span><br><span class="line"></span><br><span class="line"><span class="comment"># 4. 查看日志</span></span><br><span class="line">docker compose logs -f api</span><br><span class="line"></span><br><span class="line"><span class="comment"># 5. 配置 Nginx</span></span><br><span class="line"><span class="built_in">sudo</span> <span class="built_in">cp</span> nginx.conf /etc/nginx/sites-available/myapp</span><br><span class="line"><span class="built_in">sudo</span> <span class="built_in">ln</span> -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/</span><br><span class="line"><span class="built_in">sudo</span> nginx -t          <span class="comment"># 测试配置语法</span></span><br><span class="line"><span class="built_in">sudo</span> systemctl reload nginx</span><br><span class="line"></span><br><span class="line"><span class="comment"># 6. （可选）配置 HTTPS（Let&#x27;s Encrypt 免费证书）</span></span><br><span class="line"><span class="built_in">sudo</span> apt install certbot python3-certbot-nginx</span><br><span class="line"><span class="built_in">sudo</span> certbot --nginx -d yourdomain.com</span><br></pre></td></tr></table></figure><hr><h3 id="1-6-常见新手坑"><a href="#1-6-常见新手坑" class="headerlink" title="1.6 常见新手坑"></a>1.6 常见新手坑</h3><table><thead><tr><th>#</th><th>坑</th><th>现象</th><th>解决方法</th></tr></thead><tbody><tr><td>1</td><td><strong>不用虚拟环境</strong></td><td>全局装了一堆包，项目之间互相冲突，<code>pip install</code> 报错</td><td>每个项目都 <code>python -m venv .venv</code>，养成习惯</td></tr><tr><td>2</td><td><strong>密码存明文</strong></td><td>数据库泄露后所有用户密码暴露</td><td>永远用 <code>passlib</code> + bcrypt 哈希后存储</td></tr><tr><td>3</td><td><strong>SECRET_KEY 硬编码</strong></td><td>代码推到 GitHub，密钥泄露，JWT 可被伪造</td><td>从环境变量读取：<code>os.getenv(&quot;SECRET_KEY&quot;)</code>，<code>.env</code> 文件加入 <code>.gitignore</code></td></tr><tr><td>4</td><td><strong>CORS 不配置</strong></td><td>前端请求后端报跨域错误</td><td>添加 <code>CORSMiddleware</code>，生产环境限制 <code>allow_origins</code> 为具体域名</td></tr><tr><td>5</td><td><strong>数据库会话不关闭</strong></td><td>请求越来越多，数据库连接耗尽，服务假死</td><td>用 <code>get_db()</code> 依赖注入确保会话关闭，或用 <code>try/finally</code></td></tr><tr><td>6</td><td><strong>生产环境用 <code>--reload</code></strong></td><td>uvicorn 开了 <code>--reload</code>，性能差且文件保存可能触发意外重启</td><td>生产环境去掉 <code>--reload</code>，增加 <code>--workers</code> 多进程</td></tr><tr><td>7</td><td><strong>不写 .gitignore</strong></td><td><code>db.sqlite3</code>、<code>.env</code>、<code>__pycache__</code> 全提交了</td><td>标准模板：<code>__pycache__/</code>、<code>.venv/</code>、<code>*.db</code>、<code>.env</code>、<code>*.pyc</code></td></tr><tr><td>8</td><td><strong>不用 Alembic 迁移</strong></td><td>直接改模型然后 <code>create_all</code>，线上数据丢失</td><td>用 <code>alembic revision --autogenerate</code> + <code>alembic upgrade head</code></td></tr><tr><td>9</td><td><strong>Django 的 DEBUG&#x3D;True 上线</strong></td><td>报错时页面显示完整堆栈和配置，泄露敏感信息</td><td><code>settings.py</code> 中 <code>DEBUG = False</code>，配置 <code>ALLOWED_HOSTS</code></td></tr><tr><td>10</td><td><strong>不理解同步 vs 异步</strong></td><td>在 FastAPI 里用 <code>time.sleep()</code> 或同步 ORM 查询，整个服务阻塞</td><td>FastAPI 中用 <code>await asyncio.sleep()</code>；SQLAlchemy 查询用 <code>run_in_executor</code> 或换 async 版本</td></tr></tbody></table><hr><h2 id="2-数据科学与分析"><a href="#2-数据科学与分析" class="headerlink" title="2. 数据科学与分析"></a>2. 数据科学与分析</h2><h3 id="2-1-数据科学是什么？工作内容"><a href="#2-1-数据科学是什么？工作内容" class="headerlink" title="2.1 数据科学是什么？工作内容"></a>2.1 数据科学是什么？工作内容</h3><p>数据科学简单说就是<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><span class="line">提出问题 → 获取数据 → 清洗整理数据 → 分析建模 → 输出结论/报告</span><br></pre></td></tr></table></figure><p>举几个真实场景：</p><table><thead><tr><th>场景</th><th>你要回答的问题</th><th>用到的工具</th></tr></thead><tbody><tr><td>电商运营</td><td>哪个品类利润最高？用户复购率多少？</td><td>Pandas + Matplotlib</td></tr><tr><td>金融风控</td><td>这笔贷款违约概率多大？</td><td>Scikit-learn + XGBoost</td></tr><tr><td>产品迭代</td><td>A&#x2F;B 测试哪个方案转化率高？</td><td>SciPy（统计检验）</td></tr><tr><td>用户画像</td><td>核心用户长什么样？</td><td>Pandas 分组聚合 + Seaborn</td></tr></tbody></table><p><strong>数据科学 vs 数据分析 vs 机器学习</strong>：</p><ul><li><strong>数据分析</strong>：侧重描述”发生了什么”，以报表、可视化为主</li><li><strong>数据科学</strong>：在分析基础上加入建模，预测”将会发生什么”</li><li><strong>机器学习</strong>：更偏算法和模型，是数据科学的一个子方向</li></ul><p>新手入门的<strong>最短路径</strong>：先学好 NumPy → Pandas → Matplotlib，就能完成 80% 的日常工作。</p><hr><h3 id="2-2-从零上手：环境搭建"><a href="#2-2-从零上手：环境搭建" class="headerlink" title="2.2 从零上手：环境搭建"></a>2.2 从零上手：环境搭建</h3><h4 id="安装-Anaconda-Miniconda"><a href="#安装-Anaconda-Miniconda" class="headerlink" title="安装 Anaconda &#x2F; Miniconda"></a>安装 Anaconda &#x2F; Miniconda</h4><p>Anaconda 是数据科学的一站式发行版，自带 Python + NumPy + Pandas + Jupyter 等 200+ 包。<br>Miniconda 是精简版，只带 conda 包管理器，其余按需安装。</p><p><strong>Windows 安装步骤</strong>：</p><ol><li>下载：<a href="https://www.anaconda.com/download">https://www.anaconda.com/download</a>（选 Windows 版本）</li><li>双击安装，建议<strong>不要勾选</strong> “Add Anaconda to my PATH”（让 Anaconda 自己管理环境）</li><li>安装完成后，打开 <strong>Anaconda Prompt</strong>（不是普通 CMD）</li></ol><p><strong>Miniconda 安装</strong>（推荐给磁盘空间有限的同学）：</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><span class="line"><span class="comment"># 下载地址 https://docs.conda.io/en/latest/miniconda.html</span></span><br><span class="line"><span class="comment"># 安装完后创建独立环境</span></span><br><span class="line">conda create -n datascience python=3.11 -y</span><br><span class="line">conda activate datascience</span><br><span class="line"><span class="comment"># 安装核心包</span></span><br><span class="line">conda install numpy pandas matplotlib seaborn jupyter -y</span><br></pre></td></tr></table></figure><h4 id="验证安装"><a href="#验证安装" class="headerlink" title="验证安装"></a>验证安装</h4><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></pre></td><td class="code"><pre><span class="line">python -c <span class="string">&quot;import numpy; print(&#x27;NumPy&#x27;, numpy.__version__)&quot;</span></span><br><span class="line">python -c <span class="string">&quot;import pandas;  print(&#x27;Pandas&#x27;, pandas.__version__)&quot;</span></span><br><span class="line">python -c <span class="string">&quot;import matplotlib; print(&#x27;Matplotlib&#x27;, matplotlib.__version__)&quot;</span></span><br></pre></td></tr></table></figure><h4 id="Jupyter-Notebook-使用"><a href="#Jupyter-Notebook-使用" class="headerlink" title="Jupyter Notebook 使用"></a>Jupyter Notebook 使用</h4><p>Jupyter 是数据科学的<strong>主力 IDE</strong>——代码、图表、文字混排，就像一本交互式笔记本。</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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 启动（在目标目录下运行）</span></span><br><span class="line">jupyter notebook</span><br><span class="line"></span><br><span class="line"><span class="comment"># 或者用 JupyterLab（下一代界面）</span></span><br><span class="line">jupyter lab</span><br></pre></td></tr></table></figure><p><strong>常用快捷键</strong>（在命令模式下，按 <code>Esc</code> 进入命令模式）：</p><table><thead><tr><th>快捷键</th><th>作用</th></tr></thead><tbody><tr><td><code>Enter</code></td><td>进入编辑模式</td></tr><tr><td><code>Shift+Enter</code></td><td>运行当前单元格，跳到下一个</td></tr><tr><td><code>Ctrl+Enter</code></td><td>运行当前单元格，不跳转</td></tr><tr><td><code>A</code></td><td>在当前单元格上方插入新单元格</td></tr><tr><td><code>B</code></td><td>在当前单元格下方插入新单元格</td></tr><tr><td><code>DD</code></td><td>删除当前单元格</td></tr><tr><td><code>M</code></td><td>将单元格切换为 Markdown</td></tr><tr><td><code>Y</code></td><td>将单元格切换为 Code</td></tr></tbody></table><blockquote><p><strong>提示</strong>：Jupyter 中按 <code>Tab</code> 可自动补全，按 <code>Shift+Tab</code> 在括号内可查看函数文档。</p></blockquote><hr><h3 id="2-3-NumPy-详细教程"><a href="#2-3-NumPy-详细教程" class="headerlink" title="2.3 NumPy 详细教程"></a>2.3 NumPy 详细教程</h3><p>NumPy（Numerical Python）是所有数据科学库的基石，Pandas、Scikit-learn 底层都依赖它。</p><h4 id="数组创建"><a href="#数组创建" class="headerlink" title="数组创建"></a>数组创建</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"></span><br><span class="line"><span class="comment"># ── 1. 从 Python 列表创建 ──────────────────────────</span></span><br><span class="line">a1 = np.array([<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>])            <span class="comment"># 一维数组</span></span><br><span class="line">a2 = np.array([[<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>],              <span class="comment"># 二维数组（2行3列）</span></span><br><span class="line">               [<span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>]])</span><br><span class="line"><span class="built_in">print</span>(a1)            <span class="comment"># [1 2 3 4]</span></span><br><span class="line"><span class="built_in">print</span>(a2)</span><br><span class="line"><span class="comment"># [[1 2 3]</span></span><br><span class="line"><span class="comment">#  [4 5 6]]</span></span><br><span class="line"><span class="built_in">print</span>(a2.shape)      <span class="comment"># (2, 3)  —— 2行3列</span></span><br><span class="line"><span class="built_in">print</span>(a2.dtype)      <span class="comment"># int64   —— 元素类型</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ── 2. 用内置函数快速创建 ──────────────────────────</span></span><br><span class="line">zeros  = np.zeros((<span class="number">3</span>, <span class="number">4</span>))              <span class="comment"># 3行4列，全0</span></span><br><span class="line">ones   = np.ones((<span class="number">2</span>, <span class="number">3</span>))               <span class="comment"># 2行3列，全1</span></span><br><span class="line">full   = np.full((<span class="number">2</span>, <span class="number">2</span>), <span class="number">7</span>)            <span class="comment"># 2行2列，全填7</span></span><br><span class="line">eye    = np.eye(<span class="number">3</span>)                     <span class="comment"># 3×3 单位矩阵</span></span><br><span class="line">arange = np.arange(<span class="number">0</span>, <span class="number">10</span>, <span class="number">2</span>)           <span class="comment"># [0 2 4 6 8]，步长2</span></span><br><span class="line">linspace = np.linspace(<span class="number">0</span>, <span class="number">1</span>, <span class="number">5</span>)        <span class="comment"># [0. 0.25 0.5 0.75 1.]，等分5个点</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(arange)    <span class="comment"># [0 2 4 6 8]</span></span><br><span class="line"><span class="built_in">print</span>(linspace)  <span class="comment"># [0.   0.25 0.5  0.75 1.  ]</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ── 3. 随机数创建 ──────────────────────────────────</span></span><br><span class="line">np.random.seed(<span class="number">42</span>)                     <span class="comment"># 固定随机种子，结果可复现</span></span><br><span class="line">r1 = np.random.rand(<span class="number">3</span>, <span class="number">2</span>)             <span class="comment"># 3×2，[0,1)均匀分布</span></span><br><span class="line">r2 = np.random.randn(<span class="number">3</span>, <span class="number">2</span>)            <span class="comment"># 3×2，标准正态分布</span></span><br><span class="line">r3 = np.random.randint(<span class="number">1</span>, <span class="number">100</span>, size=(<span class="number">2</span>, <span class="number">5</span>))  <span class="comment"># 2×5，[1,100)随机整数</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(r3)</span><br><span class="line"><span class="comment"># 示例输出：</span></span><br><span class="line"><span class="comment"># [[52 93 15 72 61]</span></span><br><span class="line"><span class="comment">#  [21 83 87 75 75]]</span></span><br></pre></td></tr></table></figure><h4 id="数组运算（加减乘除、广播机制）"><a href="#数组运算（加减乘除、广播机制）" class="headerlink" title="数组运算（加减乘除、广播机制）"></a>数组运算（加减乘除、广播机制）</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"></span><br><span class="line">a = np.array([<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>])</span><br><span class="line">b = np.array([<span class="number">10</span>, <span class="number">20</span>, <span class="number">30</span>, <span class="number">40</span>])</span><br><span class="line"></span><br><span class="line"><span class="comment"># ── 逐元素运算 ──────────────────────────────────────</span></span><br><span class="line"><span class="built_in">print</span>(a + b)     <span class="comment"># [11 22 33 44]</span></span><br><span class="line"><span class="built_in">print</span>(a - b)     <span class="comment"># [ -9 -18 -27 -36]</span></span><br><span class="line"><span class="built_in">print</span>(a * b)     <span class="comment"># [ 10  40  90 160]  —— 注意：这是逐元素相乘，不是矩阵乘法</span></span><br><span class="line"><span class="built_in">print</span>(b / a)     <span class="comment"># [10. 10. 10. 10.]</span></span><br><span class="line"><span class="built_in">print</span>(a ** <span class="number">2</span>)    <span class="comment"># [ 1  4  9 16]      —— 逐元素平方</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ── 标量运算（广播机制）──────────────────────────────</span></span><br><span class="line"><span class="built_in">print</span>(a * <span class="number">10</span>)    <span class="comment"># [10 20 30 40]   —— 标量自动&quot;广播&quot;到每个元素</span></span><br><span class="line"><span class="built_in">print</span>(a + <span class="number">100</span>)   <span class="comment"># [101 102 103 104]</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ── 广播机制详解 ────────────────────────────────────</span></span><br><span class="line"><span class="comment"># 形状不同的数组也能运算，NumPy 会自动&quot;扩展&quot;小数组</span></span><br><span class="line">c = np.array([[<span class="number">1</span>], [<span class="number">2</span>], [<span class="number">3</span>]])   <span class="comment"># 形状 (3, 1)</span></span><br><span class="line">d = np.array([<span class="number">10</span>, <span class="number">20</span>, <span class="number">30</span>])     <span class="comment"># 形状 (3,)</span></span><br><span class="line"><span class="built_in">print</span>(c + d)</span><br><span class="line"><span class="comment"># [[11 21 31]</span></span><br><span class="line"><span class="comment">#  [12 22 32]</span></span><br><span class="line"><span class="comment">#  [13 23 33]]</span></span><br><span class="line"><span class="comment"># c 被广播为 (3,3)，d 也被广播为 (3,3)</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ── 常用数学函数 ────────────────────────────────────</span></span><br><span class="line">x = np.array([<span class="number">0</span>, np.pi/<span class="number">2</span>, np.pi])</span><br><span class="line"><span class="built_in">print</span>(np.sin(x))     <span class="comment"># [0.000e+00 1.000e+00 1.224e-16]（≈0,1,0）</span></span><br><span class="line"><span class="built_in">print</span>(np.sqrt(a))    <span class="comment"># [1.    1.414 1.732 2.   ]</span></span><br><span class="line"><span class="built_in">print</span>(np.exp(a))     <span class="comment"># [ 2.718  7.389 20.086 54.598]</span></span><br><span class="line"><span class="built_in">print</span>(np.log(a))     <span class="comment"># [0.    0.693 1.099 1.386]</span></span><br></pre></td></tr></table></figure><h4 id="索引和切片"><a href="#索引和切片" class="headerlink" title="索引和切片"></a>索引和切片</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"></span><br><span class="line">arr = np.array([</span><br><span class="line">    [<span class="number">10</span>, <span class="number">20</span>, <span class="number">30</span>, <span class="number">40</span>],</span><br><span class="line">    [<span class="number">50</span>, <span class="number">60</span>, <span class="number">70</span>, <span class="number">80</span>],</span><br><span class="line">    [<span class="number">90</span>, <span class="number">100</span>, <span class="number">110</span>, <span class="number">120</span>]</span><br><span class="line">])</span><br><span class="line"><span class="comment"># arr 形状 (3, 4)</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ── 基础索引 ────────────────────────────────────────</span></span><br><span class="line"><span class="built_in">print</span>(arr[<span class="number">0</span>])         <span class="comment"># [10 20 30 40]     —— 第0行</span></span><br><span class="line"><span class="built_in">print</span>(arr[<span class="number">1</span>, <span class="number">2</span>])      <span class="comment"># 70                —— 第1行第2列</span></span><br><span class="line"><span class="built_in">print</span>(arr[-<span class="number">1</span>])        <span class="comment"># [ 90 100 110 120] —— 最后一行</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ── 切片 ────────────────────────────────────────────</span></span><br><span class="line"><span class="built_in">print</span>(arr[<span class="number">0</span>:<span class="number">2</span>])       <span class="comment"># 前两行</span></span><br><span class="line"><span class="comment"># [[10 20 30 40]</span></span><br><span class="line"><span class="comment">#  [50 60 70 80]]</span></span><br><span class="line"><span class="built_in">print</span>(arr[:, <span class="number">1</span>])      <span class="comment"># [ 20  60 100]     —— 所有行的第1列</span></span><br><span class="line"><span class="built_in">print</span>(arr[<span class="number">1</span>:, <span class="number">2</span>:])    <span class="comment"># 第1行往后、第2列往右</span></span><br><span class="line"><span class="comment"># [[ 70  80]</span></span><br><span class="line"><span class="comment">#  [110 120]]</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ── 布尔索引（非常常用！）────────────────────────────</span></span><br><span class="line"><span class="built_in">print</span>(arr[arr &gt; <span class="number">50</span>])  <span class="comment"># [ 60  70  80  90 100 110 120] —— 所有大于50的元素</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ── 花式索引（用数组做下标）─────────────────────────</span></span><br><span class="line"><span class="built_in">print</span>(arr[[<span class="number">0</span>, <span class="number">2</span>]])    <span class="comment"># 第0行和第2行</span></span><br><span class="line"><span class="comment"># [[ 10  20  30  40]</span></span><br><span class="line"><span class="comment">#  [ 90 100 110 120]]</span></span><br></pre></td></tr></table></figure><h4 id="统计函数"><a href="#统计函数" class="headerlink" title="统计函数"></a>统计函数</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"></span><br><span class="line">data = np.array([<span class="number">23</span>, <span class="number">45</span>, <span class="number">12</span>, <span class="number">67</span>, <span class="number">34</span>, <span class="number">89</span>, <span class="number">56</span>, <span class="number">78</span>, <span class="number">11</span>, <span class="number">90</span>])</span><br><span class="line"></span><br><span class="line"><span class="comment"># ── 基础统计量 ──────────────────────────────────────</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;均值:&quot;</span>,   np.mean(data))      <span class="comment"># 50.5</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;中位数:&quot;</span>, np.median(data))    <span class="comment"># 50.5</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;标准差:&quot;</span>, np.std(data))       <span class="comment"># 27.49...</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;方差:&quot;</span>,   np.var(data))       <span class="comment"># 755.65</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;最小值:&quot;</span>, np.<span class="built_in">min</span>(data))       <span class="comment"># 11</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;最大值:&quot;</span>, np.<span class="built_in">max</span>(data))       <span class="comment"># 90</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;求和:&quot;</span>,   np.<span class="built_in">sum</span>(data))       <span class="comment"># 505</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ── 分位数 ──────────────────────────────────────────</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;25%分位:&quot;</span>, np.percentile(data, <span class="number">25</span>))  <span class="comment"># 23.0</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;75%分位:&quot;</span>, np.percentile(data, <span class="number">75</span>))  <span class="comment"># 78.0</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ── 二维数组按轴统计 ────────────────────────────────</span></span><br><span class="line">scores = np.array([</span><br><span class="line">    [<span class="number">80</span>, <span class="number">90</span>, <span class="number">70</span>],   <span class="comment"># 语文、数学、英语</span></span><br><span class="line">    [<span class="number">60</span>, <span class="number">85</span>, <span class="number">95</span>],</span><br><span class="line">    [<span class="number">75</span>, <span class="number">88</span>, <span class="number">82</span>]</span><br><span class="line">])</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;每科平均分（按列）:&quot;</span>, np.mean(scores, axis=<span class="number">0</span>))  <span class="comment"># [71.67 87.67 82.33]</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;每人平均分（按行）:&quot;</span>, np.mean(scores, axis=<span class="number">1</span>))  <span class="comment"># [80.   80.   81.67]</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ── 累计运算 ────────────────────────────────────────</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;累计求和:&quot;</span>, np.cumsum(data))   <span class="comment"># [ 23  68  80 147 181 270 326 404 415 505]</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;累计乘积:&quot;</span>, np.cumprod(data[:<span class="number">5</span>]))  <span class="comment"># 前5个元素的累计乘积</span></span><br></pre></td></tr></table></figure><h4 id="矩阵运算"><a href="#矩阵运算" class="headerlink" title="矩阵运算"></a>矩阵运算</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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"></span><br><span class="line">A = np.array([[<span class="number">1</span>, <span class="number">2</span>],</span><br><span class="line">              [<span class="number">3</span>, <span class="number">4</span>]])</span><br><span class="line">B = np.array([[<span class="number">5</span>, <span class="number">6</span>],</span><br><span class="line">              [<span class="number">7</span>, <span class="number">8</span>]])</span><br><span class="line"></span><br><span class="line"><span class="comment"># ── 矩阵乘法（点乘）────────────────────────────────</span></span><br><span class="line"><span class="comment"># 注意：A * B 是逐元素相乘，A @ B 才是矩阵乘法</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;逐元素相乘:\n&quot;</span>, A * B)</span><br><span class="line"><span class="comment"># [[ 5 12]</span></span><br><span class="line"><span class="comment">#  [21 32]]</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;矩阵乘法:\n&quot;</span>, A @ B)</span><br><span class="line"><span class="comment"># [[19 22]</span></span><br><span class="line"><span class="comment">#  [43 50]]</span></span><br><span class="line"><span class="comment"># 等价写法：np.dot(A, B) 或 A.dot(B)</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ── 转置 ────────────────────────────────────────────</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;A 的转置:\n&quot;</span>, A.T)</span><br><span class="line"><span class="comment"># [[1 3]</span></span><br><span class="line"><span class="comment">#  [2 4]]</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ── 逆矩阵 ────────────────────────────────────────</span></span><br><span class="line">A_inv = np.linalg.inv(A)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;A 的逆矩阵:\n&quot;</span>, A_inv)</span><br><span class="line"><span class="comment"># [[-2.   1. ]</span></span><br><span class="line"><span class="comment">#  [ 1.5 -0.5]]</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 验证：A @ A_inv 应该等于单位矩阵</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;A @ A_inv:\n&quot;</span>, np.<span class="built_in">round</span>(A @ A_inv))</span><br><span class="line"><span class="comment"># [[1. 0.]</span></span><br><span class="line"><span class="comment">#  [0. 1.]]</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ── 行列式 ──────────────────────────────────────────</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;A 的行列式:&quot;</span>, np.linalg.det(A))  <span class="comment"># -2.0</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ── 特征值和特征向量 ────────────────────────────────</span></span><br><span class="line">eigenvalues, eigenvectors = np.linalg.eig(A)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;特征值:&quot;</span>, eigenvalues)   <span class="comment"># [-0.372  5.372]</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ── 解线性方程组 Ax = b ────────────────────────────</span></span><br><span class="line">b = np.array([<span class="number">1</span>, <span class="number">2</span>])</span><br><span class="line">x = np.linalg.solve(A, b)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;方程组解 x =&quot;</span>, x)       <span class="comment"># [-1.110e-16  5.000e-01]（≈ [0, 0.5]）</span></span><br></pre></td></tr></table></figure><hr><h3 id="2-4-Pandas-详细教程"><a href="#2-4-Pandas-详细教程" class="headerlink" title="2.4 Pandas 详细教程"></a>2.4 Pandas 详细教程</h3><p>Pandas 是数据分析的<strong>核心武器</strong>，90% 的数据处理工作都靠它完成。核心数据结构：</p><ul><li><strong>Series</strong>：一维数组（带标签）</li><li><strong>DataFrame</strong>：二维表格（带行标签和列标签），类似 Excel 表</li></ul><h4 id="创建和读取数据"><a href="#创建和读取数据" class="headerlink" title="创建和读取数据"></a>创建和读取数据</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><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><span class="line"><span class="keyword">import</span> pandas <span class="keyword">as</span> pd</span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"></span><br><span class="line"><span class="comment"># ── 1. 从字典创建 DataFrame ────────────────────────</span></span><br><span class="line">df = pd.DataFrame(&#123;</span><br><span class="line">    <span class="string">&quot;姓名&quot;</span>: [<span class="string">&quot;张三&quot;</span>, <span class="string">&quot;李四&quot;</span>, <span class="string">&quot;王五&quot;</span>, <span class="string">&quot;赵六&quot;</span>, <span class="string">&quot;钱七&quot;</span>],</span><br><span class="line">    <span class="string">&quot;年龄&quot;</span>: [<span class="number">25</span>, <span class="number">30</span>, <span class="number">35</span>, <span class="number">28</span>, <span class="number">40</span>],</span><br><span class="line">    <span class="string">&quot;城市&quot;</span>: [<span class="string">&quot;北京&quot;</span>, <span class="string">&quot;上海&quot;</span>, <span class="string">&quot;广州&quot;</span>, <span class="string">&quot;深圳&quot;</span>, <span class="string">&quot;杭州&quot;</span>],</span><br><span class="line">    <span class="string">&quot;薪资&quot;</span>: [<span class="number">8000</span>, <span class="number">12000</span>, <span class="number">15000</span>, <span class="number">9000</span>, <span class="number">20000</span>],</span><br><span class="line">    <span class="string">&quot;入职年份&quot;</span>: pd.to_datetime([<span class="string">&quot;2020-01-15&quot;</span>, <span class="string">&quot;2019-06-01&quot;</span>, <span class="string">&quot;2018-03-20&quot;</span>, <span class="string">&quot;2021-09-10&quot;</span>, <span class="string">&quot;2017-11-05&quot;</span>])</span><br><span class="line">&#125;)</span><br><span class="line"><span class="built_in">print</span>(df)</span><br><span class="line"><span class="comment">#    姓名  年龄  城市     薪资       入职年份</span></span><br><span class="line"><span class="comment"># 0  张三  25  北京   8000 2020-01-15</span></span><br><span class="line"><span class="comment"># 1  李四  30  上海  12000 2019-06-01</span></span><br><span class="line"><span class="comment"># 2  王五  35  广州  15000 2018-03-20</span></span><br><span class="line"><span class="comment"># 3  赵六  28  深圳   9000 2021-09-10</span></span><br><span class="line"><span class="comment"># 4  钱七  40  杭州  20000 2017-11-05</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ── 2. 从 CSV 读取 ────────────────────────────────</span></span><br><span class="line"><span class="comment"># df = pd.read_csv(&quot;data.csv&quot;, encoding=&quot;utf-8&quot;)</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 常用参数：</span></span><br><span class="line"><span class="comment"># df = pd.read_csv(&quot;data.csv&quot;,</span></span><br><span class="line"><span class="comment">#     encoding=&quot;gbk&quot;,          # 中文文件常用 gbk</span></span><br><span class="line"><span class="comment">#     nrows=1000,              # 只读前1000行（大文件先预览）</span></span><br><span class="line"><span class="comment">#     usecols=[&quot;姓名&quot;,&quot;薪资&quot;], # 只读指定列</span></span><br><span class="line"><span class="comment">#     parse_dates=[&quot;日期&quot;],    # 自动解析日期列</span></span><br><span class="line"><span class="comment">#     na_values=[&quot;NA&quot;,&quot;null&quot;], # 把这些值视为缺失值</span></span><br><span class="line"><span class="comment"># )</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ── 3. 从 Excel 读取 ───────────────────────────────</span></span><br><span class="line"><span class="comment"># df = pd.read_excel(&quot;data.xlsx&quot;, sheet_name=&quot;Sheet1&quot;)</span></span><br><span class="line"><span class="comment"># 需要安装：pip install openpyxl</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ── 4. 从 SQL 数据库读取 ────────────────────────────</span></span><br><span class="line"><span class="comment"># from sqlalchemy import create_engine</span></span><br><span class="line"><span class="comment"># engine = create_engine(&quot;mysql+pymysql://user:password@host:3306/dbname&quot;)</span></span><br><span class="line"><span class="comment"># df = pd.read_sql(&quot;SELECT * FROM employees&quot;, engine)</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ── 5. 保存数据 ────────────────────────────────────</span></span><br><span class="line"><span class="comment"># df.to_csv(&quot;output.csv&quot;, index=False, encoding=&quot;utf-8-sig&quot;)  # utf-8-sig 带 BOM，Excel 打开不乱码</span></span><br><span class="line"><span class="comment"># df.to_excel(&quot;output.xlsx&quot;, index=False)</span></span><br></pre></td></tr></table></figure><h4 id="数据查看（head-info-describe）"><a href="#数据查看（head-info-describe）" class="headerlink" title="数据查看（head &#x2F; info &#x2F; describe）"></a>数据查看（head &#x2F; info &#x2F; describe）</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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> pandas <span class="keyword">as</span> pd</span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"></span><br><span class="line">df = pd.DataFrame(&#123;</span><br><span class="line">    <span class="string">&quot;姓名&quot;</span>: [<span class="string">&quot;张三&quot;</span>, <span class="string">&quot;李四&quot;</span>, <span class="string">&quot;王五&quot;</span>, <span class="string">&quot;赵六&quot;</span>, <span class="string">&quot;钱七&quot;</span>, <span class="string">&quot;孙八&quot;</span>, <span class="string">&quot;周九&quot;</span>, <span class="string">&quot;吴十&quot;</span>],</span><br><span class="line">    <span class="string">&quot;年龄&quot;</span>: [<span class="number">25</span>, <span class="number">30</span>, <span class="number">35</span>, <span class="number">28</span>, <span class="number">40</span>, <span class="number">32</span>, <span class="number">27</span>, <span class="number">45</span>],</span><br><span class="line">    <span class="string">&quot;城市&quot;</span>: [<span class="string">&quot;北京&quot;</span>, <span class="string">&quot;上海&quot;</span>, <span class="string">&quot;广州&quot;</span>, <span class="string">&quot;深圳&quot;</span>, <span class="string">&quot;杭州&quot;</span>, <span class="string">&quot;成都&quot;</span>, <span class="string">&quot;武汉&quot;</span>, <span class="string">&quot;南京&quot;</span>],</span><br><span class="line">    <span class="string">&quot;薪资&quot;</span>: [<span class="number">8000</span>, <span class="number">12000</span>, <span class="number">15000</span>, <span class="number">9000</span>, <span class="number">20000</span>, <span class="number">11000</span>, <span class="number">7500</span>, <span class="number">18000</span>],</span><br><span class="line">    <span class="string">&quot;部门&quot;</span>: [<span class="string">&quot;技术&quot;</span>, <span class="string">&quot;市场&quot;</span>, <span class="string">&quot;技术&quot;</span>, <span class="string">&quot;运营&quot;</span>, <span class="string">&quot;技术&quot;</span>, <span class="string">&quot;市场&quot;</span>, <span class="string">&quot;运营&quot;</span>, <span class="string">&quot;技术&quot;</span>]</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="comment"># ── 预览数据 ────────────────────────────────────────</span></span><br><span class="line"><span class="built_in">print</span>(df.head())        <span class="comment"># 默认看前5行</span></span><br><span class="line"><span class="built_in">print</span>(df.head(<span class="number">3</span>))       <span class="comment"># 只看前3行</span></span><br><span class="line"><span class="built_in">print</span>(df.tail(<span class="number">2</span>))       <span class="comment"># 看最后2行</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ── 基本信息 ────────────────────────────────────────</span></span><br><span class="line"><span class="built_in">print</span>(df.info())</span><br><span class="line"><span class="comment"># &lt;class &#x27;pandas.core.frame.DataFrame&#x27;&gt;</span></span><br><span class="line"><span class="comment"># RangeIndex: 8 entries, 0 to 7</span></span><br><span class="line"><span class="comment"># Data columns (total 5 columns):</span></span><br><span class="line"><span class="comment">#  #   Column  Non-Null Count  Dtype</span></span><br><span class="line"><span class="comment"># ---  ------  --------------  -----</span></span><br><span class="line"><span class="comment">#  0   姓名     8 non-null      object</span></span><br><span class="line"><span class="comment">#  1   年龄     8 non-null      int64</span></span><br><span class="line"><span class="comment">#  ...</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ── 描述性统计 ──────────────────────────────────────</span></span><br><span class="line"><span class="built_in">print</span>(df.describe())</span><br><span class="line"><span class="comment">#        年龄       薪资</span></span><br><span class="line"><span class="comment"># count   8.00     8.00</span></span><br><span class="line"><span class="comment"># mean   32.75  12562.50</span></span><br><span class="line"><span class="comment"># std     6.52   4467.85</span></span><br><span class="line"><span class="comment"># min    25.00   7500.00</span></span><br><span class="line"><span class="comment"># 25%    27.75   8750.00</span></span><br><span class="line"><span class="comment"># 50%    31.00  11500.00</span></span><br><span class="line"><span class="comment"># 75%    36.25  15750.00</span></span><br><span class="line"><span class="comment"># max    45.00  20000.00</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ── 查看各列数据类型 ────────────────────────────────</span></span><br><span class="line"><span class="built_in">print</span>(df.dtypes)</span><br><span class="line"><span class="comment"># 姓名    object</span></span><br><span class="line"><span class="comment"># 年龄     int64</span></span><br><span class="line"><span class="comment"># 城市    object</span></span><br><span class="line"><span class="comment"># 薪资     int64</span></span><br><span class="line"><span class="comment"># 部门    object</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ── 查看唯一值 ──────────────────────────────────────</span></span><br><span class="line"><span class="built_in">print</span>(df[<span class="string">&quot;城市&quot;</span>].unique())       <span class="comment"># [&#x27;北京&#x27; &#x27;上海&#x27; &#x27;广州&#x27; &#x27;深圳&#x27; &#x27;杭州&#x27; &#x27;成都&#x27; &#x27;武汉&#x27; &#x27;南京&#x27;]</span></span><br><span class="line"><span class="built_in">print</span>(df[<span class="string">&quot;部门&quot;</span>].value_counts()) <span class="comment"># 各部门人数</span></span><br><span class="line"><span class="comment"># 技术    4</span></span><br><span class="line"><span class="comment"># 市场    2</span></span><br><span class="line"><span class="comment"># 运营    2</span></span><br></pre></td></tr></table></figure><h4 id="数据筛选和排序"><a href="#数据筛选和排序" class="headerlink" title="数据筛选和排序"></a>数据筛选和排序</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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> pandas <span class="keyword">as</span> pd</span><br><span class="line"></span><br><span class="line">df = pd.DataFrame(&#123;</span><br><span class="line">    <span class="string">&quot;姓名&quot;</span>: [<span class="string">&quot;张三&quot;</span>, <span class="string">&quot;李四&quot;</span>, <span class="string">&quot;王五&quot;</span>, <span class="string">&quot;赵六&quot;</span>, <span class="string">&quot;钱七&quot;</span>, <span class="string">&quot;孙八&quot;</span>],</span><br><span class="line">    <span class="string">&quot;年龄&quot;</span>: [<span class="number">25</span>, <span class="number">30</span>, <span class="number">35</span>, <span class="number">28</span>, <span class="number">40</span>, <span class="number">32</span>],</span><br><span class="line">    <span class="string">&quot;城市&quot;</span>: [<span class="string">&quot;北京&quot;</span>, <span class="string">&quot;上海&quot;</span>, <span class="string">&quot;广州&quot;</span>, <span class="string">&quot;深圳&quot;</span>, <span class="string">&quot;杭州&quot;</span>, <span class="string">&quot;成都&quot;</span>],</span><br><span class="line">    <span class="string">&quot;薪资&quot;</span>: [<span class="number">8000</span>, <span class="number">12000</span>, <span class="number">15000</span>, <span class="number">9000</span>, <span class="number">20000</span>, <span class="number">11000</span>],</span><br><span class="line">    <span class="string">&quot;部门&quot;</span>: [<span class="string">&quot;技术&quot;</span>, <span class="string">&quot;市场&quot;</span>, <span class="string">&quot;技术&quot;</span>, <span class="string">&quot;运营&quot;</span>, <span class="string">&quot;技术&quot;</span>, <span class="string">&quot;市场&quot;</span>]</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="comment"># ── 单条件筛选 ──────────────────────────────────────</span></span><br><span class="line">high_salary = df[df[<span class="string">&quot;薪资&quot;</span>] &gt; <span class="number">10000</span>]</span><br><span class="line"><span class="built_in">print</span>(high_salary)</span><br><span class="line"><span class="comment">#    姓名  年龄  城市     薪资  部门</span></span><br><span class="line"><span class="comment"># 1  李四  30  上海  12000  市场</span></span><br><span class="line"><span class="comment"># 2  王五  35  广州  15000  技术</span></span><br><span class="line"><span class="comment"># 4  钱七  40  杭州  20000  技术</span></span><br><span class="line"><span class="comment"># 5  孙八  32  成都  11000  市场</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ── 多条件筛选 ──────────────────────────────────────</span></span><br><span class="line"><span class="comment"># &amp; 表示且，| 表示或，每个条件要用括号包裹！</span></span><br><span class="line">tech_high = df[(df[<span class="string">&quot;部门&quot;</span>] == <span class="string">&quot;技术&quot;</span>) &amp; (df[<span class="string">&quot;薪资&quot;</span>] &gt; <span class="number">10000</span>)]</span><br><span class="line"><span class="built_in">print</span>(tech_high)</span><br><span class="line"><span class="comment">#    姓名  年龄  城市     薪资  部门</span></span><br><span class="line"><span class="comment"># 2  王五  35  广州  15000  技术</span></span><br><span class="line"><span class="comment"># 4  钱七  40  杭州  20000  技术</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ── isin 筛选（值在列表中）─────────────────────────</span></span><br><span class="line">south = df[df[<span class="string">&quot;城市&quot;</span>].isin([<span class="string">&quot;广州&quot;</span>, <span class="string">&quot;深圳&quot;</span>, <span class="string">&quot;杭州&quot;</span>, <span class="string">&quot;成都&quot;</span>])]</span><br><span class="line"><span class="built_in">print</span>(south)</span><br><span class="line"><span class="comment">#    姓名  年龄  城市     薪资  部门</span></span><br><span class="line"><span class="comment"># 2  王五  35  广州  15000  技术</span></span><br><span class="line"><span class="comment"># 3  赵六  28  深圳   9000  运营</span></span><br><span class="line"><span class="comment"># 4  钱七  40  杭州  20000  技术</span></span><br><span class="line"><span class="comment"># 5  孙八  32  成都  11000  市场</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ── 字符串包含筛选 ──────────────────────────────────</span></span><br><span class="line"><span class="comment"># df[df[&quot;姓名&quot;].str.contains(&quot;张&quot;)]  # 姓名含&quot;张&quot;的行</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ── 排序 ────────────────────────────────────────────</span></span><br><span class="line"><span class="built_in">print</span>(df.sort_values(<span class="string">&quot;薪资&quot;</span>, ascending=<span class="literal">False</span>))  <span class="comment"># 按薪资降序</span></span><br><span class="line"><span class="comment">#    姓名  年龄  城市     薪资  部门</span></span><br><span class="line"><span class="comment"># 4  钱七  40  杭州  20000  技术</span></span><br><span class="line"><span class="comment"># 2  王五  35  广州  15000  技术</span></span><br><span class="line"><span class="comment"># 1  李四  30  上海  12000  市场</span></span><br><span class="line"><span class="comment"># ...</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(df.sort_values([<span class="string">&quot;部门&quot;</span>, <span class="string">&quot;薪资&quot;</span>], ascending=[<span class="literal">True</span>, <span class="literal">False</span>]))</span><br><span class="line"><span class="comment"># 先按部门升序，同部门内按薪资降序</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ── 取最大/最小行的索引 ─────────────────────────────</span></span><br><span class="line"><span class="built_in">print</span>(df.loc[df[<span class="string">&quot;薪资&quot;</span>].idxmax()])   <span class="comment"># 薪资最高的人</span></span><br><span class="line"><span class="comment"># 姓名     钱七</span></span><br><span class="line"><span class="comment"># 年龄      40</span></span><br><span class="line"><span class="comment"># 城市     杭州</span></span><br><span class="line"><span class="comment"># 薪资   20000</span></span><br><span class="line"><span class="comment"># 部门     技术</span></span><br></pre></td></tr></table></figure><h4 id="分组聚合"><a href="#分组聚合" class="headerlink" title="分组聚合"></a>分组聚合</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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> pandas <span class="keyword">as</span> pd</span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"></span><br><span class="line">df = pd.DataFrame(&#123;</span><br><span class="line">    <span class="string">&quot;部门&quot;</span>:  [<span class="string">&quot;技术&quot;</span>, <span class="string">&quot;技术&quot;</span>, <span class="string">&quot;技术&quot;</span>, <span class="string">&quot;市场&quot;</span>, <span class="string">&quot;市场&quot;</span>, <span class="string">&quot;运营&quot;</span>, <span class="string">&quot;运营&quot;</span>],</span><br><span class="line">    <span class="string">&quot;姓名&quot;</span>:  [<span class="string">&quot;张三&quot;</span>, <span class="string">&quot;王五&quot;</span>, <span class="string">&quot;钱七&quot;</span>, <span class="string">&quot;李四&quot;</span>, <span class="string">&quot;孙八&quot;</span>, <span class="string">&quot;赵六&quot;</span>, <span class="string">&quot;周九&quot;</span>],</span><br><span class="line">    <span class="string">&quot;薪资&quot;</span>:  [<span class="number">8000</span>, <span class="number">15000</span>, <span class="number">20000</span>, <span class="number">12000</span>, <span class="number">11000</span>, <span class="number">9000</span>, <span class="number">7500</span>],</span><br><span class="line">    <span class="string">&quot;绩效&quot;</span>:  [<span class="string">&quot;A&quot;</span>, <span class="string">&quot;B&quot;</span>, <span class="string">&quot;A&quot;</span>, <span class="string">&quot;B&quot;</span>, <span class="string">&quot;C&quot;</span>, <span class="string">&quot;A&quot;</span>, <span class="string">&quot;C&quot;</span>],</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="comment"># ── 单列分组 ────────────────────────────────────────</span></span><br><span class="line"><span class="built_in">print</span>(df.groupby(<span class="string">&quot;部门&quot;</span>)[<span class="string">&quot;薪资&quot;</span>].mean())</span><br><span class="line"><span class="comment"># 部门</span></span><br><span class="line"><span class="comment"># 技术    14333.33</span></span><br><span class="line"><span class="comment"># 市场    11500.00</span></span><br><span class="line"><span class="comment"># 运营     8250.00</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ── 多种聚合 ────────────────────────────────────────</span></span><br><span class="line"><span class="built_in">print</span>(df.groupby(<span class="string">&quot;部门&quot;</span>)[<span class="string">&quot;薪资&quot;</span>].agg([<span class="string">&quot;mean&quot;</span>, <span class="string">&quot;median&quot;</span>, <span class="string">&quot;min&quot;</span>, <span class="string">&quot;max&quot;</span>, <span class="string">&quot;count&quot;</span>]))</span><br><span class="line"><span class="comment">#            mean  median    min    max  count</span></span><br><span class="line"><span class="comment"># 部门</span></span><br><span class="line"><span class="comment"># 技术  14333.33  15000   8000  20000      3</span></span><br><span class="line"><span class="comment"># 市场  11500.00  11500  11000  12000      2</span></span><br><span class="line"><span class="comment"># 运营   8250.00   8250   7500   9000      2</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ── 自定义聚合函数 ──────────────────────────────────</span></span><br><span class="line"><span class="built_in">print</span>(df.groupby(<span class="string">&quot;部门&quot;</span>)[<span class="string">&quot;薪资&quot;</span>].agg(</span><br><span class="line">    平均薪资=<span class="string">&quot;mean&quot;</span>,</span><br><span class="line">    薪资极差=<span class="keyword">lambda</span> x: x.<span class="built_in">max</span>() - x.<span class="built_in">min</span>()</span><br><span class="line">))</span><br><span class="line"><span class="comment">#           平均薪资  薪资极差</span></span><br><span class="line"><span class="comment"># 部门</span></span><br><span class="line"><span class="comment"># 技术  14333.33   12000</span></span><br><span class="line"><span class="comment"># 市场  11500.00    1000</span></span><br><span class="line"><span class="comment"># 运营   8250.00    1500</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ── 多列分组 ────────────────────────────────────────</span></span><br><span class="line"><span class="built_in">print</span>(df.groupby([<span class="string">&quot;部门&quot;</span>, <span class="string">&quot;绩效&quot;</span>])[<span class="string">&quot;薪资&quot;</span>].mean())</span><br><span class="line"><span class="comment"># 部门  绩效</span></span><br><span class="line"><span class="comment"># 技术  A      14000</span></span><br><span class="line"><span class="comment">#       B      15000</span></span><br><span class="line"><span class="comment"># 市场  B      12000</span></span><br><span class="line"><span class="comment">#       C      11000</span></span><br><span class="line"><span class="comment"># 运营  A       9000</span></span><br><span class="line"><span class="comment">#       C       7500</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ── transform：不改变行数，给每行加上组统计量 ────────</span></span><br><span class="line">df[<span class="string">&quot;部门平均薪资&quot;</span>] = df.groupby(<span class="string">&quot;部门&quot;</span>)[<span class="string">&quot;薪资&quot;</span>].transform(<span class="string">&quot;mean&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(df[[<span class="string">&quot;姓名&quot;</span>, <span class="string">&quot;部门&quot;</span>, <span class="string">&quot;薪资&quot;</span>, <span class="string">&quot;部门平均薪资&quot;</span>]])</span><br><span class="line"><span class="comment">#   姓名  部门    薪资  部门平均薪资</span></span><br><span class="line"><span class="comment"># 0  张三  技术   8000  14333.33</span></span><br><span class="line"><span class="comment"># 1  王五  技术  15000  14333.33</span></span><br><span class="line"><span class="comment"># 2  钱七  技术  20000  14333.33</span></span><br><span class="line"><span class="comment"># 3  李四  市场  12000  11500.00</span></span><br><span class="line"><span class="comment"># ...</span></span><br></pre></td></tr></table></figure><h4 id="数据清洗（缺失值、重复值、类型转换）"><a href="#数据清洗（缺失值、重复值、类型转换）" class="headerlink" title="数据清洗（缺失值、重复值、类型转换）"></a>数据清洗（缺失值、重复值、类型转换）</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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> pandas <span class="keyword">as</span> pd</span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"></span><br><span class="line"><span class="comment"># 构造一份&quot;脏数据&quot;</span></span><br><span class="line">df = pd.DataFrame(&#123;</span><br><span class="line">    <span class="string">&quot;姓名&quot;</span>:  [<span class="string">&quot;张三&quot;</span>, <span class="string">&quot;李四&quot;</span>, np.nan, <span class="string">&quot;张三&quot;</span>, <span class="string">&quot;王五&quot;</span>, <span class="string">&quot;赵六&quot;</span>, np.nan],</span><br><span class="line">    <span class="string">&quot;年龄&quot;</span>:  [<span class="number">25</span>, np.nan, <span class="number">35</span>, <span class="number">25</span>, <span class="number">40</span>, np.nan, <span class="number">30</span>],</span><br><span class="line">    <span class="string">&quot;薪资&quot;</span>:  [<span class="string">&quot;8000&quot;</span>, <span class="string">&quot;12000&quot;</span>, <span class="string">&quot;15000&quot;</span>, <span class="string">&quot;8000&quot;</span>, <span class="string">&quot;20000&quot;</span>, <span class="string">&quot;N/A&quot;</span>, <span class="string">&quot;9000&quot;</span>],</span><br><span class="line">    <span class="string">&quot;入职日期&quot;</span>: [<span class="string">&quot;2020-01-15&quot;</span>, <span class="string">&quot;2019-06-01&quot;</span>, <span class="string">&quot;2018-03-20&quot;</span>, <span class="string">&quot;2020-01-15&quot;</span>, <span class="string">&quot;2017-11-05&quot;</span>, <span class="string">&quot;2021-09-10&quot;</span>, <span class="string">&quot;2022-02-28&quot;</span>]</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;── 原始数据 ──&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(df)</span><br><span class="line"><span class="comment">#     姓名   年龄     薪资       入职日期</span></span><br><span class="line"><span class="comment"># 0   张三  25.0    8000  2020-01-15</span></span><br><span class="line"><span class="comment"># 1   李四   NaN   12000  2019-06-01</span></span><br><span class="line"><span class="comment"># 2   NaN  35.0   15000  2018-03-20</span></span><br><span class="line"><span class="comment"># 3   张三  25.0    8000  2020-01-15   ← 完全重复第0行</span></span><br><span class="line"><span class="comment"># 4   王五  40.0   20000  2017-11-05</span></span><br><span class="line"><span class="comment"># 5   赵六   NaN     N/A  2021-09-10   ← 薪资是 &quot;N/A&quot;</span></span><br><span class="line"><span class="comment"># 6   NaN  30.0    9000  2022-02-28</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ── 1. 查看缺失值 ───────────────────────────────────</span></span><br><span class="line"><span class="built_in">print</span>(df.isnull().<span class="built_in">sum</span>())</span><br><span class="line"><span class="comment"># 姓名      2</span></span><br><span class="line"><span class="comment"># 年龄      2</span></span><br><span class="line"><span class="comment"># 薪资      0    ← &quot;N/A&quot; 是字符串，Pandas 不认为它是缺失！</span></span><br><span class="line"><span class="comment"># 入职日期    0</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ── 2. 处理缺失值 ───────────────────────────────────</span></span><br><span class="line"><span class="comment"># 方式一：删除含缺失值的行</span></span><br><span class="line">df_dropna = df.dropna()</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;删除缺失后行数:&quot;</span>, <span class="built_in">len</span>(df_dropna))  <span class="comment"># 3</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 方式二：填充缺失值（更常用）</span></span><br><span class="line">df_filled = df.copy()</span><br><span class="line">df_filled[<span class="string">&quot;姓名&quot;</span>] = df_filled[<span class="string">&quot;姓名&quot;</span>].fillna(<span class="string">&quot;未知&quot;</span>)        <span class="comment"># 姓名填&quot;未知&quot;</span></span><br><span class="line">df_filled[<span class="string">&quot;年龄&quot;</span>] = df_filled[<span class="string">&quot;年龄&quot;</span>].fillna(df_filled[<span class="string">&quot;年龄&quot;</span>].median())  <span class="comment"># 年龄填中位数</span></span><br><span class="line"><span class="built_in">print</span>(df_filled)</span><br><span class="line"></span><br><span class="line"><span class="comment"># ── 3. 处理重复值 ───────────────────────────────────</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;重复行数:&quot;</span>, df.duplicated().<span class="built_in">sum</span>())   <span class="comment"># 1</span></span><br><span class="line">df_dedup = df.drop_duplicates()             <span class="comment"># 去重</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;去重后行数:&quot;</span>, <span class="built_in">len</span>(df_dedup))         <span class="comment"># 6</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ── 4. 类型转换 ────────────────────────────────────</span></span><br><span class="line"><span class="comment"># 先把 &quot;N/A&quot; 替换为 NaN，再转数值</span></span><br><span class="line">df_clean = df.copy()</span><br><span class="line">df_clean[<span class="string">&quot;薪资&quot;</span>] = df_clean[<span class="string">&quot;薪资&quot;</span>].replace(<span class="string">&quot;N/A&quot;</span>, np.nan)</span><br><span class="line">df_clean[<span class="string">&quot;薪资&quot;</span>] = df_clean[<span class="string">&quot;薪资&quot;</span>].astype(<span class="built_in">float</span>)    <span class="comment"># 转为浮点数</span></span><br><span class="line"><span class="built_in">print</span>(df_clean[<span class="string">&quot;薪资&quot;</span>].dtype)  <span class="comment"># float64</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 日期类型转换</span></span><br><span class="line">df_clean[<span class="string">&quot;入职日期&quot;</span>] = pd.to_datetime(df_clean[<span class="string">&quot;入职日期&quot;</span>])</span><br><span class="line"><span class="built_in">print</span>(df_clean[<span class="string">&quot;入职日期&quot;</span>].dtype)  <span class="comment"># datetime64[ns]</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ── 5. 列重命名 ────────────────────────────────────</span></span><br><span class="line">df_clean = df_clean.rename(columns=&#123;<span class="string">&quot;姓名&quot;</span>: <span class="string">&quot;name&quot;</span>, <span class="string">&quot;薪资&quot;</span>: <span class="string">&quot;salary&quot;</span>&#125;)</span><br><span class="line"></span><br><span class="line"><span class="comment"># ── 6. 添加计算列 ──────────────────────────────────</span></span><br><span class="line">df_clean[<span class="string">&quot;入职年限&quot;</span>] = <span class="number">2026</span> - df_clean[<span class="string">&quot;入职日期&quot;</span>].dt.year</span><br><span class="line"><span class="built_in">print</span>(df_clean[[<span class="string">&quot;name&quot;</span>, <span class="string">&quot;入职年限&quot;</span>]])</span><br></pre></td></tr></table></figure><h4 id="合并和拼接"><a href="#合并和拼接" class="headerlink" title="合并和拼接"></a>合并和拼接</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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> pandas <span class="keyword">as</span> pd</span><br><span class="line"></span><br><span class="line"><span class="comment"># ── 1. merge：类似 SQL 的 JOIN ─────────────────────</span></span><br><span class="line">employees = pd.DataFrame(&#123;</span><br><span class="line">    <span class="string">&quot;员工ID&quot;</span>: [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>],</span><br><span class="line">    <span class="string">&quot;姓名&quot;</span>:   [<span class="string">&quot;张三&quot;</span>, <span class="string">&quot;李四&quot;</span>, <span class="string">&quot;王五&quot;</span>, <span class="string">&quot;赵六&quot;</span>],</span><br><span class="line">    <span class="string">&quot;部门ID&quot;</span>: [<span class="number">101</span>, <span class="number">102</span>, <span class="number">101</span>, <span class="number">103</span>]</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line">departments = pd.DataFrame(&#123;</span><br><span class="line">    <span class="string">&quot;部门ID&quot;</span>: [<span class="number">101</span>, <span class="number">102</span>, <span class="number">104</span>],</span><br><span class="line">    <span class="string">&quot;部门名称&quot;</span>: [<span class="string">&quot;技术部&quot;</span>, <span class="string">&quot;市场部&quot;</span>, <span class="string">&quot;财务部&quot;</span>]</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 内连接（只保留两边都有的）</span></span><br><span class="line">inner = pd.merge(employees, departments, on=<span class="string">&quot;部门ID&quot;</span>, how=<span class="string">&quot;inner&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;内连接:\n&quot;</span>, inner)</span><br><span class="line"><span class="comment">#    员工ID  姓名  部门ID 部门名称</span></span><br><span class="line"><span class="comment"># 0     1  张三    101  技术部</span></span><br><span class="line"><span class="comment"># 1     3  王五    101  技术部</span></span><br><span class="line"><span class="comment"># 2     2  李四    102  市场部</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 左连接（保留左表所有行）</span></span><br><span class="line">left = pd.merge(employees, departments, on=<span class="string">&quot;部门ID&quot;</span>, how=<span class="string">&quot;left&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;左连接:\n&quot;</span>, left)</span><br><span class="line"><span class="comment"># 赵六的部门名称为 NaN，因为 103 在右表不存在</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 全外连接</span></span><br><span class="line">outer = pd.merge(employees, departments, on=<span class="string">&quot;部门ID&quot;</span>, how=<span class="string">&quot;outer&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;全外连接:\n&quot;</span>, outer)</span><br><span class="line"></span><br><span class="line"><span class="comment"># ── 2. concat：纵向/横向拼接 ──────────────────────</span></span><br><span class="line">df1 = pd.DataFrame(&#123;<span class="string">&quot;A&quot;</span>: [<span class="number">1</span>, <span class="number">2</span>], <span class="string">&quot;B&quot;</span>: [<span class="number">3</span>, <span class="number">4</span>]&#125;)</span><br><span class="line">df2 = pd.DataFrame(&#123;<span class="string">&quot;A&quot;</span>: [<span class="number">5</span>, <span class="number">6</span>], <span class="string">&quot;B&quot;</span>: [<span class="number">7</span>, <span class="number">8</span>]&#125;)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 纵向拼接（追加行）</span></span><br><span class="line"><span class="built_in">print</span>(pd.concat([df1, df2], ignore_index=<span class="literal">True</span>))</span><br><span class="line"><span class="comment">#    A  B</span></span><br><span class="line"><span class="comment"># 0  1  3</span></span><br><span class="line"><span class="comment"># 1  2  4</span></span><br><span class="line"><span class="comment"># 2  5  7</span></span><br><span class="line"><span class="comment"># 3  6  8</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 横向拼接（追加列）</span></span><br><span class="line">df3 = pd.DataFrame(&#123;<span class="string">&quot;C&quot;</span>: [<span class="number">9</span>, <span class="number">10</span>], <span class="string">&quot;D&quot;</span>: [<span class="number">11</span>, <span class="number">12</span>]&#125;)</span><br><span class="line"><span class="built_in">print</span>(pd.concat([df1, df3], axis=<span class="number">1</span>))</span><br><span class="line"><span class="comment">#    A  B   C   D</span></span><br><span class="line"><span class="comment"># 0  1  3   9  11</span></span><br><span class="line"><span class="comment"># 1  2  4  10  12</span></span><br></pre></td></tr></table></figure><h4 id="时间序列处理"><a href="#时间序列处理" class="headerlink" title="时间序列处理"></a>时间序列处理</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><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><span class="line"><span class="keyword">import</span> pandas <span class="keyword">as</span> pd</span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"></span><br><span class="line"><span class="comment"># ── 创建时间序列 ────────────────────────────────────</span></span><br><span class="line"><span class="comment"># 生成2024年1月每天的销售数据</span></span><br><span class="line">dates = pd.date_range(<span class="string">&quot;2024-01-01&quot;</span>, periods=<span class="number">31</span>, freq=<span class="string">&quot;D&quot;</span>)</span><br><span class="line">np.random.seed(<span class="number">42</span>)</span><br><span class="line">sales = np.random.randint(<span class="number">100</span>, <span class="number">500</span>, size=<span class="number">31</span>)</span><br><span class="line">ts = pd.DataFrame(&#123;<span class="string">&quot;日期&quot;</span>: dates, <span class="string">&quot;销售额&quot;</span>: sales&#125;)</span><br><span class="line">ts = ts.set_index(<span class="string">&quot;日期&quot;</span>)  <span class="comment"># 把日期设为索引</span></span><br><span class="line"><span class="built_in">print</span>(ts.head())</span><br><span class="line"><span class="comment">#             销售额</span></span><br><span class="line"><span class="comment"># 日期</span></span><br><span class="line"><span class="comment"># 2024-01-01    137</span></span><br><span class="line"><span class="comment"># 2024-01-02    269</span></span><br><span class="line"><span class="comment"># 2024-01-03    141</span></span><br><span class="line"><span class="comment"># 2024-01-04    447</span></span><br><span class="line"><span class="comment"># 2024-01-05    215</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ── 按时间范围切片 ──────────────────────────────────</span></span><br><span class="line"><span class="built_in">print</span>(ts[<span class="string">&quot;2024-01-10&quot;</span>:<span class="string">&quot;2024-01-15&quot;</span>])  <span class="comment"># 1月10日到15日</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ── 重采样（降频：日 → 周/月）──────────────────────</span></span><br><span class="line">weekly = ts.resample(<span class="string">&quot;W&quot;</span>).<span class="built_in">sum</span>()       <span class="comment"># 按周汇总</span></span><br><span class="line">monthly = ts.resample(<span class="string">&quot;M&quot;</span>).mean()     <span class="comment"># 按月取平均</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;周度汇总:\n&quot;</span>, weekly.head())</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;月度平均:\n&quot;</span>, monthly)</span><br><span class="line"></span><br><span class="line"><span class="comment"># ── 滚动窗口 ────────────────────────────────────────</span></span><br><span class="line">ts[<span class="string">&quot;7日移动平均&quot;</span>] = ts[<span class="string">&quot;销售额&quot;</span>].rolling(window=<span class="number">7</span>).mean()</span><br><span class="line"><span class="built_in">print</span>(ts.head(<span class="number">10</span>))</span><br><span class="line"><span class="comment">#             销售额  7日移动平均</span></span><br><span class="line"><span class="comment"># 日期</span></span><br><span class="line"><span class="comment"># 2024-01-01    137       NaN</span></span><br><span class="line"><span class="comment"># ...</span></span><br><span class="line"><span class="comment"># 2024-01-07    295    258.43   ← 从第7天开始有值</span></span><br><span class="line"><span class="comment"># 2024-01-08    253    264.14</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ── 差分（计算环比变化）─────────────────────────────</span></span><br><span class="line">ts[<span class="string">&quot;日环比变化&quot;</span>] = ts[<span class="string">&quot;销售额&quot;</span>].diff()</span><br><span class="line"><span class="built_in">print</span>(ts.head())</span><br><span class="line"><span class="comment">#             销售额  日环比变化</span></span><br><span class="line"><span class="comment"># 日期</span></span><br><span class="line"><span class="comment"># 2024-01-01    137       NaN</span></span><br><span class="line"><span class="comment"># 2024-01-02    269     132.0</span></span><br><span class="line"><span class="comment"># 2024-01-03    141    -128.0</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ── 提取时间特征 ────────────────────────────────────</span></span><br><span class="line">ts[<span class="string">&quot;星期几&quot;</span>] = ts.index.dayofweek      <span class="comment"># 0=周一, 6=周日</span></span><br><span class="line">ts[<span class="string">&quot;是否周末&quot;</span>] = ts.index.dayofweek &gt;= <span class="number">5</span></span><br><span class="line"><span class="built_in">print</span>(ts[[<span class="string">&quot;销售额&quot;</span>, <span class="string">&quot;星期几&quot;</span>, <span class="string">&quot;是否周末&quot;</span>]].head(<span class="number">7</span>))</span><br></pre></td></tr></table></figure><hr><h3 id="2-5-Matplotlib-Seaborn-可视化教程"><a href="#2-5-Matplotlib-Seaborn-可视化教程" class="headerlink" title="2.5 Matplotlib &#x2F; Seaborn 可视化教程"></a>2.5 Matplotlib &#x2F; Seaborn 可视化教程</h3><h4 id="中文字体配置（必看！）"><a href="#中文字体配置（必看！）" class="headerlink" title="中文字体配置（必看！）"></a>中文字体配置（必看！）</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line"><span class="keyword">import</span> matplotlib</span><br><span class="line"></span><br><span class="line"><span class="comment"># ── 方式一：设置系统中文字体 ────────────────────────</span></span><br><span class="line">plt.rcParams[<span class="string">&quot;font.sans-serif&quot;</span>] = [<span class="string">&quot;SimHei&quot;</span>]    <span class="comment"># 黑体（Windows）</span></span><br><span class="line">plt.rcParams[<span class="string">&quot;axes.unicode_minus&quot;</span>] = <span class="literal">False</span>       <span class="comment"># 正常显示负号</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ── 方式二：Mac 用户 ───────────────────────────────</span></span><br><span class="line"><span class="comment"># plt.rcParams[&quot;font.sans-serif&quot;] = [&quot;Arial Unicode MS&quot;]</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ── 方式三：Linux 用户 ─────────────────────────────</span></span><br><span class="line"><span class="comment"># plt.rcParams[&quot;font.sans-serif&quot;] = [&quot;WenQuanYi Micro Hei&quot;]</span></span><br></pre></td></tr></table></figure><blockquote><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> matplotlib.font_manager <span class="keyword">import</span> fontManager</span><br><span class="line"><span class="keyword">for</span> f <span class="keyword">in</span> fontManager.ttflist:</span><br><span class="line">    <span class="keyword">if</span> <span class="string">&quot;Hei&quot;</span> <span class="keyword">in</span> f.name <span class="keyword">or</span> <span class="string">&quot;Song&quot;</span> <span class="keyword">in</span> f.name <span class="keyword">or</span> <span class="string">&quot;CN&quot;</span> <span class="keyword">in</span> f.name:</span><br><span class="line">        <span class="built_in">print</span>(f.name, f.fname)</span><br></pre></td></tr></table></figure></blockquote><h4 id="折线图"><a href="#折线图" class="headerlink" title="折线图"></a>折线图</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"></span><br><span class="line">plt.rcParams[<span class="string">&quot;font.sans-serif&quot;</span>] = [<span class="string">&quot;SimHei&quot;</span>]</span><br><span class="line">plt.rcParams[<span class="string">&quot;axes.unicode_minus&quot;</span>] = <span class="literal">False</span></span><br><span class="line"></span><br><span class="line">months = [<span class="string">&quot;1月&quot;</span>, <span class="string">&quot;2月&quot;</span>, <span class="string">&quot;3月&quot;</span>, <span class="string">&quot;4月&quot;</span>, <span class="string">&quot;5月&quot;</span>, <span class="string">&quot;6月&quot;</span>]</span><br><span class="line">sales_A = [<span class="number">120</span>, <span class="number">150</span>, <span class="number">170</span>, <span class="number">160</span>, <span class="number">200</span>, <span class="number">220</span>]</span><br><span class="line">sales_B = [<span class="number">80</span>,  <span class="number">110</span>, <span class="number">130</span>, <span class="number">140</span>, <span class="number">120</span>, <span class="number">150</span>]</span><br><span class="line"></span><br><span class="line">plt.figure(figsize=(<span class="number">10</span>, <span class="number">5</span>))</span><br><span class="line">plt.plot(months, sales_A, marker=<span class="string">&quot;o&quot;</span>, linewidth=<span class="number">2</span>, label=<span class="string">&quot;产品A&quot;</span>, color=<span class="string">&quot;#1f77b4&quot;</span>)</span><br><span class="line">plt.plot(months, sales_B, marker=<span class="string">&quot;s&quot;</span>, linewidth=<span class="number">2</span>, label=<span class="string">&quot;产品B&quot;</span>, color=<span class="string">&quot;#ff7f0e&quot;</span>)</span><br><span class="line"></span><br><span class="line">plt.title(<span class="string">&quot;月度销售趋势对比&quot;</span>, fontsize=<span class="number">16</span>)</span><br><span class="line">plt.xlabel(<span class="string">&quot;月份&quot;</span>, fontsize=<span class="number">12</span>)</span><br><span class="line">plt.ylabel(<span class="string">&quot;销售额（万元）&quot;</span>, fontsize=<span class="number">12</span>)</span><br><span class="line">plt.legend(fontsize=<span class="number">12</span>)              <span class="comment"># 显示图例</span></span><br><span class="line">plt.grid(alpha=<span class="number">0.3</span>)                  <span class="comment"># 添加网格线，透明度0.3</span></span><br><span class="line">plt.tight_layout()                   <span class="comment"># 自动调整布局</span></span><br><span class="line">plt.savefig(<span class="string">&quot;line_chart.png&quot;</span>, dpi=<span class="number">150</span>)</span><br><span class="line">plt.show()</span><br></pre></td></tr></table></figure><h4 id="柱状图"><a href="#柱状图" class="headerlink" title="柱状图"></a>柱状图</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line"></span><br><span class="line">plt.rcParams[<span class="string">&quot;font.sans-serif&quot;</span>] = [<span class="string">&quot;SimHei&quot;</span>]</span><br><span class="line">plt.rcParams[<span class="string">&quot;axes.unicode_minus&quot;</span>] = <span class="literal">False</span></span><br><span class="line"></span><br><span class="line">departments = [<span class="string">&quot;技术部&quot;</span>, <span class="string">&quot;市场部&quot;</span>, <span class="string">&quot;运营部&quot;</span>, <span class="string">&quot;财务部&quot;</span>]</span><br><span class="line">salaries = [<span class="number">15000</span>, <span class="number">12000</span>, <span class="number">9000</span>, <span class="number">11000</span>]</span><br><span class="line"></span><br><span class="line">plt.figure(figsize=(<span class="number">8</span>, <span class="number">5</span>))</span><br><span class="line">bars = plt.bar(departments, salaries, color=[<span class="string">&quot;#4e79a7&quot;</span>, <span class="string">&quot;#f28e2b&quot;</span>, <span class="string">&quot;#e15759&quot;</span>, <span class="string">&quot;#76b7b2&quot;</span>])</span><br><span class="line"></span><br><span class="line"><span class="comment"># 在柱子上方标注数值</span></span><br><span class="line"><span class="keyword">for</span> bar, val <span class="keyword">in</span> <span class="built_in">zip</span>(bars, salaries):</span><br><span class="line">    plt.text(bar.get_x() + bar.get_width()/<span class="number">2</span>, bar.get_height() + <span class="number">200</span>,</span><br><span class="line">             <span class="string">f&quot;¥<span class="subst">&#123;val:,&#125;</span>&quot;</span>, ha=<span class="string">&quot;center&quot;</span>, fontsize=<span class="number">11</span>)</span><br><span class="line"></span><br><span class="line">plt.title(<span class="string">&quot;各部门平均薪资&quot;</span>, fontsize=<span class="number">16</span>)</span><br><span class="line">plt.ylabel(<span class="string">&quot;薪资（元）&quot;</span>, fontsize=<span class="number">12</span>)</span><br><span class="line">plt.ylim(<span class="number">0</span>, <span class="number">18000</span>)                   <span class="comment"># 留出标注空间</span></span><br><span class="line">plt.tight_layout()</span><br><span class="line">plt.savefig(<span class="string">&quot;bar_chart.png&quot;</span>, dpi=<span class="number">150</span>)</span><br><span class="line">plt.show()</span><br></pre></td></tr></table></figure><h4 id="散点图"><a href="#散点图" class="headerlink" title="散点图"></a>散点图</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"></span><br><span class="line">plt.rcParams[<span class="string">&quot;font.sans-serif&quot;</span>] = [<span class="string">&quot;SimHei&quot;</span>]</span><br><span class="line">plt.rcParams[<span class="string">&quot;axes.unicode_minus&quot;</span>] = <span class="literal">False</span></span><br><span class="line"></span><br><span class="line">np.random.seed(<span class="number">42</span>)</span><br><span class="line">experience = np.random.uniform(<span class="number">1</span>, <span class="number">10</span>, <span class="number">50</span>)         <span class="comment"># 工作年限</span></span><br><span class="line">salary = <span class="number">5000</span> + experience * <span class="number">2000</span> + np.random.normal(<span class="number">0</span>, <span class="number">1500</span>, <span class="number">50</span>)  <span class="comment"># 薪资</span></span><br><span class="line"></span><br><span class="line">plt.figure(figsize=(<span class="number">8</span>, <span class="number">5</span>))</span><br><span class="line">plt.scatter(experience, salary, alpha=<span class="number">0.7</span>, edgecolors=<span class="string">&quot;k&quot;</span>, linewidths=<span class="number">0.5</span>, c=<span class="string">&quot;#2ca02c&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 添加趋势线</span></span><br><span class="line">z = np.polyfit(experience, salary, <span class="number">1</span>)              <span class="comment"># 线性拟合</span></span><br><span class="line">p = np.poly1d(z)</span><br><span class="line">x_line = np.linspace(<span class="number">1</span>, <span class="number">10</span>, <span class="number">100</span>)</span><br><span class="line">plt.plot(x_line, p(x_line), <span class="string">&quot;r--&quot;</span>, linewidth=<span class="number">2</span>, label=<span class="string">f&quot;趋势线: y=<span class="subst">&#123;z[<span class="number">0</span>]:<span class="number">.0</span>f&#125;</span>x+<span class="subst">&#123;z[<span class="number">1</span>]:<span class="number">.0</span>f&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line">plt.title(<span class="string">&quot;工作年限 vs 薪资&quot;</span>, fontsize=<span class="number">16</span>)</span><br><span class="line">plt.xlabel(<span class="string">&quot;工作年限（年）&quot;</span>, fontsize=<span class="number">12</span>)</span><br><span class="line">plt.ylabel(<span class="string">&quot;薪资（元）&quot;</span>, fontsize=<span class="number">12</span>)</span><br><span class="line">plt.legend()</span><br><span class="line">plt.grid(alpha=<span class="number">0.3</span>)</span><br><span class="line">plt.tight_layout()</span><br><span class="line">plt.savefig(<span class="string">&quot;scatter_chart.png&quot;</span>, dpi=<span class="number">150</span>)</span><br><span class="line">plt.show()</span><br></pre></td></tr></table></figure><h4 id="饼图"><a href="#饼图" class="headerlink" title="饼图"></a>饼图</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line"></span><br><span class="line">plt.rcParams[<span class="string">&quot;font.sans-serif&quot;</span>] = [<span class="string">&quot;SimHei&quot;</span>]</span><br><span class="line">plt.rcParams[<span class="string">&quot;axes.unicode_minus&quot;</span>] = <span class="literal">False</span></span><br><span class="line"></span><br><span class="line">labels = [<span class="string">&quot;技术部&quot;</span>, <span class="string">&quot;市场部&quot;</span>, <span class="string">&quot;运营部&quot;</span>, <span class="string">&quot;财务部&quot;</span>]</span><br><span class="line">sizes = [<span class="number">40</span>, <span class="number">25</span>, <span class="number">20</span>, <span class="number">15</span>]</span><br><span class="line">colors = [<span class="string">&quot;#4e79a7&quot;</span>, <span class="string">&quot;#f28e2b&quot;</span>, <span class="string">&quot;#e15759&quot;</span>, <span class="string">&quot;#76b7b2&quot;</span>]</span><br><span class="line">explode = (<span class="number">0.05</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>)                <span class="comment"># 突出技术部</span></span><br><span class="line"></span><br><span class="line">plt.figure(figsize=(<span class="number">7</span>, <span class="number">7</span>))</span><br><span class="line">plt.pie(sizes, explode=explode, labels=labels, colors=colors,</span><br><span class="line">        autopct=<span class="string">&quot;%1.1f%%&quot;</span>,                  <span class="comment"># 显示百分比</span></span><br><span class="line">        shadow=<span class="literal">True</span>, startangle=<span class="number">90</span>,</span><br><span class="line">        textprops=&#123;<span class="string">&quot;fontsize&quot;</span>: <span class="number">12</span>&#125;)</span><br><span class="line"></span><br><span class="line">plt.title(<span class="string">&quot;各部门人数占比&quot;</span>, fontsize=<span class="number">16</span>)</span><br><span class="line">plt.tight_layout()</span><br><span class="line">plt.savefig(<span class="string">&quot;pie_chart.png&quot;</span>, dpi=<span class="number">150</span>)</span><br><span class="line">plt.show()</span><br></pre></td></tr></table></figure><h4 id="Seaborn-热力图"><a href="#Seaborn-热力图" class="headerlink" title="Seaborn 热力图"></a>Seaborn 热力图</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> seaborn <span class="keyword">as</span> sns</span><br><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"></span><br><span class="line">plt.rcParams[<span class="string">&quot;font.sans-serif&quot;</span>] = [<span class="string">&quot;SimHei&quot;</span>]</span><br><span class="line">plt.rcParams[<span class="string">&quot;axes.unicode_minus&quot;</span>] = <span class="literal">False</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 相关性热力图（展示各变量间的相关系数）</span></span><br><span class="line">data = &#123;</span><br><span class="line">    <span class="string">&quot;薪资&quot;</span>:   [<span class="number">8000</span>, <span class="number">12000</span>, <span class="number">15000</span>, <span class="number">9000</span>, <span class="number">20000</span>, <span class="number">11000</span>, <span class="number">7500</span>, <span class="number">18000</span>],</span><br><span class="line">    <span class="string">&quot;年龄&quot;</span>:   [<span class="number">25</span>, <span class="number">30</span>, <span class="number">35</span>, <span class="number">28</span>, <span class="number">40</span>, <span class="number">32</span>, <span class="number">27</span>, <span class="number">45</span>],</span><br><span class="line">    <span class="string">&quot;工龄&quot;</span>:   [<span class="number">2</span>, <span class="number">5</span>, <span class="number">8</span>, <span class="number">3</span>, <span class="number">12</span>, <span class="number">6</span>, <span class="number">1</span>, <span class="number">15</span>],</span><br><span class="line">    <span class="string">&quot;绩效分&quot;</span>: [<span class="number">78</span>, <span class="number">85</span>, <span class="number">92</span>, <span class="number">70</span>, <span class="number">95</span>, <span class="number">80</span>, <span class="number">65</span>, <span class="number">88</span>]</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">import</span> pandas <span class="keyword">as</span> pd</span><br><span class="line">df_corr = pd.DataFrame(data)</span><br><span class="line">corr_matrix = df_corr.corr()            <span class="comment"># 计算相关系数矩阵</span></span><br><span class="line"></span><br><span class="line">plt.figure(figsize=(<span class="number">8</span>, <span class="number">6</span>))</span><br><span class="line">sns.heatmap(corr_matrix, annot=<span class="literal">True</span>, fmt=<span class="string">&quot;.2f&quot;</span>, cmap=<span class="string">&quot;RdBu_r&quot;</span>,</span><br><span class="line">            vmin=-<span class="number">1</span>, vmax=<span class="number">1</span>, center=<span class="number">0</span>,</span><br><span class="line">            linewidths=<span class="number">0.5</span>)</span><br><span class="line">plt.title(<span class="string">&quot;各变量相关性热力图&quot;</span>, fontsize=<span class="number">16</span>)</span><br><span class="line">plt.tight_layout()</span><br><span class="line">plt.savefig(<span class="string">&quot;heatmap.png&quot;</span>, dpi=<span class="number">150</span>)</span><br><span class="line">plt.show()</span><br></pre></td></tr></table></figure><h4 id="Seaborn-箱线图"><a href="#Seaborn-箱线图" class="headerlink" title="Seaborn 箱线图"></a>Seaborn 箱线图</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> seaborn <span class="keyword">as</span> sns</span><br><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line"><span class="keyword">import</span> pandas <span class="keyword">as</span> pd</span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"></span><br><span class="line">plt.rcParams[<span class="string">&quot;font.sans-serif&quot;</span>] = [<span class="string">&quot;SimHei&quot;</span>]</span><br><span class="line">plt.rcParams[<span class="string">&quot;axes.unicode_minus&quot;</span>] = <span class="literal">False</span></span><br><span class="line"></span><br><span class="line">np.random.seed(<span class="number">42</span>)</span><br><span class="line">df = pd.DataFrame(&#123;</span><br><span class="line">    <span class="string">&quot;部门&quot;</span>: np.repeat([<span class="string">&quot;技术部&quot;</span>, <span class="string">&quot;市场部&quot;</span>, <span class="string">&quot;运营部&quot;</span>], <span class="number">30</span>),</span><br><span class="line">    <span class="string">&quot;薪资&quot;</span>: np.concatenate([</span><br><span class="line">        np.random.normal(<span class="number">15000</span>, <span class="number">3000</span>, <span class="number">30</span>),</span><br><span class="line">        np.random.normal(<span class="number">12000</span>, <span class="number">2500</span>, <span class="number">30</span>),</span><br><span class="line">        np.random.normal(<span class="number">9000</span>,  <span class="number">2000</span>, <span class="number">30</span>)</span><br><span class="line">    ])</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line">plt.figure(figsize=(<span class="number">8</span>, <span class="number">5</span>))</span><br><span class="line">sns.boxplot(x=<span class="string">&quot;部门&quot;</span>, y=<span class="string">&quot;薪资&quot;</span>, data=df, palette=<span class="string">&quot;Set2&quot;</span>)</span><br><span class="line">plt.title(<span class="string">&quot;各部门薪资分布（箱线图）&quot;</span>, fontsize=<span class="number">16</span>)</span><br><span class="line">plt.ylabel(<span class="string">&quot;薪资（元）&quot;</span>, fontsize=<span class="number">12</span>)</span><br><span class="line">plt.tight_layout()</span><br><span class="line">plt.savefig(<span class="string">&quot;boxplot.png&quot;</span>, dpi=<span class="number">150</span>)</span><br><span class="line">plt.show()</span><br><span class="line"><span class="comment"># 箱线图解读：箱体=25%~75%分位，中线=中位数，须=1.5倍IQR范围，点=离群值</span></span><br></pre></td></tr></table></figure><hr><h3 id="2-6-完整案例：电商销售数据分析"><a href="#2-6-完整案例：电商销售数据分析" class="headerlink" title="2.6 完整案例：电商销售数据分析"></a>2.6 完整案例：电商销售数据分析</h3><p>下面演示一个从 CSV 读取到出报告的<strong>完整流程</strong>。假设原始数据如下（实际工作中通常直接读 CSV）：</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><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><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> pandas <span class="keyword">as</span> pd</span><br><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line"><span class="keyword">import</span> seaborn <span class="keyword">as</span> sns</span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"></span><br><span class="line">plt.rcParams[<span class="string">&quot;font.sans-serif&quot;</span>] = [<span class="string">&quot;SimHei&quot;</span>]</span><br><span class="line">plt.rcParams[<span class="string">&quot;axes.unicode_minus&quot;</span>] = <span class="literal">False</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ═══════════════════════════════════════════════════</span></span><br><span class="line"><span class="comment"># 第1步：读取数据</span></span><br><span class="line"><span class="comment"># ═══════════════════════════════════════════════════</span></span><br><span class="line"><span class="comment"># 实际工作：df = pd.read_csv(&quot;sales_data.csv&quot;, encoding=&quot;utf-8&quot;)</span></span><br><span class="line"><span class="comment"># 这里用模拟数据演示</span></span><br><span class="line"></span><br><span class="line">np.random.seed(<span class="number">42</span>)</span><br><span class="line">n = <span class="number">500</span></span><br><span class="line">df = pd.DataFrame(&#123;</span><br><span class="line">    <span class="string">&quot;订单ID&quot;</span>:    <span class="built_in">range</span>(<span class="number">10001</span>, <span class="number">10001</span> + n),</span><br><span class="line">    <span class="string">&quot;日期&quot;</span>:      pd.date_range(<span class="string">&quot;2024-01-01&quot;</span>, periods=n, freq=<span class="string">&quot;D&quot;</span>),</span><br><span class="line">    <span class="string">&quot;品类&quot;</span>:      np.random.choice([<span class="string">&quot;电子产品&quot;</span>, <span class="string">&quot;服装&quot;</span>, <span class="string">&quot;食品&quot;</span>, <span class="string">&quot;家居&quot;</span>, <span class="string">&quot;图书&quot;</span>], n),</span><br><span class="line">    <span class="string">&quot;城市&quot;</span>:      np.random.choice([<span class="string">&quot;北京&quot;</span>, <span class="string">&quot;上海&quot;</span>, <span class="string">&quot;广州&quot;</span>, <span class="string">&quot;深圳&quot;</span>, <span class="string">&quot;杭州&quot;</span>, <span class="string">&quot;成都&quot;</span>], n),</span><br><span class="line">    <span class="string">&quot;金额&quot;</span>:      np.random.exponential(<span class="number">300</span>, n).<span class="built_in">round</span>(<span class="number">2</span>),  <span class="comment"># 金额服从指数分布</span></span><br><span class="line">    <span class="string">&quot;数量&quot;</span>:      np.random.randint(<span class="number">1</span>, <span class="number">10</span>, n),</span><br><span class="line">    <span class="string">&quot;是否退款&quot;</span>:  np.random.choice([<span class="number">0</span>, <span class="number">1</span>], n, p=[<span class="number">0.9</span>, <span class="number">0.1</span>]),  <span class="comment"># 10%退款率</span></span><br><span class="line">&#125;)</span><br><span class="line">df[<span class="string">&quot;实付金额&quot;</span>] = df[<span class="string">&quot;金额&quot;</span>] * df[<span class="string">&quot;数量&quot;</span>] * (<span class="number">1</span> - df[<span class="string">&quot;是否退款&quot;</span>])  <span class="comment"># 退款则为0</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;数据量：<span class="subst">&#123;<span class="built_in">len</span>(df)&#125;</span> 行&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(df.head())</span><br><span class="line"></span><br><span class="line"><span class="comment"># ═══════════════════════════════════════════════════</span></span><br><span class="line"><span class="comment"># 第2步：数据概览</span></span><br><span class="line"><span class="comment"># ═══════════════════════════════════════════════════</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;\n── 基本信息 ──&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(df.info())</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;\n── 描述性统计 ──&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(df.describe())</span><br><span class="line"></span><br><span class="line"><span class="comment"># ═══════════════════════════════════════════════════</span></span><br><span class="line"><span class="comment"># 第3步：核心指标计算</span></span><br><span class="line"><span class="comment"># ═══════════════════════════════════════════════════</span></span><br><span class="line">total_revenue = df[<span class="string">&quot;实付金额&quot;</span>].<span class="built_in">sum</span>()</span><br><span class="line">avg_order_value = df[<span class="string">&quot;实付金额&quot;</span>].mean()</span><br><span class="line">refund_rate = df[<span class="string">&quot;是否退款&quot;</span>].mean()</span><br><span class="line">total_orders = <span class="built_in">len</span>(df)</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;\n<span class="subst">&#123;<span class="string">&#x27;=&#x27;</span>*<span class="number">40</span>&#125;</span>&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;  总营收：¥<span class="subst">&#123;total_revenue:,<span class="number">.2</span>f&#125;</span>&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;  订单量：<span class="subst">&#123;total_orders&#125;</span>&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;  客单价：¥<span class="subst">&#123;avg_order_value:,<span class="number">.2</span>f&#125;</span>&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;  退款率：<span class="subst">&#123;refund_rate:<span class="number">.1</span>%&#125;</span>&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;<span class="subst">&#123;<span class="string">&#x27;=&#x27;</span>*<span class="number">40</span>&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># ═══════════════════════════════════════════════════</span></span><br><span class="line"><span class="comment"># 第4步：各品类分析</span></span><br><span class="line"><span class="comment"># ═══════════════════════════════════════════════════</span></span><br><span class="line">category_stats = df.groupby(<span class="string">&quot;品类&quot;</span>).agg(</span><br><span class="line">    订单数=(<span class="string">&quot;订单ID&quot;</span>, <span class="string">&quot;count&quot;</span>),</span><br><span class="line">    总营收=(<span class="string">&quot;实付金额&quot;</span>, <span class="string">&quot;sum&quot;</span>),</span><br><span class="line">    客单价=(<span class="string">&quot;实付金额&quot;</span>, <span class="string">&quot;mean&quot;</span>),</span><br><span class="line">    退款率=(<span class="string">&quot;是否退款&quot;</span>, <span class="string">&quot;mean&quot;</span>)</span><br><span class="line">).sort_values(<span class="string">&quot;总营收&quot;</span>, ascending=<span class="literal">False</span>)</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;\n── 各品类统计 ──&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(category_stats)</span><br><span class="line"></span><br><span class="line"><span class="comment"># ═══════════════════════════════════════════════════</span></span><br><span class="line"><span class="comment"># 第5步：可视化</span></span><br><span class="line"><span class="comment"># ═══════════════════════════════════════════════════</span></span><br><span class="line">fig, axes = plt.subplots(<span class="number">2</span>, <span class="number">2</span>, figsize=(<span class="number">14</span>, <span class="number">10</span>))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 5-1 各品类营收柱状图</span></span><br><span class="line">category_stats[<span class="string">&quot;总营收&quot;</span>].plot(kind=<span class="string">&quot;bar&quot;</span>, ax=axes[<span class="number">0</span>, <span class="number">0</span>], color=<span class="string">&quot;#4e79a7&quot;</span>)</span><br><span class="line">axes[<span class="number">0</span>, <span class="number">0</span>].set_title(<span class="string">&quot;各品类总营收&quot;</span>, fontsize=<span class="number">14</span>)</span><br><span class="line">axes[<span class="number">0</span>, <span class="number">0</span>].set_ylabel(<span class="string">&quot;营收（元）&quot;</span>)</span><br><span class="line">axes[<span class="number">0</span>, <span class="number">0</span>].tick_params(axis=<span class="string">&quot;x&quot;</span>, rotation=<span class="number">0</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 5-2 各城市订单量</span></span><br><span class="line">city_orders = df[<span class="string">&quot;城市&quot;</span>].value_counts()</span><br><span class="line">city_orders.plot(kind=<span class="string">&quot;bar&quot;</span>, ax=axes[<span class="number">0</span>, <span class="number">1</span>], color=<span class="string">&quot;#f28e2b&quot;</span>)</span><br><span class="line">axes[<span class="number">0</span>, <span class="number">1</span>].set_title(<span class="string">&quot;各城市订单量&quot;</span>, fontsize=<span class="number">14</span>)</span><br><span class="line">axes[<span class="number">0</span>, <span class="number">1</span>].set_ylabel(<span class="string">&quot;订单数&quot;</span>)</span><br><span class="line">axes[<span class="number">0</span>, <span class="number">1</span>].tick_params(axis=<span class="string">&quot;x&quot;</span>, rotation=<span class="number">0</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 5-3 月度营收趋势</span></span><br><span class="line">monthly = df.set_index(<span class="string">&quot;日期&quot;</span>)[<span class="string">&quot;实付金额&quot;</span>].resample(<span class="string">&quot;M&quot;</span>).<span class="built_in">sum</span>()</span><br><span class="line">monthly.plot(ax=axes[<span class="number">1</span>, <span class="number">0</span>], marker=<span class="string">&quot;o&quot;</span>, color=<span class="string">&quot;#2ca02c&quot;</span>, linewidth=<span class="number">2</span>)</span><br><span class="line">axes[<span class="number">1</span>, <span class="number">0</span>].set_title(<span class="string">&quot;月度营收趋势&quot;</span>, fontsize=<span class="number">14</span>)</span><br><span class="line">axes[<span class="number">1</span>, <span class="number">0</span>].set_ylabel(<span class="string">&quot;营收（元）&quot;</span>)</span><br><span class="line">axes[<span class="number">1</span>, <span class="number">0</span>].grid(alpha=<span class="number">0.3</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 5-4 退款率饼图</span></span><br><span class="line">refund_counts = df[<span class="string">&quot;是否退款&quot;</span>].value_counts()</span><br><span class="line">axes[<span class="number">1</span>, <span class="number">1</span>].pie(refund_counts, labels=[<span class="string">&quot;未退款&quot;</span>, <span class="string">&quot;已退款&quot;</span>],</span><br><span class="line">               autopct=<span class="string">&quot;%1.1f%%&quot;</span>, colors=[<span class="string">&quot;#76b7b2&quot;</span>, <span class="string">&quot;#e15759&quot;</span>],</span><br><span class="line">               startangle=<span class="number">90</span>, textprops=&#123;<span class="string">&quot;fontsize&quot;</span>: <span class="number">12</span>&#125;)</span><br><span class="line">axes[<span class="number">1</span>, <span class="number">1</span>].set_title(<span class="string">&quot;退款比例&quot;</span>, fontsize=<span class="number">14</span>)</span><br><span class="line"></span><br><span class="line">plt.suptitle(<span class="string">&quot;电商销售数据分析报告&quot;</span>, fontsize=<span class="number">18</span>, y=<span class="number">1.02</span>)</span><br><span class="line">plt.tight_layout()</span><br><span class="line">plt.savefig(<span class="string">&quot;ecommerce_report.png&quot;</span>, dpi=<span class="number">150</span>, bbox_inches=<span class="string">&quot;tight&quot;</span>)</span><br><span class="line">plt.show()</span><br><span class="line"></span><br><span class="line"><span class="comment"># ═══════════════════════════════════════════════════</span></span><br><span class="line"><span class="comment"># 第6步：输出结论</span></span><br><span class="line"><span class="comment"># ═══════════════════════════════════════════════════</span></span><br><span class="line">top_category = category_stats.index[<span class="number">0</span>]</span><br><span class="line">top_city = city_orders.index[<span class="number">0</span>]</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;\n📊 分析结论：&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;  1. 营收最高的品类：<span class="subst">&#123;top_category&#125;</span>（¥<span class="subst">&#123;category_stats.loc[top_category, <span class="string">&#x27;总营收&#x27;</span>]:,<span class="number">.2</span>f&#125;</span>）&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;  2. 订单最多的城市：<span class="subst">&#123;top_city&#125;</span>（<span class="subst">&#123;city_orders[top_city]&#125;</span> 单）&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;  3. 整体退款率：<span class="subst">&#123;refund_rate:<span class="number">.1</span>%&#125;</span>，建议关注退款原因&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;  4. 客单价：¥<span class="subst">&#123;avg_order_value:,<span class="number">.2</span>f&#125;</span>，可考虑提升客单价的运营策略&quot;</span>)</span><br></pre></td></tr></table></figure><hr><h3 id="2-7-常见新手坑"><a href="#2-7-常见新手坑" class="headerlink" title="2.7 常见新手坑"></a>2.7 常见新手坑</h3><table><thead><tr><th>序号</th><th>坑</th><th>说明</th><th>正确做法</th></tr></thead><tbody><tr><td>1</td><td><strong>SettingWithCopyWarning</strong></td><td>链式赋值 <code>df[df[&quot;age&quot;]&gt;30][&quot;salary&quot;] = 999</code> 触发警告，改的是副本不是原表</td><td>用 <code>.loc</code>：<code>df.loc[df[&quot;age&quot;]&gt;30, &quot;salary&quot;] = 999</code></td></tr><tr><td>2</td><td><strong>中文乱码</strong></td><td>Matplotlib 默认不显示中文，标题&#x2F;标签变方块</td><td>设 <code>rcParams[&quot;font.sans-serif&quot;] = [&quot;SimHei&quot;]</code>（见 2.5 节）</td></tr><tr><td>3</td><td><strong>CSV 编码错误</strong></td><td><code>UnicodeDecodeError</code></td><td>先用 <code>encoding=&quot;gbk&quot;</code> 或 <code>&quot;utf-8-sig&quot;</code> 试试</td></tr><tr><td>4</td><td><strong>Pandas 链式索引</strong></td><td><code>df[df[&quot;a&quot;]&gt;0][&quot;b&quot;]</code> 返回副本，后续修改不生效</td><td>统一用 <code>df.loc[条件, 列名]</code></td></tr><tr><td>5</td><td><strong>NumPy 广播报错</strong></td><td>形状不兼容时报 <code>ValueError: operands could not be broadcast</code></td><td>检查两个数组的 shape，确保满足广播规则（末维相同或一方为1）</td></tr><tr><td>6</td><td><strong><code>inplace=True</code> 不可靠</strong></td><td><code>df.dropna(inplace=True)</code> 在某些场景下不生效</td><td>改用赋值：<code>df = df.dropna()</code></td></tr><tr><td>7</td><td><strong>大文件内存溢出</strong></td><td><code>pd.read_csv()</code> 默认全部加载到内存</td><td>用 <code>chunksize</code> 分块读，或指定 <code>usecols</code> 只读需要的列</td></tr><tr><td>8</td><td><strong>日期解析慢</strong></td><td><code>pd.read_csv(parse_dates=[&quot;col&quot;])</code> 对大文件很慢</td><td>改用 <code>pd.to_datetime()</code> 读完后单独转换，或指定 <code>format</code> 参数</td></tr><tr><td>9</td><td><strong><code>==</code> 判断 NaN</strong></td><td><code>NaN == NaN</code> 返回 <code>False</code></td><td>用 <code>pd.isna()</code> 或 <code>df.isnull()</code></td></tr><tr><td>10</td><td><strong>忘记 <code>import</code></strong></td><td><code>NameError: name &#39;np&#39; is not defined</code></td><td>每个脚本开头统一导入：<code>import numpy as np; import pandas as pd; import matplotlib.pyplot as plt</code></td></tr></tbody></table><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><span class="line"><span class="keyword">import</span> pandas <span class="keyword">as</span> pd</span><br><span class="line"></span><br><span class="line"><span class="comment"># 方式一：分块迭代处理</span></span><br><span class="line">chunk_summary = []</span><br><span class="line"><span class="keyword">for</span> chunk <span class="keyword">in</span> pd.read_csv(<span class="string">&quot;huge_file.csv&quot;</span>, chunksize=<span class="number">10000</span>, encoding=<span class="string">&quot;utf-8&quot;</span>):</span><br><span class="line">    <span class="comment"># 每次只读 10000 行，处理完释放内存</span></span><br><span class="line">    result = chunk.groupby(<span class="string">&quot;品类&quot;</span>)[<span class="string">&quot;金额&quot;</span>].<span class="built_in">sum</span>()</span><br><span class="line">    chunk_summary.append(result)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 合并各块结果</span></span><br><span class="line">final = pd.concat(chunk_summary).groupby(level=<span class="number">0</span>).<span class="built_in">sum</span>()</span><br><span class="line"><span class="built_in">print</span>(final)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 方式二：只读需要的列（大幅减少内存）</span></span><br><span class="line">df = pd.read_csv(<span class="string">&quot;huge_file.csv&quot;</span>, usecols=[<span class="string">&quot;品类&quot;</span>, <span class="string">&quot;金额&quot;</span>], encoding=<span class="string">&quot;utf-8&quot;</span>)</span><br></pre></td></tr></table></figure><p><strong>数据科学工具链全景</strong>：</p><figure class="highlight plaintext"><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><span class="line">数据科学工具链：</span><br><span class="line">├── 数据处理：NumPy / Pandas / Polars（更快）</span><br><span class="line">├── 可视化：Matplotlib / Seaborn / Plotly / Bokeh</span><br><span class="line">├── 机器学习：Scikit-learn / XGBoost / LightGBM</span><br><span class="line">├── 深度学习：PyTorch / TensorFlow / JAX</span><br><span class="line">├── notebooks：Jupyter / JupyterLab / Google Colab</span><br><span class="line">├── 大数据：PySpark / Dask / Ray</span><br><span class="line">├── 特征工程：Feature-engine / category_encoders</span><br><span class="line">└── AutoML：Auto-sklearn / TPOT / H2O</span><br></pre></td></tr></table></figure><hr><h2 id="3-网络爬虫与数据采集"><a href="#3-网络爬虫与数据采集" class="headerlink" title="3. 网络爬虫与数据采集"></a>3. 网络爬虫与数据采集</h2><h3 id="3-1-爬虫是什么？合法边界"><a href="#3-1-爬虫是什么？合法边界" class="headerlink" title="3.1 爬虫是什么？合法边界"></a>3.1 爬虫是什么？合法边界</h3><p><strong>爬虫原理</strong>：网络爬虫（Web Crawler &#x2F; Spider）本质上是一个自动化程序，它模拟浏览器向服务器发送 HTTP 请求，获取网页内容，然后从中提取需要的数据。</p><figure class="highlight plaintext"><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><span class="line">爬虫工作流程：</span><br><span class="line">用户程序 → 发送 HTTP 请求 → 目标服务器 → 返回 HTML/JSON → 解析提取数据 → 存储</span><br><span class="line">    │            │                                              │</span><br><span class="line">    └→ 构造 URL  └→ 添加请求头/Cookie                           └→ CSS/XPath/JSON</span><br></pre></td></tr></table></figure><p><strong>robots.txt</strong>：网站通过 <code>robots.txt</code> 文件声明哪些页面允许&#x2F;禁止爬取。访问方式：<code>https://example.com/robots.txt</code></p><figure class="highlight plaintext"><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><span class="line"># robots.txt 示例</span><br><span class="line">User-agent: *          # 对所有爬虫生效</span><br><span class="line">Allow: /public/        # 允许爬取 /public/ 下的页面</span><br><span class="line">Disallow: /admin/      # 禁止爬取 /admin/ 下的页面</span><br><span class="line">Disallow: /private/    # 禁止爬取 /private/ 下的页面</span><br><span class="line">Crawl-delay: 5         # 请求间隔至少 5 秒</span><br></pre></td></tr></table></figure><blockquote><p>⚠️ <strong>法律注意事项</strong>：</p><ol><li><strong>遵守 robots.txt</strong>：虽然它不是法律强制要求，但违反可能被视为不正当竞争</li><li><strong>不爬取个人隐私数据</strong>：手机号、身份证、住址等属于个人信息，未经授权采集违法</li><li><strong>控制请求频率</strong>：高频请求可能构成 DDOS 攻击，务必加延迟（建议至少 1-3 秒）</li><li><strong>尊重版权</strong>：爬取的内容用于商业用途需获得授权</li><li><strong>不绕过登录限制</strong>：绕过付费墙或登录验证可能违法</li><li><strong>了解《数据安全法》《个人信息保护法》</strong>：中国法律对数据采集有明确约束</li></ol></blockquote><h3 id="3-2-从零上手：requests-BeautifulSoup"><a href="#3-2-从零上手：requests-BeautifulSoup" class="headerlink" title="3.2 从零上手：requests + BeautifulSoup"></a>3.2 从零上手：requests + BeautifulSoup</h3><p><strong>安装依赖</strong>：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pip install requests beautifulsoup4 lxml</span><br></pre></td></tr></table></figure><p><strong>发送请求（GET&#x2F;POST、请求头、Cookie、Session）</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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> requests</span><br><span class="line"><span class="keyword">from</span> bs4 <span class="keyword">import</span> BeautifulSoup</span><br><span class="line"></span><br><span class="line"><span class="comment"># ===== 1. 最简单的 GET 请求 =====</span></span><br><span class="line">response = requests.get(<span class="string">&quot;https://httpbin.org/get&quot;</span>, timeout=<span class="number">10</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;状态码: <span class="subst">&#123;response.status_code&#125;</span>&quot;</span>)   <span class="comment"># 200 表示成功</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;响应内容: <span class="subst">&#123;response.text[:<span class="number">200</span>]&#125;</span>&quot;</span>)  <span class="comment"># 前面 200 个字符</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ===== 2. 带请求头（模拟浏览器，防止被拒绝） =====</span></span><br><span class="line">headers = &#123;</span><br><span class="line">    <span class="string">&quot;User-Agent&quot;</span>: <span class="string">&quot;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 &quot;</span></span><br><span class="line">                  <span class="string">&quot;(KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36&quot;</span>,</span><br><span class="line">    <span class="string">&quot;Accept&quot;</span>: <span class="string">&quot;text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8&quot;</span>,</span><br><span class="line">    <span class="string">&quot;Accept-Language&quot;</span>: <span class="string">&quot;zh-CN,zh;q=0.9,en;q=0.8&quot;</span>,</span><br><span class="line">&#125;</span><br><span class="line">response = requests.get(<span class="string">&quot;https://httpbin.org/get&quot;</span>, headers=headers, timeout=<span class="number">10</span>)</span><br><span class="line"><span class="built_in">print</span>(response.json())  <span class="comment"># httpbin 返回 JSON，可以看到请求头已被修改</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ===== 3. GET 请求带参数 =====</span></span><br><span class="line">params = &#123;</span><br><span class="line">    <span class="string">&quot;q&quot;</span>: <span class="string">&quot;Python 爬虫&quot;</span>,</span><br><span class="line">    <span class="string">&quot;page&quot;</span>: <span class="number">1</span>,</span><br><span class="line">&#125;</span><br><span class="line">response = requests.get(<span class="string">&quot;https://httpbin.org/get&quot;</span>, params=params, timeout=<span class="number">10</span>)</span><br><span class="line"><span class="built_in">print</span>(response.json()[<span class="string">&quot;args&quot;</span>])  <span class="comment"># &#123;&#x27;q&#x27;: &#x27;Python 爬虫&#x27;, &#x27;page&#x27;: &#x27;1&#x27;&#125;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ===== 4. POST 请求（提交表单 / JSON） =====</span></span><br><span class="line"><span class="comment"># 提交表单数据</span></span><br><span class="line">form_data = &#123;<span class="string">&quot;username&quot;</span>: <span class="string">&quot;admin&quot;</span>, <span class="string">&quot;password&quot;</span>: <span class="string">&quot;123456&quot;</span>&#125;</span><br><span class="line">response = requests.post(<span class="string">&quot;https://httpbin.org/post&quot;</span>, data=form_data, timeout=<span class="number">10</span>)</span><br><span class="line"><span class="built_in">print</span>(response.json()[<span class="string">&quot;form&quot;</span>])  <span class="comment"># &#123;&#x27;username&#x27;: &#x27;admin&#x27;, &#x27;password&#x27;: &#x27;123456&#x27;&#125;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 提交 JSON 数据</span></span><br><span class="line">json_data = &#123;<span class="string">&quot;title&quot;</span>: <span class="string">&quot;测试&quot;</span>, <span class="string">&quot;content&quot;</span>: <span class="string">&quot;Hello&quot;</span>&#125;</span><br><span class="line">response = requests.post(<span class="string">&quot;https://httpbin.org/post&quot;</span>, json=json_data, timeout=<span class="number">10</span>)</span><br><span class="line"><span class="built_in">print</span>(response.json()[<span class="string">&quot;json&quot;</span>])  <span class="comment"># &#123;&#x27;title&#x27;: &#x27;测试&#x27;, &#x27;content&#x27;: &#x27;Hello&#x27;&#125;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ===== 5. 使用 Cookie =====</span></span><br><span class="line"><span class="comment"># 方式一：手动设置 Cookie</span></span><br><span class="line">cookies = &#123;<span class="string">&quot;session_id&quot;</span>: <span class="string">&quot;abc123&quot;</span>, <span class="string">&quot;user&quot;</span>: <span class="string">&quot;admin&quot;</span>&#125;</span><br><span class="line">response = requests.get(<span class="string">&quot;https://httpbin.org/cookies&quot;</span>, cookies=cookies, timeout=<span class="number">10</span>)</span><br><span class="line"><span class="built_in">print</span>(response.json()[<span class="string">&quot;cookies&quot;</span>])  <span class="comment"># &#123;&#x27;session_id&#x27;: &#x27;abc123&#x27;, &#x27;user&#x27;: &#x27;admin&#x27;&#125;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 方式二：从响应中获取 Cookie</span></span><br><span class="line">response = requests.post(<span class="string">&quot;https://httpbin.org/cookies/set?name=value&quot;</span>, timeout=<span class="number">10</span>)</span><br><span class="line"><span class="built_in">print</span>(response.cookies.get_dict())  <span class="comment"># &#123;&#x27;name&#x27;: &#x27;value&#x27;&#125;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ===== 6. 使用 Session（自动管理 Cookie，模拟登录状态） =====</span></span><br><span class="line">session = requests.Session()</span><br><span class="line">session.headers.update(&#123;<span class="string">&quot;User-Agent&quot;</span>: <span class="string">&quot;Mozilla/5.0 ...&quot;</span>&#125;)</span><br><span class="line"><span class="comment"># 第一次请求：登录（服务器会返回 Set-Cookie）</span></span><br><span class="line">session.post(<span class="string">&quot;https://httpbin.org/cookies/set?token=mytoken&quot;</span>, timeout=<span class="number">10</span>)</span><br><span class="line"><span class="comment"># 后续请求自动携带 Cookie</span></span><br><span class="line">response = session.get(<span class="string">&quot;https://httpbin.org/cookies&quot;</span>, timeout=<span class="number">10</span>)</span><br><span class="line"><span class="built_in">print</span>(response.json()[<span class="string">&quot;cookies&quot;</span>])  <span class="comment"># &#123;&#x27;token&#x27;: &#x27;mytoken&#x27;&#125;</span></span><br><span class="line">session.close()  <span class="comment"># 用完关闭</span></span><br></pre></td></tr></table></figure><p><strong>解析 HTML（find&#x2F;find_all、CSS 选择器）</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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> bs4 <span class="keyword">import</span> BeautifulSoup</span><br><span class="line"></span><br><span class="line"><span class="comment"># 模拟一个网页 HTML</span></span><br><span class="line">html = <span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">&lt;!DOCTYPE html&gt;</span></span><br><span class="line"><span class="string">&lt;html&gt;</span></span><br><span class="line"><span class="string">&lt;head&gt;&lt;title&gt;示例网站&lt;/title&gt;&lt;/head&gt;</span></span><br><span class="line"><span class="string">&lt;body&gt;</span></span><br><span class="line"><span class="string">    &lt;div class=&quot;container&quot;&gt;</span></span><br><span class="line"><span class="string">        &lt;h1 class=&quot;title&quot;&gt;新闻列表&lt;/h1&gt;</span></span><br><span class="line"><span class="string">        &lt;div class=&quot;news-list&quot;&gt;</span></span><br><span class="line"><span class="string">            &lt;article class=&quot;news-item&quot; data-id=&quot;1&quot;&gt;</span></span><br><span class="line"><span class="string">                &lt;h2 class=&quot;news-title&quot;&gt;&lt;a href=&quot;/news/1&quot;&gt;Python 3.12 正式发布&lt;/a&gt;&lt;/h2&gt;</span></span><br><span class="line"><span class="string">                &lt;p class=&quot;summary&quot;&gt;新版本带来了更快的性能和更多特性。&lt;/p&gt;</span></span><br><span class="line"><span class="string">                &lt;span class=&quot;tag&quot;&gt;技术&lt;/span&gt;</span></span><br><span class="line"><span class="string">                &lt;span class=&quot;date&quot;&gt;2024-10-01&lt;/span&gt;</span></span><br><span class="line"><span class="string">            &lt;/article&gt;</span></span><br><span class="line"><span class="string">            &lt;article class=&quot;news-item&quot; data-id=&quot;2&quot;&gt;</span></span><br><span class="line"><span class="string">                &lt;h2 class=&quot;news-title&quot;&gt;&lt;a href=&quot;/news/2&quot;&gt;AI 改变世界&lt;/a&gt;&lt;/h2&gt;</span></span><br><span class="line"><span class="string">                &lt;p class=&quot;summary&quot;&gt;人工智能正在重塑各行各业。&lt;/p&gt;</span></span><br><span class="line"><span class="string">                &lt;span class=&quot;tag&quot;&gt;AI&lt;/span&gt;</span></span><br><span class="line"><span class="string">                &lt;span class=&quot;date&quot;&gt;2024-10-02&lt;/span&gt;</span></span><br><span class="line"><span class="string">            &lt;/article&gt;</span></span><br><span class="line"><span class="string">            &lt;article class=&quot;news-item&quot; data-id=&quot;3&quot;&gt;</span></span><br><span class="line"><span class="string">                &lt;h2 class=&quot;news-title&quot;&gt;&lt;a href=&quot;/news/3&quot;&gt;爬虫入门教程&lt;/a&gt;&lt;/h2&gt;</span></span><br><span class="line"><span class="string">                &lt;p class=&quot;summary&quot;&gt;从零开始学习 Python 爬虫。&lt;/p&gt;</span></span><br><span class="line"><span class="string">                &lt;span class=&quot;tag&quot;&gt;教程&lt;/span&gt;</span></span><br><span class="line"><span class="string">                &lt;span class=&quot;date&quot;&gt;2024-10-03&lt;/span&gt;</span></span><br><span class="line"><span class="string">            &lt;/article&gt;</span></span><br><span class="line"><span class="string">        &lt;/div&gt;</span></span><br><span class="line"><span class="string">        &lt;div id=&quot;sidebar&quot;&gt;</span></span><br><span class="line"><span class="string">            &lt;ul class=&quot;hot-topics&quot;&gt;</span></span><br><span class="line"><span class="string">                &lt;li&gt;&lt;a href=&quot;/topic/python&quot;&gt;Python&lt;/a&gt;&lt;/li&gt;</span></span><br><span class="line"><span class="string">                &lt;li&gt;&lt;a href=&quot;/topic/ai&quot;&gt;AI&lt;/a&gt;&lt;/li&gt;</span></span><br><span class="line"><span class="string">            &lt;/ul&gt;</span></span><br><span class="line"><span class="string">        &lt;/div&gt;</span></span><br><span class="line"><span class="string">    &lt;/div&gt;</span></span><br><span class="line"><span class="string">&lt;/body&gt;</span></span><br><span class="line"><span class="string">&lt;/html&gt;</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">soup = BeautifulSoup(html, <span class="string">&quot;lxml&quot;</span>)  <span class="comment"># 也可以用 &quot;html.parser&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ===== 1. find —— 找第一个匹配的元素 =====</span></span><br><span class="line">title = soup.find(<span class="string">&quot;h1&quot;</span>, class_=<span class="string">&quot;title&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(title.get_text())  <span class="comment"># &quot;新闻列表&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ===== 2. find_all —— 找所有匹配的元素 =====</span></span><br><span class="line">articles = soup.find_all(<span class="string">&quot;article&quot;</span>, class_=<span class="string">&quot;news-item&quot;</span>)</span><br><span class="line"><span class="keyword">for</span> article <span class="keyword">in</span> articles:</span><br><span class="line">    <span class="comment"># 获取子元素</span></span><br><span class="line">    title_tag = article.find(<span class="string">&quot;h2&quot;</span>, class_=<span class="string">&quot;news-title&quot;</span>)</span><br><span class="line">    link_tag = title_tag.find(<span class="string">&quot;a&quot;</span>)</span><br><span class="line">    summary = article.find(<span class="string">&quot;p&quot;</span>, class_=<span class="string">&quot;summary&quot;</span>)</span><br><span class="line">    tag = article.find(<span class="string">&quot;span&quot;</span>, class_=<span class="string">&quot;tag&quot;</span>)</span><br><span class="line">    date = article.find(<span class="string">&quot;span&quot;</span>, class_=<span class="string">&quot;date&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;标题: <span class="subst">&#123;link_tag.get_text()&#125;</span>&quot;</span>)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;链接: <span class="subst">&#123;link_tag[<span class="string">&#x27;href&#x27;</span>]&#125;</span>&quot;</span>)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;摘要: <span class="subst">&#123;summary.get_text()&#125;</span>&quot;</span>)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;标签: <span class="subst">&#123;tag.get_text()&#125;</span>&quot;</span>)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;日期: <span class="subst">&#123;date.get_text()&#125;</span>&quot;</span>)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;---&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># ===== 3. CSS 选择器（更灵活，类似 jQuery） =====</span></span><br><span class="line"><span class="comment"># select_one —— 找第一个</span></span><br><span class="line">first_article = soup.select_one(<span class="string">&quot;article.news-item&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(first_article.select_one(<span class="string">&quot;a&quot;</span>)[<span class="string">&quot;href&quot;</span>])  <span class="comment"># &quot;/news/1&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># select —— 找所有</span></span><br><span class="line">all_links = soup.select(<span class="string">&quot;article.news-item a&quot;</span>)</span><br><span class="line"><span class="keyword">for</span> link <span class="keyword">in</span> all_links:</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;<span class="subst">&#123;link.get_text()&#125;</span>: <span class="subst">&#123;link[<span class="string">&#x27;href&#x27;</span>]&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 属性选择器</span></span><br><span class="line">article_with_id = soup.select_one(<span class="string">&#x27;article[data-id=&quot;2&quot;]&#x27;</span>)</span><br><span class="line"><span class="built_in">print</span>(article_with_id.select_one(<span class="string">&quot;.news-title&quot;</span>).get_text())  <span class="comment"># &quot;AI 改变世界&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 组合选择器</span></span><br><span class="line">sidebar_links = soup.select(<span class="string">&quot;#sidebar .hot-topics a&quot;</span>)</span><br><span class="line"><span class="keyword">for</span> link <span class="keyword">in</span> sidebar_links:</span><br><span class="line">    <span class="built_in">print</span>(link.get_text(), link[<span class="string">&quot;href&quot;</span>])</span><br><span class="line"></span><br><span class="line"><span class="comment"># ===== 4. 获取属性和文本 =====</span></span><br><span class="line">article = soup.select_one(<span class="string">&quot;article.news-item&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(article.get_text(strip=<span class="literal">True</span>))     <span class="comment"># 所有文本（合并）</span></span><br><span class="line"><span class="built_in">print</span>(article[<span class="string">&quot;data-id&quot;</span>])               <span class="comment"># 属性值：&#x27;1&#x27;</span></span><br><span class="line"><span class="built_in">print</span>(article.get(<span class="string">&quot;data-id&quot;</span>))           <span class="comment"># 属性值：&#x27;1&#x27;（不存在返回 None）</span></span><br><span class="line"><span class="built_in">print</span>(article.attrs)                    <span class="comment"># 所有属性字典</span></span><br></pre></td></tr></table></figure><p><strong>保存数据（CSV&#x2F;JSON&#x2F;数据库）</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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> csv</span><br><span class="line"><span class="keyword">import</span> json</span><br><span class="line"><span class="keyword">import</span> sqlite3</span><br><span class="line"><span class="keyword">import</span> requests</span><br><span class="line"><span class="keyword">from</span> bs4 <span class="keyword">import</span> BeautifulSoup</span><br><span class="line"></span><br><span class="line"><span class="comment"># 先采集数据（假设已经拿到新闻列表）</span></span><br><span class="line"><span class="comment"># 这里用模拟数据演示保存方式</span></span><br><span class="line">news_list = [</span><br><span class="line">    &#123;<span class="string">&quot;title&quot;</span>: <span class="string">&quot;Python 3.12 正式发布&quot;</span>, <span class="string">&quot;link&quot;</span>: <span class="string">&quot;/news/1&quot;</span>, <span class="string">&quot;tag&quot;</span>: <span class="string">&quot;技术&quot;</span>, <span class="string">&quot;date&quot;</span>: <span class="string">&quot;2024-10-01&quot;</span>&#125;,</span><br><span class="line">    &#123;<span class="string">&quot;title&quot;</span>: <span class="string">&quot;AI 改变世界&quot;</span>, <span class="string">&quot;link&quot;</span>: <span class="string">&quot;/news/2&quot;</span>, <span class="string">&quot;tag&quot;</span>: <span class="string">&quot;AI&quot;</span>, <span class="string">&quot;date&quot;</span>: <span class="string">&quot;2024-10-02&quot;</span>&#125;,</span><br><span class="line">    &#123;<span class="string">&quot;title&quot;</span>: <span class="string">&quot;爬虫入门教程&quot;</span>, <span class="string">&quot;link&quot;</span>: <span class="string">&quot;/news/3&quot;</span>, <span class="string">&quot;tag&quot;</span>: <span class="string">&quot;教程&quot;</span>, <span class="string">&quot;date&quot;</span>: <span class="string">&quot;2024-10-03&quot;</span>&#125;,</span><br><span class="line">]</span><br><span class="line"></span><br><span class="line"><span class="comment"># ===== 1. 保存为 CSV =====</span></span><br><span class="line"><span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">&quot;news.csv&quot;</span>, <span class="string">&quot;w&quot;</span>, newline=<span class="string">&quot;&quot;</span>, encoding=<span class="string">&quot;utf-8-sig&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">    writer = csv.DictWriter(f, fieldnames=[<span class="string">&quot;title&quot;</span>, <span class="string">&quot;link&quot;</span>, <span class="string">&quot;tag&quot;</span>, <span class="string">&quot;date&quot;</span>])</span><br><span class="line">    writer.writeheader()         <span class="comment"># 写入表头</span></span><br><span class="line">    writer.writerows(news_list)  <span class="comment"># 写入所有数据</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;已保存到 news.csv&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># ===== 2. 保存为 JSON =====</span></span><br><span class="line"><span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">&quot;news.json&quot;</span>, <span class="string">&quot;w&quot;</span>, encoding=<span class="string">&quot;utf-8&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">    json.dump(news_list, f, ensure_ascii=<span class="literal">False</span>, indent=<span class="number">2</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;已保存到 news.json&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># ===== 3. 保存到 SQLite 数据库 =====</span></span><br><span class="line">conn = sqlite3.connect(<span class="string">&quot;news.db&quot;</span>)</span><br><span class="line">cursor = conn.cursor()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建表</span></span><br><span class="line">cursor.execute(<span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">    CREATE TABLE IF NOT EXISTS news (</span></span><br><span class="line"><span class="string">        id INTEGER PRIMARY KEY AUTOINCREMENT,</span></span><br><span class="line"><span class="string">        title TEXT NOT NULL,</span></span><br><span class="line"><span class="string">        link TEXT,</span></span><br><span class="line"><span class="string">        tag TEXT,</span></span><br><span class="line"><span class="string">        date TEXT</span></span><br><span class="line"><span class="string">    )</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 插入数据</span></span><br><span class="line"><span class="keyword">for</span> item <span class="keyword">in</span> news_list:</span><br><span class="line">    cursor.execute(</span><br><span class="line">        <span class="string">&quot;INSERT INTO news (title, link, tag, date) VALUES (?, ?, ?, ?)&quot;</span>,</span><br><span class="line">        (item[<span class="string">&quot;title&quot;</span>], item[<span class="string">&quot;link&quot;</span>], item[<span class="string">&quot;tag&quot;</span>], item[<span class="string">&quot;date&quot;</span>])</span><br><span class="line">    )</span><br><span class="line"></span><br><span class="line">conn.commit()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 验证：查询数据</span></span><br><span class="line">cursor.execute(<span class="string">&quot;SELECT * FROM news&quot;</span>)</span><br><span class="line"><span class="keyword">for</span> row <span class="keyword">in</span> cursor.fetchall():</span><br><span class="line">    <span class="built_in">print</span>(row)</span><br><span class="line"></span><br><span class="line">conn.close()</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;已保存到 news.db&quot;</span>)</span><br></pre></td></tr></table></figure><h3 id="3-3-动态页面爬取"><a href="#3-3-动态页面爬取" class="headerlink" title="3.3 动态页面爬取"></a>3.3 动态页面爬取</h3><p>很多现代网站使用 JavaScript 动态加载数据，requests 只能拿到初始 HTML，看不到动态内容。这时需要用 <strong>Playwright</strong> 让浏览器自动渲染页面。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">pip install playwright</span><br><span class="line">playwright install chromium  <span class="comment"># 下载浏览器（只需执行一次）</span></span><br></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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> playwright.sync_api <span class="keyword">import</span> sync_playwright</span><br><span class="line"><span class="keyword">import</span> json</span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">scrape_dynamic_page</span>():</span><br><span class="line">    <span class="string">&quot;&quot;&quot;使用 Playwright 爬取动态加载的网页。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">with</span> sync_playwright() <span class="keyword">as</span> p:</span><br><span class="line">        <span class="comment"># 启动浏览器（headless=True 表示不显示浏览器窗口）</span></span><br><span class="line">        browser = p.chromium.launch(headless=<span class="literal">True</span>)</span><br><span class="line">        page = browser.new_page()</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 设置请求头</span></span><br><span class="line">        page.set_extra_http_headers(&#123;</span><br><span class="line">            <span class="string">&quot;Accept-Language&quot;</span>: <span class="string">&quot;zh-CN,zh;q=0.9&quot;</span>,</span><br><span class="line">        &#125;)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 访问目标页面</span></span><br><span class="line">        page.goto(<span class="string">&quot;https://news.ycombinator.com/&quot;</span>, wait_until=<span class="string">&quot;networkidle&quot;</span>)</span><br><span class="line">        <span class="comment"># wait_until=&quot;networkidle&quot; 表示等到网络请求基本结束</span></span><br><span class="line"></span><br><span class="line">        <span class="comment"># 等待特定元素出现</span></span><br><span class="line">        page.wait_for_selector(<span class="string">&quot;.athing&quot;</span>)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># ===== 方法1：用 Playwright 内置选择器提取数据 =====</span></span><br><span class="line">        items = page.query_selector_all(<span class="string">&quot;.athing&quot;</span>)</span><br><span class="line">        results = []</span><br><span class="line">        <span class="keyword">for</span> item <span class="keyword">in</span> items[:<span class="number">10</span>]:  <span class="comment"># 只取前 10 条</span></span><br><span class="line">            title_el = item.query_selector(<span class="string">&quot;.titleline &gt; a&quot;</span>)</span><br><span class="line">            <span class="keyword">if</span> title_el:</span><br><span class="line">                title = title_el.inner_text()</span><br><span class="line">                link = title_el.get_attribute(<span class="string">&quot;href&quot;</span>)</span><br><span class="line">                results.append(&#123;<span class="string">&quot;title&quot;</span>: title, <span class="string">&quot;link&quot;</span>: link&#125;)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># ===== 方法2：用 BeautifulSoup 解析（更灵活） =====</span></span><br><span class="line">        <span class="comment"># 获取完整渲染后的 HTML</span></span><br><span class="line">        html = page.content()</span><br><span class="line">        <span class="keyword">from</span> bs4 <span class="keyword">import</span> BeautifulSoup</span><br><span class="line">        soup = BeautifulSoup(html, <span class="string">&quot;lxml&quot;</span>)</span><br><span class="line">        <span class="comment"># 后续和 3.2 节一样解析...</span></span><br><span class="line"></span><br><span class="line">        <span class="comment"># ===== 截图（调试用） =====</span></span><br><span class="line">        page.screenshot(path=<span class="string">&quot;screenshot.png&quot;</span>)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># ===== 滚动加载（无限滚动页面） =====</span></span><br><span class="line">        <span class="comment"># for i in range(3):  # 滚动 3 次</span></span><br><span class="line">        <span class="comment">#     page.evaluate(&quot;window.scrollTo(0, document.body.scrollHeight)&quot;)</span></span><br><span class="line">        <span class="comment">#     page.wait_for_timeout(2000)  # 等待 2 秒加载</span></span><br><span class="line"></span><br><span class="line">        <span class="comment"># ===== 模拟点击（如&quot;加载更多&quot;按钮） =====</span></span><br><span class="line">        <span class="comment"># more_btn = page.query_selector(&quot;.load-more&quot;)</span></span><br><span class="line">        <span class="comment"># if more_btn:</span></span><br><span class="line">        <span class="comment">#     more_btn.click()</span></span><br><span class="line">        <span class="comment">#     page.wait_for_timeout(2000)</span></span><br><span class="line"></span><br><span class="line">        browser.close()</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> results</span><br><span class="line"></span><br><span class="line"><span class="comment"># 运行</span></span><br><span class="line">data = scrape_dynamic_page()</span><br><span class="line"><span class="keyword">for</span> item <span class="keyword">in</span> data:</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;标题: <span class="subst">&#123;item[<span class="string">&#x27;title&#x27;</span>]&#125;</span>&quot;</span>)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;链接: <span class="subst">&#123;item[<span class="string">&#x27;link&#x27;</span>]&#125;</span>&quot;</span>)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;---&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 保存结果</span></span><br><span class="line"><span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">&quot;hacker_news.json&quot;</span>, <span class="string">&quot;w&quot;</span>, encoding=<span class="string">&quot;utf-8&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">    json.dump(data, f, ensure_ascii=<span class="literal">False</span>, indent=<span class="number">2</span>)</span><br></pre></td></tr></table></figure><h3 id="3-4-Scrapy-框架入门"><a href="#3-4-Scrapy-框架入门" class="headerlink" title="3.4 Scrapy 框架入门"></a>3.4 Scrapy 框架入门</h3><p>Scrapy 是 Python 最强大的爬虫框架，适合大规模、结构化的爬虫项目。</p><p><strong>安装和创建项目</strong>：</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><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">pip install scrapy</span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建项目</span></span><br><span class="line">scrapy startproject news_spider</span><br><span class="line"><span class="built_in">cd</span> news_spider</span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建爬虫</span></span><br><span class="line">scrapy genspider quotes quotes.toscrape.com</span><br></pre></td></tr></table></figure><p><strong>项目结构</strong>：</p><figure class="highlight plaintext"><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><span class="line">news_spider/</span><br><span class="line">├── scrapy.cfg                # 项目配置</span><br><span class="line">└── news_spider/</span><br><span class="line">    ├── __init__.py</span><br><span class="line">    ├── items.py              # 数据结构定义</span><br><span class="line">    ├── middlewares.py        # 中间件</span><br><span class="line">    ├── pipelines.py          # 数据管道</span><br><span class="line">    ├── settings.py           # 全局设置</span><br><span class="line">    └── spiders/</span><br><span class="line">        ├── __init__.py</span><br><span class="line">        └── quotes_spider.py  # 爬虫文件</span><br></pre></td></tr></table></figure><p><strong>定义 Item（items.py）</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> scrapy</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">NewsSpiderItem</span>(scrapy.Item):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;定义要采集的数据字段。&quot;&quot;&quot;</span></span><br><span class="line">    title = scrapy.Field()     <span class="comment"># 标题</span></span><br><span class="line">    author = scrapy.Field()    <span class="comment"># 作者</span></span><br><span class="line">    tags = scrapy.Field()      <span class="comment"># 标签</span></span><br><span class="line">    content = scrapy.Field()   <span class="comment"># 内容</span></span><br></pre></td></tr></table></figure><p><strong>编写 Spider（spiders&#x2F;quotes_spider.py）</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><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><span class="line"><span class="keyword">import</span> scrapy</span><br><span class="line"><span class="keyword">from</span> news_spider.items <span class="keyword">import</span> NewsSpiderItem</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">QuotesSpider</span>(scrapy.Spider):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;Quotes 网站爬虫 —— Scrapy 入门示例。&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">    name = <span class="string">&quot;quotes&quot;</span>                       <span class="comment"># 爬虫名称（唯一标识）</span></span><br><span class="line">    allowed_domains = [<span class="string">&quot;quotes.toscrape.com&quot;</span>]  <span class="comment"># 限制爬取域名</span></span><br><span class="line">    start_urls = [<span class="string">&quot;https://quotes.toscrape.com/&quot;</span>]  <span class="comment"># 起始 URL</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">parse</span>(<span class="params">self, response</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;解析页面，提取数据。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="comment"># 使用 CSS 选择器提取所有名言块</span></span><br><span class="line">        <span class="keyword">for</span> quote <span class="keyword">in</span> response.css(<span class="string">&quot;div.quote&quot;</span>):</span><br><span class="line">            item = NewsSpiderItem()</span><br><span class="line">            item[<span class="string">&quot;title&quot;</span>] = quote.css(<span class="string">&quot;span.text::text&quot;</span>).get()     <span class="comment"># 名言内容</span></span><br><span class="line">            item[<span class="string">&quot;author&quot;</span>] = quote.css(<span class="string">&quot;small.author::text&quot;</span>).get() <span class="comment"># 作者</span></span><br><span class="line">            item[<span class="string">&quot;tags&quot;</span>] = quote.css(<span class="string">&quot;div.tags a.tag::text&quot;</span>).getall()  <span class="comment"># 标签列表</span></span><br><span class="line">            item[<span class="string">&quot;content&quot;</span>] = <span class="literal">None</span></span><br><span class="line">            <span class="keyword">yield</span> item  <span class="comment"># 交给 Pipeline 处理</span></span><br><span class="line"></span><br><span class="line">        <span class="comment"># 翻页：提取下一页链接，继续爬取</span></span><br><span class="line">        next_page = response.css(<span class="string">&quot;li.next a::attr(href)&quot;</span>).get()</span><br><span class="line">        <span class="keyword">if</span> next_page:</span><br><span class="line">            <span class="keyword">yield</span> response.follow(next_page, callback=<span class="variable language_">self</span>.parse)</span><br><span class="line">            <span class="comment"># response.follow 会自动拼接完整 URL</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 也可以用 XPath 选择器（更灵活）</span></span><br><span class="line">    <span class="comment"># def parse(self, response):</span></span><br><span class="line">    <span class="comment">#     for quote in response.xpath(&#x27;//div[@class=&quot;quote&quot;]&#x27;):</span></span><br><span class="line">    <span class="comment">#         item = NewsSpiderItem()</span></span><br><span class="line">    <span class="comment">#         item[&quot;title&quot;] = quote.xpath(&#x27;.//span[@class=&quot;text&quot;]/text()&#x27;).get()</span></span><br><span class="line">    <span class="comment">#         item[&quot;author&quot;] = quote.xpath(&#x27;.//small[@class=&quot;author&quot;]/text()&#x27;).get()</span></span><br><span class="line">    <span class="comment">#         yield item</span></span><br></pre></td></tr></table></figure><p><strong>数据管道 Pipeline（pipelines.py）</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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> json</span><br><span class="line"><span class="keyword">import</span> csv</span><br><span class="line"><span class="keyword">import</span> sqlite3</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">JsonPipeline</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;将数据保存为 JSON 文件。&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">open_spider</span>(<span class="params">self, spider</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;爬虫启动时调用。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="variable language_">self</span>.file = <span class="built_in">open</span>(<span class="string">&quot;quotes.json&quot;</span>, <span class="string">&quot;w&quot;</span>, encoding=<span class="string">&quot;utf-8&quot;</span>)</span><br><span class="line">        <span class="variable language_">self</span>.data = []</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">close_spider</span>(<span class="params">self, spider</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;爬虫关闭时调用。&quot;&quot;&quot;</span></span><br><span class="line">        json.dump(<span class="variable language_">self</span>.data, <span class="variable language_">self</span>.file, ensure_ascii=<span class="literal">False</span>, indent=<span class="number">2</span>)</span><br><span class="line">        <span class="variable language_">self</span>.file.close()</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">process_item</span>(<span class="params">self, item, spider</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;处理每一条数据。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="variable language_">self</span>.data.append(<span class="built_in">dict</span>(item))</span><br><span class="line">        <span class="keyword">return</span> item  <span class="comment"># 必须返回 item，交给下一个 Pipeline</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">CsvPipeline</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;将数据保存为 CSV 文件。&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">open_spider</span>(<span class="params">self, spider</span>):</span><br><span class="line">        <span class="variable language_">self</span>.file = <span class="built_in">open</span>(<span class="string">&quot;quotes.csv&quot;</span>, <span class="string">&quot;w&quot;</span>, newline=<span class="string">&quot;&quot;</span>, encoding=<span class="string">&quot;utf-8-sig&quot;</span>)</span><br><span class="line">        <span class="variable language_">self</span>.writer = csv.DictWriter(</span><br><span class="line">            <span class="variable language_">self</span>.file, fieldnames=[<span class="string">&quot;title&quot;</span>, <span class="string">&quot;author&quot;</span>, <span class="string">&quot;tags&quot;</span>, <span class="string">&quot;content&quot;</span>]</span><br><span class="line">        )</span><br><span class="line">        <span class="variable language_">self</span>.writer.writeheader()</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">close_spider</span>(<span class="params">self, spider</span>):</span><br><span class="line">        <span class="variable language_">self</span>.file.close()</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">process_item</span>(<span class="params">self, item, spider</span>):</span><br><span class="line">        <span class="variable language_">self</span>.writer.writerow(<span class="built_in">dict</span>(item))</span><br><span class="line">        <span class="keyword">return</span> item</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">SQLitePipeline</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;将数据保存到 SQLite 数据库。&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">open_spider</span>(<span class="params">self, spider</span>):</span><br><span class="line">        <span class="variable language_">self</span>.conn = sqlite3.connect(<span class="string">&quot;quotes.db&quot;</span>)</span><br><span class="line">        <span class="variable language_">self</span>.cursor = <span class="variable language_">self</span>.conn.cursor()</span><br><span class="line">        <span class="variable language_">self</span>.cursor.execute(<span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">            CREATE TABLE IF NOT EXISTS quotes (</span></span><br><span class="line"><span class="string">                id INTEGER PRIMARY KEY AUTOINCREMENT,</span></span><br><span class="line"><span class="string">                title TEXT,</span></span><br><span class="line"><span class="string">                author TEXT,</span></span><br><span class="line"><span class="string">                tags TEXT,</span></span><br><span class="line"><span class="string">                content TEXT</span></span><br><span class="line"><span class="string">            )</span></span><br><span class="line"><span class="string">        &quot;&quot;&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">close_spider</span>(<span class="params">self, spider</span>):</span><br><span class="line">        <span class="variable language_">self</span>.conn.commit()</span><br><span class="line">        <span class="variable language_">self</span>.conn.close()</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">process_item</span>(<span class="params">self, item, spider</span>):</span><br><span class="line">        <span class="variable language_">self</span>.cursor.execute(</span><br><span class="line">            <span class="string">&quot;INSERT INTO quotes (title, author, tags, content) VALUES (?, ?, ?, ?)&quot;</span>,</span><br><span class="line">            (item[<span class="string">&quot;title&quot;</span>], item[<span class="string">&quot;author&quot;</span>], json.dumps(item[<span class="string">&quot;tags&quot;</span>], ensure_ascii=<span class="literal">False</span>), item[<span class="string">&quot;content&quot;</span>])</span><br><span class="line">        )</span><br><span class="line">        <span class="keyword">return</span> item</span><br></pre></td></tr></table></figure><p><strong>配置 Pipeline（settings.py）</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><span class="line"><span class="comment"># settings.py 中启用 Pipeline（取消注释并修改）</span></span><br><span class="line">ITEM_PIPELINES = &#123;</span><br><span class="line">    <span class="string">&quot;news_spider.pipelines.JsonPipeline&quot;</span>: <span class="number">300</span>,    <span class="comment"># 数字越小优先级越高</span></span><br><span class="line">    <span class="comment"># &quot;news_spider.pipelines.CsvPipeline&quot;: 400,</span></span><br><span class="line">    <span class="comment"># &quot;news_spider.pipelines.SQLitePipeline&quot;: 500,</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 其他常用设置</span></span><br><span class="line">ROBOTSTXT_OBEY = <span class="literal">True</span>          <span class="comment"># 遵守 robots.txt</span></span><br><span class="line">DOWNLOAD_DELAY = <span class="number">2</span>             <span class="comment"># 请求间隔 2 秒</span></span><br><span class="line">CONCURRENT_REQUESTS = <span class="number">8</span>        <span class="comment"># 并发请求数</span></span><br><span class="line">USER_AGENT = <span class="string">&quot;Mozilla/5.0 ...&quot;</span> <span class="comment"># 自定义 UA</span></span><br><span class="line">FEED_EXPORT_ENCODING = <span class="string">&quot;utf-8&quot;</span> <span class="comment"># 导出编码</span></span><br></pre></td></tr></table></figure><p><strong>运行命令</strong>：</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><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><span class="line"><span class="comment"># 运行指定爬虫</span></span><br><span class="line">scrapy crawl quotes</span><br><span class="line"></span><br><span class="line"><span class="comment"># 运行并导出为 JSON 文件</span></span><br><span class="line">scrapy crawl quotes -o quotes.json</span><br><span class="line"></span><br><span class="line"><span class="comment"># 运行并导出为 CSV 文件</span></span><br><span class="line">scrapy crawl quotes -o quotes.csv -t csv</span><br><span class="line"></span><br><span class="line"><span class="comment"># 调试模式（在 Shell 中测试选择器）</span></span><br><span class="line">scrapy shell <span class="string">&quot;https://quotes.toscrape.com/&quot;</span></span><br><span class="line">&gt;&gt;&gt; response.css(<span class="string">&quot;span.text::text&quot;</span>).getall()</span><br></pre></td></tr></table></figure><h3 id="3-5-反反爬技巧"><a href="#3-5-反反爬技巧" class="headerlink" title="3.5 反反爬技巧"></a>3.5 反反爬技巧</h3><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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> random</span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"><span class="keyword">import</span> requests</span><br><span class="line"><span class="keyword">from</span> itertools <span class="keyword">import</span> cycle</span><br><span class="line"></span><br><span class="line"><span class="comment"># ===== 1. User-Agent 轮换 =====</span></span><br><span class="line"><span class="comment"># 每次请求使用不同的 UA，避免被识别为爬虫</span></span><br><span class="line">USER_AGENTS = [</span><br><span class="line">    <span class="string">&quot;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120.0.0.0 Safari/537.36&quot;</span>,</span><br><span class="line">    <span class="string">&quot;Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 Chrome/119.0.0.0 Safari/537.36&quot;</span>,</span><br><span class="line">    <span class="string">&quot;Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 Chrome/120.0.0.0 Safari/537.36&quot;</span>,</span><br><span class="line">    <span class="string">&quot;Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:120.0) Gecko/20100101 Firefox/120.0&quot;</span>,</span><br><span class="line">    <span class="string">&quot;Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 Safari/17.1&quot;</span>,</span><br><span class="line">]</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">get_random_ua</span>():</span><br><span class="line">    <span class="string">&quot;&quot;&quot;随机获取一个 User-Agent。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">return</span> random.choice(USER_AGENTS)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用</span></span><br><span class="line">headers = &#123;<span class="string">&quot;User-Agent&quot;</span>: get_random_ua()&#125;</span><br><span class="line">response = requests.get(<span class="string">&quot;https://httpbin.org/user-agent&quot;</span>, headers=headers, timeout=<span class="number">10</span>)</span><br><span class="line"><span class="built_in">print</span>(response.json()[<span class="string">&quot;user-agent&quot;</span>])</span><br><span class="line"></span><br><span class="line"><span class="comment"># ===== 2. 代理池 =====</span></span><br><span class="line"><span class="comment"># 通过代理 IP 发送请求，隐藏真实 IP</span></span><br><span class="line">PROXIES = [</span><br><span class="line">    <span class="string">&quot;http://proxy1:8080&quot;</span>,</span><br><span class="line">    <span class="string">&quot;http://proxy2:8080&quot;</span>,</span><br><span class="line">    <span class="string">&quot;http://proxy3:8080&quot;</span>,</span><br><span class="line">    <span class="comment"># 实际使用中从代理服务商获取，如快代理、芝麻代理等</span></span><br><span class="line">]</span><br><span class="line"></span><br><span class="line">proxy_pool = cycle(PROXIES)  <span class="comment"># 循环使用代理</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">request_with_proxy</span>(<span class="params">url, max_retries=<span class="number">3</span></span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;使用代理池发送请求，失败自动重试。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">for</span> attempt <span class="keyword">in</span> <span class="built_in">range</span>(max_retries):</span><br><span class="line">        proxy = <span class="built_in">next</span>(proxy_pool)</span><br><span class="line">        proxies = &#123;<span class="string">&quot;http&quot;</span>: proxy, <span class="string">&quot;https&quot;</span>: proxy&#125;</span><br><span class="line">        <span class="keyword">try</span>:</span><br><span class="line">            response = requests.get(</span><br><span class="line">                url,</span><br><span class="line">                headers=&#123;<span class="string">&quot;User-Agent&quot;</span>: get_random_ua()&#125;,</span><br><span class="line">                proxies=proxies,</span><br><span class="line">                timeout=<span class="number">10</span>,</span><br><span class="line">            )</span><br><span class="line">            <span class="keyword">if</span> response.status_code == <span class="number">200</span>:</span><br><span class="line">                <span class="keyword">return</span> response</span><br><span class="line">        <span class="keyword">except</span> requests.RequestException <span class="keyword">as</span> e:</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">f&quot;代理 <span class="subst">&#123;proxy&#125;</span> 请求失败（第 <span class="subst">&#123;attempt + <span class="number">1</span>&#125;</span> 次）: <span class="subst">&#123;e&#125;</span>&quot;</span>)</span><br><span class="line">            <span class="keyword">continue</span></span><br><span class="line">    <span class="keyword">return</span> <span class="literal">None</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ===== 3. 请求频率控制 =====</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">polite_crawl</span>(<span class="params">urls, min_delay=<span class="number">1</span>, max_delay=<span class="number">3</span></span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;礼貌爬取：控制请求频率，避免给服务器造成压力。&quot;&quot;&quot;</span></span><br><span class="line">    results = []</span><br><span class="line">    session = requests.Session()</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> i, url <span class="keyword">in</span> <span class="built_in">enumerate</span>(urls):</span><br><span class="line">        <span class="comment"># 随机延迟 1~3 秒</span></span><br><span class="line">        delay = random.uniform(min_delay, max_delay)</span><br><span class="line">        time.sleep(delay)</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span>:</span><br><span class="line">            response = session.get(</span><br><span class="line">                url,</span><br><span class="line">                headers=&#123;<span class="string">&quot;User-Agent&quot;</span>: get_random_ua()&#125;,</span><br><span class="line">                timeout=<span class="number">10</span>,</span><br><span class="line">            )</span><br><span class="line">            <span class="keyword">if</span> response.status_code == <span class="number">200</span>:</span><br><span class="line">                results.append(response)</span><br><span class="line">                <span class="built_in">print</span>(<span class="string">f&quot;[<span class="subst">&#123;i + <span class="number">1</span>&#125;</span>/<span class="subst">&#123;<span class="built_in">len</span>(urls)&#125;</span>] 成功: <span class="subst">&#123;url&#125;</span>&quot;</span>)</span><br><span class="line">            <span class="keyword">else</span>:</span><br><span class="line">                <span class="built_in">print</span>(<span class="string">f&quot;[<span class="subst">&#123;i + <span class="number">1</span>&#125;</span>/<span class="subst">&#123;<span class="built_in">len</span>(urls)&#125;</span>] 状态码 <span class="subst">&#123;response.status_code&#125;</span>: <span class="subst">&#123;url&#125;</span>&quot;</span>)</span><br><span class="line">        <span class="keyword">except</span> requests.RequestException <span class="keyword">as</span> e:</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">f&quot;[<span class="subst">&#123;i + <span class="number">1</span>&#125;</span>/<span class="subst">&#123;<span class="built_in">len</span>(urls)&#125;</span>] 失败: <span class="subst">&#123;url&#125;</span> - <span class="subst">&#123;e&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> results</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用</span></span><br><span class="line"><span class="comment"># urls = [&quot;https://example.com/page/1&quot;, &quot;https://example.com/page/2&quot;]</span></span><br><span class="line"><span class="comment"># responses = polite_crawl(urls)</span></span><br></pre></td></tr></table></figure><h3 id="3-6-完整案例：新闻网站爬虫"><a href="#3-6-完整案例：新闻网站爬虫" class="headerlink" title="3.6 完整案例：新闻网站爬虫"></a>3.6 完整案例：新闻网站爬虫</h3><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><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></pre></td><td class="code"><pre><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">完整案例：新闻网站爬虫</span></span><br><span class="line"><span class="string">目标网站：https://quotes.toscrape.com/（专为学习爬虫设计的网站）</span></span><br><span class="line"><span class="string">功能：采集名言、作者、标签，保存为 CSV 和 JSON</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> csv</span><br><span class="line"><span class="keyword">import</span> json</span><br><span class="line"><span class="keyword">import</span> random</span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"><span class="keyword">import</span> requests</span><br><span class="line"><span class="keyword">from</span> bs4 <span class="keyword">import</span> BeautifulSoup</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">NewsSpider</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;新闻网站爬虫（requests + BeautifulSoup 版）。&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="variable language_">self</span>.base_url = <span class="string">&quot;https://quotes.toscrape.com&quot;</span></span><br><span class="line">        <span class="variable language_">self</span>.session = requests.Session()</span><br><span class="line">        <span class="variable language_">self</span>.session.headers.update(&#123;</span><br><span class="line">            <span class="string">&quot;User-Agent&quot;</span>: <span class="string">&quot;Mozilla/5.0 (Windows NT 10.0; Win64; x64) &quot;</span></span><br><span class="line">                          <span class="string">&quot;AppleWebKit/537.36 Chrome/120.0.0.0 Safari/537.36&quot;</span>,</span><br><span class="line">        &#125;)</span><br><span class="line">        <span class="variable language_">self</span>.results = []</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">fetch_page</span>(<span class="params">self, url</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;发送请求并返回 BeautifulSoup 对象。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">try</span>:</span><br><span class="line">            response = <span class="variable language_">self</span>.session.get(url, timeout=<span class="number">10</span>)</span><br><span class="line">            response.raise_for_status()  <span class="comment"># 状态码非 200 抛异常</span></span><br><span class="line">            <span class="keyword">return</span> BeautifulSoup(response.text, <span class="string">&quot;lxml&quot;</span>)</span><br><span class="line">        <span class="keyword">except</span> requests.RequestException <span class="keyword">as</span> e:</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">f&quot;请求失败: <span class="subst">&#123;url&#125;</span> - <span class="subst">&#123;e&#125;</span>&quot;</span>)</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">None</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">parse_page</span>(<span class="params">self, soup</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;解析单个页面，提取数据。&quot;&quot;&quot;</span></span><br><span class="line">        items = []</span><br><span class="line">        <span class="keyword">for</span> quote <span class="keyword">in</span> soup.select(<span class="string">&quot;div.quote&quot;</span>):</span><br><span class="line">            item = &#123;</span><br><span class="line">                <span class="string">&quot;text&quot;</span>: quote.select_one(<span class="string">&quot;span.text&quot;</span>).get_text(),</span><br><span class="line">                <span class="string">&quot;author&quot;</span>: quote.select_one(<span class="string">&quot;small.author&quot;</span>).get_text(),</span><br><span class="line">                <span class="string">&quot;tags&quot;</span>: [tag.get_text() <span class="keyword">for</span> tag <span class="keyword">in</span> quote.select(<span class="string">&quot;div.tags a.tag&quot;</span>)],</span><br><span class="line">            &#125;</span><br><span class="line">            items.append(item)</span><br><span class="line">        <span class="keyword">return</span> items</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_next_page</span>(<span class="params">self, soup</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;获取下一页的 URL，没有则返回 None。&quot;&quot;&quot;</span></span><br><span class="line">        next_link = soup.select_one(<span class="string">&quot;li.next a&quot;</span>)</span><br><span class="line">        <span class="keyword">if</span> next_link:</span><br><span class="line">            <span class="keyword">return</span> <span class="variable language_">self</span>.base_url + next_link[<span class="string">&quot;href&quot;</span>]</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">None</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">crawl</span>(<span class="params">self, max_pages=<span class="number">5</span></span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;主爬取逻辑。&quot;&quot;&quot;</span></span><br><span class="line">        url = <span class="variable language_">self</span>.base_url + <span class="string">&quot;/page/1/&quot;</span></span><br><span class="line">        page_count = <span class="number">0</span></span><br><span class="line"></span><br><span class="line">        <span class="keyword">while</span> url <span class="keyword">and</span> page_count &lt; max_pages:</span><br><span class="line">            page_count += <span class="number">1</span></span><br><span class="line">            <span class="built_in">print</span>(<span class="string">f&quot;正在爬取第 <span class="subst">&#123;page_count&#125;</span> 页: <span class="subst">&#123;url&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line">            soup = <span class="variable language_">self</span>.fetch_page(url)</span><br><span class="line">            <span class="keyword">if</span> <span class="keyword">not</span> soup:</span><br><span class="line">                <span class="keyword">break</span></span><br><span class="line"></span><br><span class="line">            <span class="comment"># 提取数据</span></span><br><span class="line">            items = <span class="variable language_">self</span>.parse_page(soup)</span><br><span class="line">            <span class="variable language_">self</span>.results.extend(items)</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">f&quot;  本页采集 <span class="subst">&#123;<span class="built_in">len</span>(items)&#125;</span> 条数据&quot;</span>)</span><br><span class="line"></span><br><span class="line">            <span class="comment"># 获取下一页</span></span><br><span class="line">            url = <span class="variable language_">self</span>.get_next_page(soup)</span><br><span class="line"></span><br><span class="line">            <span class="comment"># 礼貌延迟</span></span><br><span class="line">            time.sleep(random.uniform(<span class="number">1</span>, <span class="number">2</span>))</span><br><span class="line"></span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;\n爬取完成！共采集 <span class="subst">&#123;<span class="built_in">len</span>(self.results)&#125;</span> 条数据&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">save_csv</span>(<span class="params">self, filename=<span class="string">&quot;quotes.csv&quot;</span></span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;保存为 CSV。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">with</span> <span class="built_in">open</span>(filename, <span class="string">&quot;w&quot;</span>, newline=<span class="string">&quot;&quot;</span>, encoding=<span class="string">&quot;utf-8-sig&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">            writer = csv.DictWriter(f, fieldnames=[<span class="string">&quot;text&quot;</span>, <span class="string">&quot;author&quot;</span>, <span class="string">&quot;tags&quot;</span>])</span><br><span class="line">            writer.writeheader()</span><br><span class="line">            <span class="keyword">for</span> item <span class="keyword">in</span> <span class="variable language_">self</span>.results:</span><br><span class="line">                writer.writerow(&#123;</span><br><span class="line">                    <span class="string">&quot;text&quot;</span>: item[<span class="string">&quot;text&quot;</span>],</span><br><span class="line">                    <span class="string">&quot;author&quot;</span>: item[<span class="string">&quot;author&quot;</span>],</span><br><span class="line">                    <span class="string">&quot;tags&quot;</span>: <span class="string">&quot;|&quot;</span>.join(item[<span class="string">&quot;tags&quot;</span>]),  <span class="comment"># 列表转字符串</span></span><br><span class="line">                &#125;)</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;已保存到 <span class="subst">&#123;filename&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">save_json</span>(<span class="params">self, filename=<span class="string">&quot;quotes.json&quot;</span></span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;保存为 JSON。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">with</span> <span class="built_in">open</span>(filename, <span class="string">&quot;w&quot;</span>, encoding=<span class="string">&quot;utf-8&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">            json.dump(<span class="variable language_">self</span>.results, f, ensure_ascii=<span class="literal">False</span>, indent=<span class="number">2</span>)</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;已保存到 <span class="subst">&#123;filename&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># ===== 运行 =====</span></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    spider = NewsSpider()</span><br><span class="line">    spider.crawl(max_pages=<span class="number">5</span>)</span><br><span class="line">    spider.save_csv()</span><br><span class="line">    spider.save_json()</span><br></pre></td></tr></table></figure><h3 id="3-7-常见新手坑"><a href="#3-7-常见新手坑" class="headerlink" title="3.7 常见新手坑"></a>3.7 常见新手坑</h3><table><thead><tr><th>坑</th><th>说明</th><th>解决方案</th></tr></thead><tbody><tr><td><strong>不设请求头</strong></td><td>服务器返回 403 或空内容</td><td>始终添加 User-Agent</td></tr><tr><td><strong>不设超时</strong></td><td>程序卡死在某个请求上</td><td>所有请求加 <code>timeout=10</code></td></tr><tr><td><strong>频率太高</strong></td><td>IP 被封，或给服务器造成压力</td><td>每次请求后 <code>time.sleep(1~3)</code></td></tr><tr><td><strong>编码错误</strong></td><td>中文乱码</td><td>用 <code>response.encoding = response.apparent_encoding</code> 或指定 <code>utf-8</code></td></tr><tr><td><strong>只看 HTML</strong></td><td>数据是 JS 动态加载的</td><td>用 Playwright 或找 API 接口</td></tr><tr><td><strong>不处理异常</strong></td><td>网络波动导致程序崩溃</td><td>用 try-except 包裹请求代码</td></tr><tr><td><strong>无视 robots.txt</strong></td><td>可能面临法律风险</td><td>先检查 <code>域名/robots.txt</code></td></tr><tr><td><strong>直接爬生产环境</strong></td><td>高并发影响正常用户</td><td>优先找官方 API，限制并发数</td></tr></tbody></table><hr><h2 id="4-自动化运维与-DevOps"><a href="#4-自动化运维与-DevOps" class="headerlink" title="4. 自动化运维与 DevOps"></a>4. 自动化运维与 DevOps</h2><h3 id="4-1-DevOps-是什么？日常工作"><a href="#4-1-DevOps-是什么？日常工作" class="headerlink" title="4.1 DevOps 是什么？日常工作"></a>4.1 DevOps 是什么？日常工作</h3><p>DevOps 是 Development（开发）和 Operations（运维）的组合词，核心理念是<strong>打通开发与运维的壁垒，通过自动化实现软件的快速、可靠交付</strong>。</p><p><strong>运维工程师的日常工作</strong>：</p><figure class="highlight plaintext"><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><span class="line">日常运维工作：</span><br><span class="line">├── 服务器管理</span><br><span class="line">│   ├── 部署新服务、扩容缩容</span><br><span class="line">│   ├── 监控服务器状态（CPU/内存/磁盘/网络）</span><br><span class="line">│   └── 处理告警和故障</span><br><span class="line">├── 自动化脚本</span><br><span class="line">│   ├── 日志清理、备份归档</span><br><span class="line">│   ├── 批量文件操作</span><br><span class="line">│   └── 定时任务管理</span><br><span class="line">├── CI/CD 流水线</span><br><span class="line">│   ├── 代码提交 → 自动测试 → 自动构建 → 自动部署</span><br><span class="line">│   └── 管理 GitHub Actions / Jenkins / GitLab CI</span><br><span class="line">├── 容器与编排</span><br><span class="line">│   ├── Docker 镜像构建与管理</span><br><span class="line">│   └── K8s 集群维护</span><br><span class="line">└── 云资源管理</span><br><span class="line">    ├── AWS / 阿里云资源操作</span><br><span class="line">    └── 成本监控与优化</span><br></pre></td></tr></table></figure><blockquote><p><strong>一句话理解</strong>：DevOps &#x3D; 用代码和自动化工具来管理服务器、部署应用、监控状态，减少手动操作和人为失误。</p></blockquote><h3 id="4-2-从零上手：Python-运维脚本"><a href="#4-2-从零上手：Python-运维脚本" class="headerlink" title="4.2 从零上手：Python 运维脚本"></a>4.2 从零上手：Python 运维脚本</h3><h4 id="文件批量管理（查找大文件、清理日志、备份）"><a href="#文件批量管理（查找大文件、清理日志、备份）" class="headerlink" title="文件批量管理（查找大文件、清理日志、备份）"></a>文件批量管理（查找大文件、清理日志、备份）</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><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></pre></td><td class="code"><pre><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">文件批量管理脚本</span></span><br><span class="line"><span class="string">功能：查找大文件、清理过期日志、自动备份</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"><span class="keyword">import</span> shutil</span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"><span class="keyword">from</span> pathlib <span class="keyword">import</span> Path</span><br><span class="line"><span class="keyword">from</span> datetime <span class="keyword">import</span> datetime, timedelta</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">find_large_files</span>(<span class="params">directory: <span class="built_in">str</span>, size_mb: <span class="built_in">int</span> = <span class="number">100</span></span>) -&gt; <span class="built_in">list</span>[<span class="built_in">dict</span>]:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;查找指定目录下超过 size_mb 的大文件。&quot;&quot;&quot;</span></span><br><span class="line">    threshold = size_mb * <span class="number">1024</span> * <span class="number">1024</span>  <span class="comment"># 转换为字节</span></span><br><span class="line">    large_files = []</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> root, dirs, files <span class="keyword">in</span> os.walk(directory):</span><br><span class="line">        <span class="keyword">for</span> filename <span class="keyword">in</span> files:</span><br><span class="line">            filepath = os.path.join(root, filename)</span><br><span class="line">            <span class="keyword">try</span>:</span><br><span class="line">                file_size = os.path.getsize(filepath)</span><br><span class="line">                <span class="keyword">if</span> file_size &gt; threshold:</span><br><span class="line">                    large_files.append(&#123;</span><br><span class="line">                        <span class="string">&quot;path&quot;</span>: filepath,</span><br><span class="line">                        <span class="string">&quot;size_mb&quot;</span>: <span class="built_in">round</span>(file_size / (<span class="number">1024</span> * <span class="number">1024</span>), <span class="number">2</span>),</span><br><span class="line">                        <span class="string">&quot;modified&quot;</span>: datetime.fromtimestamp(</span><br><span class="line">                            os.path.getmtime(filepath)</span><br><span class="line">                        ).strftime(<span class="string">&quot;%Y-%m-%d %H:%M&quot;</span>),</span><br><span class="line">                    &#125;)</span><br><span class="line">            <span class="keyword">except</span> (PermissionError, FileNotFoundError):</span><br><span class="line">                <span class="keyword">continue</span>  <span class="comment"># 跳过无权限或已删除的文件</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 按大小降序排列</span></span><br><span class="line">    large_files.sort(key=<span class="keyword">lambda</span> x: x[<span class="string">&quot;size_mb&quot;</span>], reverse=<span class="literal">True</span>)</span><br><span class="line">    <span class="keyword">return</span> large_files</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">clean_old_logs</span>(<span class="params">log_dir: <span class="built_in">str</span>, days: <span class="built_in">int</span> = <span class="number">30</span></span>) -&gt; <span class="built_in">list</span>[<span class="built_in">str</span>]:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;清理超过 days 天的旧日志文件。&quot;&quot;&quot;</span></span><br><span class="line">    cutoff = time.time() - days * <span class="number">86400</span>  <span class="comment"># 计算截止时间戳</span></span><br><span class="line">    deleted = []</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> log_file <span class="keyword">in</span> Path(log_dir).rglob(<span class="string">&quot;*.log&quot;</span>):</span><br><span class="line">        <span class="comment"># 也处理 .log.gz 等压缩日志</span></span><br><span class="line">        <span class="keyword">if</span> log_file.stat().st_mtime &lt; cutoff:</span><br><span class="line">            <span class="keyword">try</span>:</span><br><span class="line">                log_file.unlink()  <span class="comment"># 删除文件</span></span><br><span class="line">                deleted.append(<span class="built_in">str</span>(log_file))</span><br><span class="line">            <span class="keyword">except</span> PermissionError:</span><br><span class="line">                <span class="keyword">continue</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 同时清理空的日志目录</span></span><br><span class="line">    <span class="keyword">for</span> subdir <span class="keyword">in</span> <span class="built_in">sorted</span>(Path(log_dir).rglob(<span class="string">&quot;*&quot;</span>), reverse=<span class="literal">True</span>):</span><br><span class="line">        <span class="keyword">if</span> subdir.is_dir() <span class="keyword">and</span> <span class="keyword">not</span> <span class="built_in">any</span>(subdir.iterdir()):</span><br><span class="line">            subdir.rmdir()</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> deleted</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">backup_directory</span>(<span class="params"></span></span><br><span class="line"><span class="params">    src: <span class="built_in">str</span>, dst: <span class="built_in">str</span>, exclude: <span class="built_in">list</span>[<span class="built_in">str</span>] | <span class="literal">None</span> = <span class="literal">None</span></span></span><br><span class="line"><span class="params"></span>) -&gt; <span class="built_in">str</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;将 src 目录备份到 dst，自动添加日期后缀。&quot;&quot;&quot;</span></span><br><span class="line">    exclude = exclude <span class="keyword">or</span> []</span><br><span class="line">    timestamp = datetime.now().strftime(<span class="string">&quot;%Y%m%d_%H%M%S&quot;</span>)</span><br><span class="line">    backup_name = <span class="string">f&quot;<span class="subst">&#123;Path(src).name&#125;</span>_backup_<span class="subst">&#123;timestamp&#125;</span>&quot;</span></span><br><span class="line">    backup_path = os.path.join(dst, backup_name)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 如果目标目录不存在，先创建</span></span><br><span class="line">    os.makedirs(dst, exist_ok=<span class="literal">True</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 复制整个目录，忽略指定文件</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">ignore_patterns</span>(<span class="params">path, names</span>):</span><br><span class="line">        <span class="keyword">return</span> [n <span class="keyword">for</span> n <span class="keyword">in</span> names <span class="keyword">if</span> n <span class="keyword">in</span> exclude]</span><br><span class="line"></span><br><span class="line">    shutil.copytree(src, backup_path, ignore=ignore_patterns)</span><br><span class="line">    <span class="keyword">return</span> backup_path</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># ===== 使用示例 =====</span></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    <span class="comment"># 1. 查找大文件</span></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;=== 查找大文件 ===&quot;</span>)</span><br><span class="line">    big_files = find_large_files(<span class="string">&quot;/var/log&quot;</span>, size_mb=<span class="number">10</span>)</span><br><span class="line">    <span class="keyword">for</span> f <span class="keyword">in</span> big_files[:<span class="number">10</span>]:  <span class="comment"># 只显示前 10 个</span></span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;  <span class="subst">&#123;f[<span class="string">&#x27;size_mb&#x27;</span>]&#125;</span> MB  <span class="subst">&#123;f[<span class="string">&#x27;modified&#x27;</span>]&#125;</span>  <span class="subst">&#123;f[<span class="string">&#x27;path&#x27;</span>]&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 2. 清理 30 天前的旧日志</span></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;\n=== 清理旧日志 ===&quot;</span>)</span><br><span class="line">    deleted_logs = clean_old_logs(<span class="string">&quot;/var/log/app&quot;</span>, days=<span class="number">30</span>)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;  已清理 <span class="subst">&#123;<span class="built_in">len</span>(deleted_logs)&#125;</span> 个日志文件&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 3. 备份目录</span></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;\n=== 备份目录 ===&quot;</span>)</span><br><span class="line">    backup_path = backup_directory(</span><br><span class="line">        src=<span class="string">&quot;/home/user/project&quot;</span>,</span><br><span class="line">        dst=<span class="string">&quot;/backup&quot;</span>,</span><br><span class="line">        exclude=[<span class="string">&quot;__pycache__&quot;</span>, <span class="string">&quot;.git&quot;</span>, <span class="string">&quot;node_modules&quot;</span>],</span><br><span class="line">    )</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;  备份完成: <span class="subst">&#123;backup_path&#125;</span>&quot;</span>)</span><br></pre></td></tr></table></figure><h4 id="系统监控脚本（CPU-内存-磁盘）"><a href="#系统监控脚本（CPU-内存-磁盘）" class="headerlink" title="系统监控脚本（CPU &#x2F; 内存 &#x2F; 磁盘）"></a>系统监控脚本（CPU &#x2F; 内存 &#x2F; 磁盘）</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><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><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">系统监控脚本</span></span><br><span class="line"><span class="string">功能：实时监控 CPU、内存、磁盘，超阈值自动告警</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="keyword">import</span> psutil</span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"><span class="keyword">import</span> json</span><br><span class="line"><span class="keyword">from</span> datetime <span class="keyword">import</span> datetime</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">SystemMonitor</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;系统资源监控器。&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params"></span></span><br><span class="line"><span class="params">        self,</span></span><br><span class="line"><span class="params">        cpu_threshold: <span class="built_in">float</span> = <span class="number">80.0</span>,</span></span><br><span class="line"><span class="params">        memory_threshold: <span class="built_in">float</span> = <span class="number">85.0</span>,</span></span><br><span class="line"><span class="params">        disk_threshold: <span class="built_in">float</span> = <span class="number">90.0</span>,</span></span><br><span class="line"><span class="params">    </span>):</span><br><span class="line">        <span class="variable language_">self</span>.cpu_threshold = cpu_threshold        <span class="comment"># CPU 告警阈值（%）</span></span><br><span class="line">        <span class="variable language_">self</span>.memory_threshold = memory_threshold  <span class="comment"># 内存告警阈值（%）</span></span><br><span class="line">        <span class="variable language_">self</span>.disk_threshold = disk_threshold      <span class="comment"># 磁盘告警阈值（%）</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_cpu_info</span>(<span class="params">self</span>) -&gt; <span class="built_in">dict</span>:</span><br><span class="line">        <span class="string">&quot;&quot;&quot;获取 CPU 信息。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">return</span> &#123;</span><br><span class="line">            <span class="string">&quot;percent&quot;</span>: psutil.cpu_percent(interval=<span class="number">1</span>),     <span class="comment"># 总使用率</span></span><br><span class="line">            <span class="string">&quot;per_cpu&quot;</span>: psutil.cpu_percent(interval=<span class="number">1</span>, percpu=<span class="literal">True</span>),  <span class="comment"># 每核使用率</span></span><br><span class="line">            <span class="string">&quot;count_logical&quot;</span>: psutil.cpu_count(),           <span class="comment"># 逻辑核心数</span></span><br><span class="line">            <span class="string">&quot;count_physical&quot;</span>: psutil.cpu_count(logical=<span class="literal">False</span>),  <span class="comment"># 物理核心数</span></span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_memory_info</span>(<span class="params">self</span>) -&gt; <span class="built_in">dict</span>:</span><br><span class="line">        <span class="string">&quot;&quot;&quot;获取内存信息。&quot;&quot;&quot;</span></span><br><span class="line">        mem = psutil.virtual_memory()</span><br><span class="line">        <span class="keyword">return</span> &#123;</span><br><span class="line">            <span class="string">&quot;total_gb&quot;</span>: <span class="built_in">round</span>(mem.total / (<span class="number">1024</span> ** <span class="number">3</span>), <span class="number">2</span>),</span><br><span class="line">            <span class="string">&quot;used_gb&quot;</span>: <span class="built_in">round</span>(mem.used / (<span class="number">1024</span> ** <span class="number">3</span>), <span class="number">2</span>),</span><br><span class="line">            <span class="string">&quot;available_gb&quot;</span>: <span class="built_in">round</span>(mem.available / (<span class="number">1024</span> ** <span class="number">3</span>), <span class="number">2</span>),</span><br><span class="line">            <span class="string">&quot;percent&quot;</span>: mem.percent,</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_disk_info</span>(<span class="params">self</span>) -&gt; <span class="built_in">list</span>[<span class="built_in">dict</span>]:</span><br><span class="line">        <span class="string">&quot;&quot;&quot;获取所有磁盘分区信息。&quot;&quot;&quot;</span></span><br><span class="line">        disks = []</span><br><span class="line">        <span class="keyword">for</span> partition <span class="keyword">in</span> psutil.disk_partitions():</span><br><span class="line">            <span class="keyword">try</span>:</span><br><span class="line">                usage = psutil.disk_usage(partition.mountpoint)</span><br><span class="line">                disks.append(&#123;</span><br><span class="line">                    <span class="string">&quot;mountpoint&quot;</span>: partition.mountpoint,</span><br><span class="line">                    <span class="string">&quot;device&quot;</span>: partition.device,</span><br><span class="line">                    <span class="string">&quot;fstype&quot;</span>: partition.fstype,</span><br><span class="line">                    <span class="string">&quot;total_gb&quot;</span>: <span class="built_in">round</span>(usage.total / (<span class="number">1024</span> ** <span class="number">3</span>), <span class="number">2</span>),</span><br><span class="line">                    <span class="string">&quot;used_gb&quot;</span>: <span class="built_in">round</span>(usage.used / (<span class="number">1024</span> ** <span class="number">3</span>), <span class="number">2</span>),</span><br><span class="line">                    <span class="string">&quot;free_gb&quot;</span>: <span class="built_in">round</span>(usage.free / (<span class="number">1024</span> ** <span class="number">3</span>), <span class="number">2</span>),</span><br><span class="line">                    <span class="string">&quot;percent&quot;</span>: usage.percent,</span><br><span class="line">                &#125;)</span><br><span class="line">            <span class="keyword">except</span> (PermissionError, FileNotFoundError):</span><br><span class="line">                <span class="keyword">continue</span></span><br><span class="line">        <span class="keyword">return</span> disks</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_top_processes</span>(<span class="params">self, n: <span class="built_in">int</span> = <span class="number">5</span></span>) -&gt; <span class="built_in">list</span>[<span class="built_in">dict</span>]:</span><br><span class="line">        <span class="string">&quot;&quot;&quot;获取内存占用最高的前 n 个进程。&quot;&quot;&quot;</span></span><br><span class="line">        processes = []</span><br><span class="line">        <span class="keyword">for</span> proc <span class="keyword">in</span> psutil.process_iter([<span class="string">&quot;pid&quot;</span>, <span class="string">&quot;name&quot;</span>, <span class="string">&quot;memory_percent&quot;</span>, <span class="string">&quot;cpu_percent&quot;</span>]):</span><br><span class="line">            <span class="keyword">try</span>:</span><br><span class="line">                processes.append(proc.info)</span><br><span class="line">            <span class="keyword">except</span> (psutil.NoSuchProcess, psutil.AccessDenied):</span><br><span class="line">                <span class="keyword">continue</span></span><br><span class="line"></span><br><span class="line">        <span class="comment"># 按内存占用降序排列</span></span><br><span class="line">        processes.sort(key=<span class="keyword">lambda</span> x: x.get(<span class="string">&quot;memory_percent&quot;</span>, <span class="number">0</span>), reverse=<span class="literal">True</span>)</span><br><span class="line">        <span class="keyword">return</span> processes[:n]</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">check_alerts</span>(<span class="params">self</span>) -&gt; <span class="built_in">list</span>[<span class="built_in">str</span>]:</span><br><span class="line">        <span class="string">&quot;&quot;&quot;检查是否有资源超阈值，返回告警列表。&quot;&quot;&quot;</span></span><br><span class="line">        alerts = []</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 检查 CPU</span></span><br><span class="line">        cpu_percent = psutil.cpu_percent(interval=<span class="number">1</span>)</span><br><span class="line">        <span class="keyword">if</span> cpu_percent &gt; <span class="variable language_">self</span>.cpu_threshold:</span><br><span class="line">            alerts.append(<span class="string">f&quot;⚠ CPU 使用率 <span class="subst">&#123;cpu_percent&#125;</span>% 超过阈值 <span class="subst">&#123;self.cpu_threshold&#125;</span>%&quot;</span>)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 检查内存</span></span><br><span class="line">        mem = psutil.virtual_memory()</span><br><span class="line">        <span class="keyword">if</span> mem.percent &gt; <span class="variable language_">self</span>.memory_threshold:</span><br><span class="line">            alerts.append(<span class="string">f&quot;⚠ 内存使用率 <span class="subst">&#123;mem.percent&#125;</span>% 超过阈值 <span class="subst">&#123;self.memory_threshold&#125;</span>%&quot;</span>)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 检查磁盘</span></span><br><span class="line">        <span class="keyword">for</span> partition <span class="keyword">in</span> psutil.disk_partitions():</span><br><span class="line">            <span class="keyword">try</span>:</span><br><span class="line">                usage = psutil.disk_usage(partition.mountpoint)</span><br><span class="line">                <span class="keyword">if</span> usage.percent &gt; <span class="variable language_">self</span>.disk_threshold:</span><br><span class="line">                    alerts.append(</span><br><span class="line">                        <span class="string">f&quot;⚠ 磁盘 <span class="subst">&#123;partition.mountpoint&#125;</span> 使用率 <span class="subst">&#123;usage.percent&#125;</span>% &quot;</span></span><br><span class="line">                        <span class="string">f&quot;超过阈值 <span class="subst">&#123;self.disk_threshold&#125;</span>%&quot;</span></span><br><span class="line">                    )</span><br><span class="line">            <span class="keyword">except</span> (PermissionError, FileNotFoundError):</span><br><span class="line">                <span class="keyword">continue</span></span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> alerts</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">run</span>(<span class="params">self, interval: <span class="built_in">int</span> = <span class="number">60</span>, times: <span class="built_in">int</span> = <span class="number">0</span></span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">        持续监控。</span></span><br><span class="line"><span class="string">        interval: 检查间隔（秒）</span></span><br><span class="line"><span class="string">        times: 检查次数，0 表示无限</span></span><br><span class="line"><span class="string">        &quot;&quot;&quot;</span></span><br><span class="line">        count = <span class="number">0</span></span><br><span class="line">        <span class="keyword">while</span> times == <span class="number">0</span> <span class="keyword">or</span> count &lt; times:</span><br><span class="line">            count += <span class="number">1</span></span><br><span class="line">            timestamp = datetime.now().strftime(<span class="string">&quot;%Y-%m-%d %H:%M:%S&quot;</span>)</span><br><span class="line"></span><br><span class="line">            <span class="built_in">print</span>(<span class="string">f&quot;\n<span class="subst">&#123;<span class="string">&#x27;=&#x27;</span>*<span class="number">50</span>&#125;</span>&quot;</span>)</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">f&quot;系统监控报告 [<span class="subst">&#123;timestamp&#125;</span>]  (第 <span class="subst">&#123;count&#125;</span> 次检查)&quot;</span>)</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">f&quot;<span class="subst">&#123;<span class="string">&#x27;=&#x27;</span>*<span class="number">50</span>&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line">            <span class="comment"># CPU 信息</span></span><br><span class="line">            cpu = <span class="variable language_">self</span>.get_cpu_info()</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">f&quot;\n📊 CPU 使用率: <span class="subst">&#123;cpu[<span class="string">&#x27;percent&#x27;</span>]&#125;</span>%  (共 <span class="subst">&#123;cpu[<span class="string">&#x27;count_logical&#x27;</span>]&#125;</span> 核)&quot;</span>)</span><br><span class="line">            <span class="keyword">if</span> <span class="built_in">len</span>(cpu[<span class="string">&#x27;per_cpu&#x27;</span>]) &lt;= <span class="number">8</span>:</span><br><span class="line">                <span class="keyword">for</span> i, pct <span class="keyword">in</span> <span class="built_in">enumerate</span>(cpu[<span class="string">&#x27;per_cpu&#x27;</span>]):</span><br><span class="line">                    bar = <span class="string">&quot;█&quot;</span> * <span class="built_in">int</span>(pct / <span class="number">5</span>) + <span class="string">&quot;░&quot;</span> * (<span class="number">20</span> - <span class="built_in">int</span>(pct / <span class="number">5</span>))</span><br><span class="line">                    <span class="built_in">print</span>(<span class="string">f&quot;  核心 <span class="subst">&#123;i&#125;</span>: [<span class="subst">&#123;bar&#125;</span>] <span class="subst">&#123;pct&#125;</span>%&quot;</span>)</span><br><span class="line"></span><br><span class="line">            <span class="comment"># 内存信息</span></span><br><span class="line">            mem = <span class="variable language_">self</span>.get_memory_info()</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">f&quot;\n💾 内存: <span class="subst">&#123;mem[<span class="string">&#x27;used_gb&#x27;</span>]&#125;</span>/<span class="subst">&#123;mem[<span class="string">&#x27;total_gb&#x27;</span>]&#125;</span> GB (<span class="subst">&#123;mem[<span class="string">&#x27;percent&#x27;</span>]&#125;</span>%)&quot;</span>)</span><br><span class="line">            bar_len = <span class="built_in">int</span>(mem[<span class="string">&#x27;percent&#x27;</span>] / <span class="number">5</span>)</span><br><span class="line">            bar = <span class="string">&quot;█&quot;</span> * bar_len + <span class="string">&quot;░&quot;</span> * (<span class="number">20</span> - bar_len)</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">f&quot;  [<span class="subst">&#123;bar&#125;</span>] <span class="subst">&#123;mem[<span class="string">&#x27;percent&#x27;</span>]&#125;</span>%&quot;</span>)</span><br><span class="line"></span><br><span class="line">            <span class="comment"># 磁盘信息</span></span><br><span class="line">            <span class="built_in">print</span>(<span class="string">f&quot;\n💽 磁盘:&quot;</span>)</span><br><span class="line">            <span class="keyword">for</span> disk <span class="keyword">in</span> <span class="variable language_">self</span>.get_disk_info():</span><br><span class="line">                <span class="built_in">print</span>(<span class="string">f&quot;  <span class="subst">&#123;disk[<span class="string">&#x27;mountpoint&#x27;</span>]&#125;</span>: &quot;</span></span><br><span class="line">                      <span class="string">f&quot;<span class="subst">&#123;disk[<span class="string">&#x27;used_gb&#x27;</span>]&#125;</span>/<span class="subst">&#123;disk[<span class="string">&#x27;total_gb&#x27;</span>]&#125;</span> GB (<span class="subst">&#123;disk[<span class="string">&#x27;percent&#x27;</span>]&#125;</span>%)&quot;</span>)</span><br><span class="line"></span><br><span class="line">            <span class="comment"># 占用最高的进程</span></span><br><span class="line">            <span class="built_in">print</span>(<span class="string">f&quot;\n🔝 内存占用 TOP 5:&quot;</span>)</span><br><span class="line">            <span class="keyword">for</span> proc <span class="keyword">in</span> <span class="variable language_">self</span>.get_top_processes(<span class="number">5</span>):</span><br><span class="line">                <span class="built_in">print</span>(<span class="string">f&quot;  PID <span class="subst">&#123;proc[<span class="string">&#x27;pid&#x27;</span>]:&gt;<span class="number">6</span>&#125;</span>  &quot;</span></span><br><span class="line">                      <span class="string">f&quot;<span class="subst">&#123;proc[<span class="string">&#x27;name&#x27;</span>]:&lt;<span class="number">20</span>&#125;</span>  &quot;</span></span><br><span class="line">                      <span class="string">f&quot;内存 <span class="subst">&#123;proc.get(<span class="string">&#x27;memory_percent&#x27;</span>, <span class="number">0</span>):<span class="number">.1</span>f&#125;</span>%&quot;</span>)</span><br><span class="line"></span><br><span class="line">            <span class="comment"># 告警检查</span></span><br><span class="line">            alerts = <span class="variable language_">self</span>.check_alerts()</span><br><span class="line">            <span class="keyword">if</span> alerts:</span><br><span class="line">                <span class="built_in">print</span>(<span class="string">f&quot;\n🚨 告警:&quot;</span>)</span><br><span class="line">                <span class="keyword">for</span> alert <span class="keyword">in</span> alerts:</span><br><span class="line">                    <span class="built_in">print</span>(<span class="string">f&quot;  <span class="subst">&#123;alert&#125;</span>&quot;</span>)</span><br><span class="line">            <span class="keyword">else</span>:</span><br><span class="line">                <span class="built_in">print</span>(<span class="string">f&quot;\n✅ 所有指标正常&quot;</span>)</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> times == <span class="number">0</span> <span class="keyword">or</span> count &lt; times:</span><br><span class="line">                time.sleep(interval)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># ===== 使用示例 =====</span></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    monitor = SystemMonitor(</span><br><span class="line">        cpu_threshold=<span class="number">80.0</span>,      <span class="comment"># CPU 超过 80% 告警</span></span><br><span class="line">        memory_threshold=<span class="number">85.0</span>,   <span class="comment"># 内存超过 85% 告警</span></span><br><span class="line">        disk_threshold=<span class="number">90.0</span>,     <span class="comment"># 磁盘超过 90% 告警</span></span><br><span class="line">    )</span><br><span class="line">    <span class="comment"># 检查 3 次，每次间隔 10 秒（演示用，实际建议 60 秒）</span></span><br><span class="line">    monitor.run(interval=<span class="number">10</span>, times=<span class="number">3</span>)</span><br></pre></td></tr></table></figure><h4 id="日志分析脚本（统计错误、提取关键信息）"><a href="#日志分析脚本（统计错误、提取关键信息）" class="headerlink" title="日志分析脚本（统计错误、提取关键信息）"></a>日志分析脚本（统计错误、提取关键信息）</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><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><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">日志分析脚本</span></span><br><span class="line"><span class="string">功能：统计错误数量、按级别分类、提取关键信息、生成报告</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="keyword">import</span> re</span><br><span class="line"><span class="keyword">from</span> collections <span class="keyword">import</span> Counter, defaultdict</span><br><span class="line"><span class="keyword">from</span> pathlib <span class="keyword">import</span> Path</span><br><span class="line"><span class="keyword">from</span> datetime <span class="keyword">import</span> datetime</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">LogAnalyzer</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;日志分析器。&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 匹配常见日志格式的正则表达式</span></span><br><span class="line">    <span class="comment"># 格式示例：2025-07-24 10:30:45 [ERROR] Database connection failed</span></span><br><span class="line">    LOG_PATTERN = re.<span class="built_in">compile</span>(</span><br><span class="line">        <span class="string">r&quot;(?P&lt;timestamp&gt;\d&#123;4&#125;-\d&#123;2&#125;-\d&#123;2&#125;\s+\d&#123;2&#125;:\d&#123;2&#125;:\d&#123;2&#125;)\s+&quot;</span></span><br><span class="line">        <span class="string">r&quot;\[(?P&lt;level&gt;DEBUG|INFO|WARNING|ERROR|CRITICAL)\]\s+&quot;</span></span><br><span class="line">        <span class="string">r&quot;(?P&lt;message&gt;.*)&quot;</span></span><br><span class="line">    )</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, log_path: <span class="built_in">str</span></span>):</span><br><span class="line">        <span class="variable language_">self</span>.log_path = Path(log_path)</span><br><span class="line">        <span class="variable language_">self</span>.entries = []       <span class="comment"># 所有解析后的日志条目</span></span><br><span class="line">        <span class="variable language_">self</span>.errors = []        <span class="comment"># 错误日志</span></span><br><span class="line">        <span class="variable language_">self</span>.level_counts = Counter()    <span class="comment"># 各级别计数</span></span><br><span class="line">        <span class="variable language_">self</span>.hourly_counts = defaultdict(<span class="built_in">int</span>)  <span class="comment"># 每小时日志计数</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">parse</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;解析日志文件。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">with</span> <span class="built_in">open</span>(<span class="variable language_">self</span>.log_path, <span class="string">&quot;r&quot;</span>, encoding=<span class="string">&quot;utf-8&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">            <span class="keyword">for</span> line_num, line <span class="keyword">in</span> <span class="built_in">enumerate</span>(f, <span class="number">1</span>):</span><br><span class="line">                line = line.strip()</span><br><span class="line">                <span class="keyword">if</span> <span class="keyword">not</span> line:</span><br><span class="line">                    <span class="keyword">continue</span></span><br><span class="line"></span><br><span class="line">                <span class="keyword">match</span> = <span class="variable language_">self</span>.LOG_PATTERN.<span class="keyword">match</span>(line)</span><br><span class="line">                <span class="keyword">if</span> <span class="keyword">match</span>:</span><br><span class="line">                    entry = &#123;</span><br><span class="line">                        <span class="string">&quot;line&quot;</span>: line_num,</span><br><span class="line">                        <span class="string">&quot;timestamp&quot;</span>: <span class="keyword">match</span>.group(<span class="string">&quot;timestamp&quot;</span>),</span><br><span class="line">                        <span class="string">&quot;level&quot;</span>: <span class="keyword">match</span>.group(<span class="string">&quot;level&quot;</span>),</span><br><span class="line">                        <span class="string">&quot;message&quot;</span>: <span class="keyword">match</span>.group(<span class="string">&quot;message&quot;</span>),</span><br><span class="line">                    &#125;</span><br><span class="line">                    <span class="variable language_">self</span>.entries.append(entry)</span><br><span class="line">                    <span class="variable language_">self</span>.level_counts[entry[<span class="string">&quot;level&quot;</span>]] += <span class="number">1</span></span><br><span class="line"></span><br><span class="line">                    <span class="comment"># 提取小时用于统计</span></span><br><span class="line">                    <span class="keyword">try</span>:</span><br><span class="line">                        hour = entry[<span class="string">&quot;timestamp&quot;</span>].split(<span class="string">&quot; &quot;</span>)[<span class="number">1</span>][:<span class="number">2</span>]</span><br><span class="line">                        <span class="variable language_">self</span>.hourly_counts[hour] += <span class="number">1</span></span><br><span class="line">                    <span class="keyword">except</span> (IndexError, ValueError):</span><br><span class="line">                        <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line">                    <span class="comment"># 收集错误日志</span></span><br><span class="line">                    <span class="keyword">if</span> entry[<span class="string">&quot;level&quot;</span>] <span class="keyword">in</span> (<span class="string">&quot;ERROR&quot;</span>, <span class="string">&quot;CRITICAL&quot;</span>):</span><br><span class="line">                        <span class="variable language_">self</span>.errors.append(entry)</span><br><span class="line">                <span class="keyword">else</span>:</span><br><span class="line">                    <span class="comment"># 无法匹配的行，归为 UNKNOWN 级别</span></span><br><span class="line">                    <span class="variable language_">self</span>.entries.append(&#123;</span><br><span class="line">                        <span class="string">&quot;line&quot;</span>: line_num,</span><br><span class="line">                        <span class="string">&quot;timestamp&quot;</span>: <span class="string">&quot;&quot;</span>,</span><br><span class="line">                        <span class="string">&quot;level&quot;</span>: <span class="string">&quot;UNKNOWN&quot;</span>,</span><br><span class="line">                        <span class="string">&quot;message&quot;</span>: line,</span><br><span class="line">                    &#125;)</span><br><span class="line">                    <span class="variable language_">self</span>.level_counts[<span class="string">&quot;UNKNOWN&quot;</span>] += <span class="number">1</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">find_error_patterns</span>(<span class="params">self</span>) -&gt; <span class="built_in">list</span>[<span class="built_in">dict</span>]:</span><br><span class="line">        <span class="string">&quot;&quot;&quot;找出重复出现的错误模式（按错误消息分类）。&quot;&quot;&quot;</span></span><br><span class="line">        error_messages = Counter()</span><br><span class="line">        <span class="keyword">for</span> error <span class="keyword">in</span> <span class="variable language_">self</span>.errors:</span><br><span class="line">            <span class="comment"># 去掉动态内容（如数字、ID），只保留模式</span></span><br><span class="line">            pattern = re.sub(<span class="string">r&quot;\d+&quot;</span>, <span class="string">&quot;N&quot;</span>, error[<span class="string">&quot;message&quot;</span>])</span><br><span class="line">            pattern = re.sub(<span class="string">r&quot;0x[0-9a-fA-F]+&quot;</span>, <span class="string">&quot;0xHEX&quot;</span>, pattern)</span><br><span class="line">            error_messages[pattern] += <span class="number">1</span></span><br><span class="line"></span><br><span class="line">        <span class="comment"># 返回出现次数最多的错误模式</span></span><br><span class="line">        results = []</span><br><span class="line">        <span class="keyword">for</span> message, count <span class="keyword">in</span> error_messages.most_common(<span class="number">10</span>):</span><br><span class="line">            <span class="comment"># 找到该模式的一条完整日志作为示例</span></span><br><span class="line">            example = <span class="built_in">next</span>(</span><br><span class="line">                e <span class="keyword">for</span> e <span class="keyword">in</span> <span class="variable language_">self</span>.errors</span><br><span class="line">                <span class="keyword">if</span> re.sub(<span class="string">r&quot;\d+&quot;</span>, <span class="string">&quot;N&quot;</span>, e[<span class="string">&quot;message&quot;</span>]) == message</span><br><span class="line">            )</span><br><span class="line">            results.append(&#123;</span><br><span class="line">                <span class="string">&quot;pattern&quot;</span>: message,</span><br><span class="line">                <span class="string">&quot;count&quot;</span>: count,</span><br><span class="line">                <span class="string">&quot;example&quot;</span>: example,</span><br><span class="line">            &#125;)</span><br><span class="line">        <span class="keyword">return</span> results</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_hourly_summary</span>(<span class="params">self</span>) -&gt; <span class="built_in">dict</span>[<span class="built_in">str</span>, <span class="built_in">int</span>]:</span><br><span class="line">        <span class="string">&quot;&quot;&quot;获取每小时的日志数量统计。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">dict</span>(<span class="built_in">sorted</span>(<span class="variable language_">self</span>.hourly_counts.items()))</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">generate_report</span>(<span class="params">self</span>) -&gt; <span class="built_in">str</span>:</span><br><span class="line">        <span class="string">&quot;&quot;&quot;生成文本格式的分析报告。&quot;&quot;&quot;</span></span><br><span class="line">        lines = []</span><br><span class="line">        lines.append(<span class="string">&quot;=&quot;</span> * <span class="number">60</span>)</span><br><span class="line">        lines.append(<span class="string">f&quot;  日志分析报告: <span class="subst">&#123;self.log_path.name&#125;</span>&quot;</span>)</span><br><span class="line">        lines.append(<span class="string">f&quot;  生成时间: <span class="subst">&#123;datetime.now().strftime(<span class="string">&#x27;%Y-%m-%d %H:%M:%S&#x27;</span>)&#125;</span>&quot;</span>)</span><br><span class="line">        lines.append(<span class="string">&quot;=&quot;</span> * <span class="number">60</span>)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 总体统计</span></span><br><span class="line">        total = <span class="built_in">len</span>(<span class="variable language_">self</span>.entries)</span><br><span class="line">        lines.append(<span class="string">f&quot;\n📊 总体统计:&quot;</span>)</span><br><span class="line">        lines.append(<span class="string">f&quot;  总日志条数: <span class="subst">&#123;total&#125;</span>&quot;</span>)</span><br><span class="line">        <span class="keyword">for</span> level, count <span class="keyword">in</span> <span class="variable language_">self</span>.level_counts.most_common():</span><br><span class="line">            pct = count / total * <span class="number">100</span> <span class="keyword">if</span> total &gt; <span class="number">0</span> <span class="keyword">else</span> <span class="number">0</span></span><br><span class="line">            lines.append(<span class="string">f&quot;  <span class="subst">&#123;level:&lt;<span class="number">10</span>&#125;</span> <span class="subst">&#123;count:&gt;<span class="number">6</span>&#125;</span> 条  (<span class="subst">&#123;pct:<span class="number">.1</span>f&#125;</span>%)&quot;</span>)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 错误统计</span></span><br><span class="line">        lines.append(<span class="string">f&quot;\n🚨 错误详情 (<span class="subst">&#123;<span class="built_in">len</span>(self.errors)&#125;</span> 条):&quot;</span>)</span><br><span class="line">        <span class="keyword">for</span> pattern_info <span class="keyword">in</span> <span class="variable language_">self</span>.find_error_patterns():</span><br><span class="line">            lines.append(</span><br><span class="line">                <span class="string">f&quot;  [<span class="subst">&#123;pattern_info[<span class="string">&#x27;count&#x27;</span>]&#125;</span>次] <span class="subst">&#123;pattern_info[<span class="string">&#x27;pattern&#x27;</span>]&#125;</span>&quot;</span></span><br><span class="line">            )</span><br><span class="line">            lines.append(</span><br><span class="line">                <span class="string">f&quot;    示例: 行 <span class="subst">&#123;pattern_info[<span class="string">&#x27;example&#x27;</span>][<span class="string">&#x27;line&#x27;</span>]&#125;</span> - &quot;</span></span><br><span class="line">                <span class="string">f&quot;<span class="subst">&#123;pattern_info[<span class="string">&#x27;example&#x27;</span>][<span class="string">&#x27;message&#x27;</span>]&#125;</span>&quot;</span></span><br><span class="line">            )</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 每小时分布</span></span><br><span class="line">        lines.append(<span class="string">f&quot;\n📈 每小时日志分布:&quot;</span>)</span><br><span class="line">        hourly = <span class="variable language_">self</span>.get_hourly_summary()</span><br><span class="line">        <span class="keyword">if</span> hourly:</span><br><span class="line">            max_count = <span class="built_in">max</span>(hourly.values())</span><br><span class="line">            <span class="keyword">for</span> hour, count <span class="keyword">in</span> hourly.items():</span><br><span class="line">                bar_len = <span class="built_in">int</span>(count / <span class="built_in">max</span>(max_count, <span class="number">1</span>) * <span class="number">30</span>)</span><br><span class="line">                bar = <span class="string">&quot;█&quot;</span> * bar_len</span><br><span class="line">                lines.append(<span class="string">f&quot;  <span class="subst">&#123;hour&#125;</span>:00 [<span class="subst">&#123;bar&#125;</span>] <span class="subst">&#123;count&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;\n&quot;</span>.join(lines)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># ===== 使用示例 =====</span></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    <span class="comment"># 先创建一个示例日志文件用于测试</span></span><br><span class="line">    sample_log = <span class="string">&quot;&quot;&quot;2025-07-24 08:01:23 [INFO] Application started</span></span><br><span class="line"><span class="string">2025-07-24 08:05:45 [INFO] User login: user_id=123</span></span><br><span class="line"><span class="string">2025-07-24 08:10:12 [WARNING] Cache miss for key=product_456</span></span><br><span class="line"><span class="string">2025-07-24 08:15:30 [ERROR] Database connection failed: timeout after 30s</span></span><br><span class="line"><span class="string">2025-07-24 08:20:01 [INFO] Request processed: /api/users</span></span><br><span class="line"><span class="string">2025-07-24 08:25:44 [ERROR] Database connection failed: timeout after 30s</span></span><br><span class="line"><span class="string">2025-07-24 09:01:00 [INFO] Scheduled task started</span></span><br><span class="line"><span class="string">2025-07-24 09:05:22 [CRITICAL] Out of memory: allocated 8192MB</span></span><br><span class="line"><span class="string">2025-07-24 09:10:15 [ERROR] File not found: /data/report_789.csv</span></span><br><span class="line"><span class="string">2025-07-24 09:30:00 [INFO] Backup completed successfully</span></span><br><span class="line"><span class="string">2025-07-24 10:00:00 [INFO] Health check passed</span></span><br><span class="line"><span class="string">2025-07-24 10:15:33 [ERROR] Database connection failed: timeout after 30s</span></span><br><span class="line"><span class="string">2025-07-24 10:20:11 [WARNING] Disk usage at 85%</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line">    test_log_path = <span class="string">&quot;test_app.log&quot;</span></span><br><span class="line">    <span class="keyword">with</span> <span class="built_in">open</span>(test_log_path, <span class="string">&quot;w&quot;</span>, encoding=<span class="string">&quot;utf-8&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">        f.write(sample_log)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 分析日志</span></span><br><span class="line">    analyzer = LogAnalyzer(test_log_path)</span><br><span class="line">    analyzer.parse()</span><br><span class="line">    report = analyzer.generate_report()</span><br><span class="line">    <span class="built_in">print</span>(report)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 清理测试文件</span></span><br><span class="line">    Path(test_log_path).unlink()</span><br></pre></td></tr></table></figure><h3 id="4-3-Docker-自动化"><a href="#4-3-Docker-自动化" class="headerlink" title="4.3 Docker 自动化"></a>4.3 Docker 自动化</h3><p>用 Python 操作 Docker，实现镜像构建、容器管理的自动化。</p><h4 id="安装-Docker-SDK"><a href="#安装-Docker-SDK" class="headerlink" title="安装 Docker SDK"></a>安装 Docker SDK</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pip install docker</span><br></pre></td></tr></table></figure><h4 id="自动构建和部署"><a href="#自动构建和部署" class="headerlink" title="自动构建和部署"></a>自动构建和部署</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><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><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">Docker 自动化脚本</span></span><br><span class="line"><span class="string">功能：用 Python 操作 Docker，自动构建镜像、管理容器</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="keyword">import</span> docker</span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"><span class="keyword">from</span> datetime <span class="keyword">import</span> datetime</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">DockerManager</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;Docker 管理器。&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="comment"># 连接本地 Docker 守护进程</span></span><br><span class="line">        <span class="comment"># 需要先安装 Docker 并启动 Docker 服务</span></span><br><span class="line">        <span class="variable language_">self</span>.client = docker.from_env()</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">list_containers</span>(<span class="params">self, <span class="built_in">all</span>: <span class="built_in">bool</span> = <span class="literal">False</span></span>) -&gt; <span class="built_in">list</span>[<span class="built_in">dict</span>]:</span><br><span class="line">        <span class="string">&quot;&quot;&quot;列出容器。all=True 包含已停止的容器。&quot;&quot;&quot;</span></span><br><span class="line">        containers = <span class="variable language_">self</span>.client.containers.<span class="built_in">list</span>(<span class="built_in">all</span>=<span class="built_in">all</span>)</span><br><span class="line">        result = []</span><br><span class="line">        <span class="keyword">for</span> c <span class="keyword">in</span> containers:</span><br><span class="line">            result.append(&#123;</span><br><span class="line">                <span class="string">&quot;id&quot;</span>: c.short_id,</span><br><span class="line">                <span class="string">&quot;name&quot;</span>: c.name,</span><br><span class="line">                <span class="string">&quot;image&quot;</span>: <span class="built_in">str</span>(c.image.tags),</span><br><span class="line">                <span class="string">&quot;status&quot;</span>: c.status,</span><br><span class="line">            &#125;)</span><br><span class="line">        <span class="keyword">return</span> result</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">build_image</span>(<span class="params">self, path: <span class="built_in">str</span>, tag: <span class="built_in">str</span></span>) -&gt; <span class="built_in">dict</span>:</span><br><span class="line">        <span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">        根据 Dockerfile 构建镜像。</span></span><br><span class="line"><span class="string">        path: Dockerfile 所在目录</span></span><br><span class="line"><span class="string">        tag: 镜像标签，如 &quot;myapp:1.0&quot;</span></span><br><span class="line"><span class="string">        &quot;&quot;&quot;</span></span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;正在构建镜像 <span class="subst">&#123;tag&#125;</span> ...&quot;</span>)</span><br><span class="line">        image, build_logs = <span class="variable language_">self</span>.client.images.build(</span><br><span class="line">            path=path,</span><br><span class="line">            tag=tag,</span><br><span class="line">            rm=<span class="literal">True</span>,  <span class="comment"># 构建完成后删除中间容器</span></span><br><span class="line">        )</span><br><span class="line">        <span class="comment"># 打印构建日志</span></span><br><span class="line">        <span class="keyword">for</span> log <span class="keyword">in</span> build_logs:</span><br><span class="line">            <span class="keyword">if</span> <span class="string">&quot;stream&quot;</span> <span class="keyword">in</span> log:</span><br><span class="line">                <span class="built_in">print</span>(<span class="string">f&quot;  <span class="subst">&#123;log[<span class="string">&#x27;stream&#x27;</span>]&#125;</span>&quot;</span>, end=<span class="string">&quot;&quot;</span>)</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> &#123;</span><br><span class="line">            <span class="string">&quot;id&quot;</span>: image.short_id,</span><br><span class="line">            <span class="string">&quot;tags&quot;</span>: image.tags,</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">run_container</span>(<span class="params"></span></span><br><span class="line"><span class="params">        self,</span></span><br><span class="line"><span class="params">        image: <span class="built_in">str</span>,</span></span><br><span class="line"><span class="params">        name: <span class="built_in">str</span>,</span></span><br><span class="line"><span class="params">        ports: <span class="built_in">dict</span> | <span class="literal">None</span> = <span class="literal">None</span>,</span></span><br><span class="line"><span class="params">        env: <span class="built_in">dict</span> | <span class="literal">None</span> = <span class="literal">None</span>,</span></span><br><span class="line"><span class="params">        volumes: <span class="built_in">dict</span> | <span class="literal">None</span> = <span class="literal">None</span>,</span></span><br><span class="line"><span class="params">        detach: <span class="built_in">bool</span> = <span class="literal">True</span>,</span></span><br><span class="line"><span class="params">    </span>) -&gt; <span class="built_in">dict</span>:</span><br><span class="line">        <span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">        运行容器。</span></span><br><span class="line"><span class="string">        ports: 端口映射，如 &#123;&quot;8000/tcp&quot;: 8000&#125;</span></span><br><span class="line"><span class="string">        env: 环境变量，如 &#123;&quot;DEBUG&quot;: &quot;true&quot;&#125;</span></span><br><span class="line"><span class="string">        volumes: 挂载卷，如 &#123;&quot;./data&quot;: &#123;&quot;bind&quot;: &quot;/app/data&quot;, &quot;mode&quot;: &quot;rw&quot;&#125;&#125;</span></span><br><span class="line"><span class="string">        &quot;&quot;&quot;</span></span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;正在启动容器 <span class="subst">&#123;name&#125;</span> ...&quot;</span>)</span><br><span class="line">        container = <span class="variable language_">self</span>.client.containers.run(</span><br><span class="line">            image=image,</span><br><span class="line">            name=name,</span><br><span class="line">            ports=ports,</span><br><span class="line">            environment=env,</span><br><span class="line">            volumes=volumes,</span><br><span class="line">            detach=detach,  <span class="comment"># 后台运行</span></span><br><span class="line">        )</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> detach:</span><br><span class="line">            <span class="comment"># 等待容器启动</span></span><br><span class="line">            time.sleep(<span class="number">2</span>)</span><br><span class="line">            container.reload()  <span class="comment"># 刷新容器状态</span></span><br><span class="line">            <span class="keyword">return</span> &#123;</span><br><span class="line">                <span class="string">&quot;id&quot;</span>: container.short_id,</span><br><span class="line">                <span class="string">&quot;name&quot;</span>: container.name,</span><br><span class="line">                <span class="string">&quot;status&quot;</span>: container.status,</span><br><span class="line">            &#125;</span><br><span class="line">        <span class="keyword">return</span> &#123;<span class="string">&quot;output&quot;</span>: container.decode(<span class="string">&quot;utf-8&quot;</span>)&#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_container_logs</span>(<span class="params">self, name: <span class="built_in">str</span>, tail: <span class="built_in">int</span> = <span class="number">50</span></span>) -&gt; <span class="built_in">str</span>:</span><br><span class="line">        <span class="string">&quot;&quot;&quot;获取容器日志。&quot;&quot;&quot;</span></span><br><span class="line">        container = <span class="variable language_">self</span>.client.containers.get(name)</span><br><span class="line">        <span class="keyword">return</span> container.logs(tail=tail).decode(<span class="string">&quot;utf-8&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">stop_container</span>(<span class="params">self, name: <span class="built_in">str</span></span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;停止容器。&quot;&quot;&quot;</span></span><br><span class="line">        container = <span class="variable language_">self</span>.client.containers.get(name)</span><br><span class="line">        container.stop()</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;容器 <span class="subst">&#123;name&#125;</span> 已停止&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">remove_container</span>(<span class="params">self, name: <span class="built_in">str</span>, force: <span class="built_in">bool</span> = <span class="literal">False</span></span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;删除容器。&quot;&quot;&quot;</span></span><br><span class="line">        container = <span class="variable language_">self</span>.client.containers.get(name)</span><br><span class="line">        container.remove(force=force)</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;容器 <span class="subst">&#123;name&#125;</span> 已删除&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">cleanup_unused</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;清理未使用的镜像、容器、网络。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">try</span>:</span><br><span class="line">            <span class="comment"># 删除所有已停止的容器</span></span><br><span class="line">            <span class="variable language_">self</span>.client.containers.prune()</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">&quot;已清理停止的容器&quot;</span>)</span><br><span class="line">            <span class="comment"># 删除未使用的镜像</span></span><br><span class="line">            <span class="variable language_">self</span>.client.images.prune()</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">&quot;已清理未使用的镜像&quot;</span>)</span><br><span class="line">        <span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">f&quot;清理失败: <span class="subst">&#123;e&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># ===== 使用示例 =====</span></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    dm = DockerManager()</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 1. 列出当前运行的容器</span></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;=== 当前容器 ===&quot;</span>)</span><br><span class="line">    <span class="keyword">for</span> c <span class="keyword">in</span> dm.list_containers(<span class="built_in">all</span>=<span class="literal">True</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;  <span class="subst">&#123;c[<span class="string">&#x27;name&#x27;</span>]:&lt;<span class="number">20</span>&#125;</span> <span class="subst">&#123;c[<span class="string">&#x27;status&#x27;</span>]:&lt;<span class="number">15</span>&#125;</span> <span class="subst">&#123;c[<span class="string">&#x27;image&#x27;</span>]&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 2. 构建镜像（需要有 Dockerfile）</span></span><br><span class="line">    <span class="comment"># image_info = dm.build_image(path=&quot;./myproject&quot;, tag=&quot;myapp:1.0&quot;)</span></span><br><span class="line">    <span class="comment"># print(f&quot;构建完成: &#123;image_info&#125;&quot;)</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 3. 运行容器</span></span><br><span class="line">    <span class="comment"># container_info = dm.run_container(</span></span><br><span class="line">    <span class="comment">#     image=&quot;myapp:1.0&quot;,</span></span><br><span class="line">    <span class="comment">#     name=&quot;myapp-server&quot;,</span></span><br><span class="line">    <span class="comment">#     ports=&#123;&quot;8000/tcp&quot;: 8000&#125;,</span></span><br><span class="line">    <span class="comment">#     env=&#123;&quot;DEBUG&quot;: &quot;true&quot;&#125;,</span></span><br><span class="line">    <span class="comment"># )</span></span><br><span class="line">    <span class="comment"># print(f&quot;容器已启动: &#123;container_info&#125;&quot;)</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 4. 查看日志</span></span><br><span class="line">    <span class="comment"># logs = dm.get_container_logs(&quot;myapp-server&quot;, tail=20)</span></span><br><span class="line">    <span class="comment"># print(logs)</span></span><br></pre></td></tr></table></figure><h3 id="4-4-CI-CD-自动化"><a href="#4-4-CI-CD-自动化" class="headerlink" title="4.4 CI&#x2F;CD 自动化"></a>4.4 CI&#x2F;CD 自动化</h3><p><strong>GitHub Actions + Python 示例</strong>：在代码推送后自动运行测试和部署。</p><p>在项目根目录创建 <code>.github/workflows/ci.yml</code>：</p><figure class="highlight yaml"><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># .github/workflows/ci.yml</span></span><br><span class="line"><span class="comment"># GitHub Actions CI/CD 配置文件</span></span><br><span class="line"></span><br><span class="line"><span class="attr">name:</span> <span class="string">Python</span> <span class="string">CI/CD</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 触发条件：push 到 main 分支 或 提交 Pull Request</span></span><br><span class="line"><span class="attr">on:</span></span><br><span class="line">  <span class="attr">push:</span></span><br><span class="line">    <span class="attr">branches:</span> [<span class="string">main</span>]</span><br><span class="line">  <span class="attr">pull_request:</span></span><br><span class="line">    <span class="attr">branches:</span> [<span class="string">main</span>]</span><br><span class="line"></span><br><span class="line"><span class="attr">jobs:</span></span><br><span class="line">  <span class="attr">test:</span></span><br><span class="line">    <span class="attr">name:</span> <span class="string">测试</span></span><br><span class="line">    <span class="attr">runs-on:</span> <span class="string">ubuntu-latest</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 使用矩阵测试多个 Python 版本</span></span><br><span class="line">    <span class="attr">strategy:</span></span><br><span class="line">      <span class="attr">matrix:</span></span><br><span class="line">        <span class="attr">python-version:</span> [<span class="string">&quot;3.10&quot;</span>, <span class="string">&quot;3.11&quot;</span>, <span class="string">&quot;3.12&quot;</span>]</span><br><span class="line"></span><br><span class="line">    <span class="attr">steps:</span></span><br><span class="line">      <span class="comment"># 第1步：拉取代码</span></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">检出代码</span></span><br><span class="line">        <span class="attr">uses:</span> <span class="string">actions/checkout@v4</span></span><br><span class="line"></span><br><span class="line">      <span class="comment"># 第2步：安装 Python</span></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">设置</span> <span class="string">Python</span> <span class="string">$&#123;&#123;</span> <span class="string">matrix.python-version</span> <span class="string">&#125;&#125;</span></span><br><span class="line">        <span class="attr">uses:</span> <span class="string">actions/setup-python@v5</span></span><br><span class="line">        <span class="attr">with:</span></span><br><span class="line">          <span class="attr">python-version:</span> <span class="string">$&#123;&#123;</span> <span class="string">matrix.python-version</span> <span class="string">&#125;&#125;</span></span><br><span class="line"></span><br><span class="line">      <span class="comment"># 第3步：安装依赖</span></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">安装依赖</span></span><br><span class="line">        <span class="attr">run:</span> <span class="string">|</span></span><br><span class="line"><span class="string">          python -m pip install --upgrade pip</span></span><br><span class="line"><span class="string">          pip install -r requirements.txt</span></span><br><span class="line"><span class="string">          pip install pytest pytest-cov flake8</span></span><br><span class="line"><span class="string"></span></span><br><span class="line">      <span class="comment"># 第4步：代码风格检查</span></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">代码风格检查</span></span><br><span class="line">        <span class="attr">run:</span> <span class="string">flake8</span> <span class="string">src/</span> <span class="string">--max-line-length=88</span> <span class="string">--exclude=__pycache__</span></span><br><span class="line"></span><br><span class="line">      <span class="comment"># 第5步：运行测试</span></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">运行测试</span></span><br><span class="line">        <span class="attr">run:</span> <span class="string">|</span></span><br><span class="line"><span class="string">          pytest tests/ -v --cov=src --cov-report=xml</span></span><br><span class="line"><span class="string"></span></span><br><span class="line">      <span class="comment"># 第6步：上传覆盖率报告</span></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">上传覆盖率</span></span><br><span class="line">        <span class="attr">uses:</span> <span class="string">codecov/codecov-action@v4</span></span><br><span class="line"></span><br><span class="line">  <span class="attr">deploy:</span></span><br><span class="line">    <span class="attr">name:</span> <span class="string">部署</span></span><br><span class="line">    <span class="attr">needs:</span> <span class="string">test</span>  <span class="comment"># 测试通过后才执行部署</span></span><br><span class="line">    <span class="attr">runs-on:</span> <span class="string">ubuntu-latest</span></span><br><span class="line">    <span class="attr">if:</span> <span class="string">github.ref</span> <span class="string">==</span> <span class="string">&#x27;refs/heads/main&#x27;</span>  <span class="comment"># 只有 main 分支才部署</span></span><br><span class="line"></span><br><span class="line">    <span class="attr">steps:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">检出代码</span></span><br><span class="line">        <span class="attr">uses:</span> <span class="string">actions/checkout@v4</span></span><br><span class="line"></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">构建</span> <span class="string">Docker</span> <span class="string">镜像</span></span><br><span class="line">        <span class="attr">run:</span> <span class="string">docker</span> <span class="string">build</span> <span class="string">-t</span> <span class="string">myapp:latest</span> <span class="string">.</span></span><br><span class="line"></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">推送镜像到仓库</span></span><br><span class="line">        <span class="attr">run:</span> <span class="string">|</span></span><br><span class="line"><span class="string">          docker login -u $&#123;&#123; secrets.DOCKER_USER &#125;&#125; -p $&#123;&#123; secrets.DOCKER_PASS &#125;&#125;</span></span><br><span class="line"><span class="string">          docker tag myapp:latest $&#123;&#123; secrets.DOCKER_USER &#125;&#125;/myapp:latest</span></span><br><span class="line"><span class="string">          docker push $&#123;&#123; secrets.DOCKER_USER &#125;&#125;/myapp:latest</span></span><br></pre></td></tr></table></figure><p><strong>配套的 Dockerfile</strong>（放在项目根目录）：</p><figure class="highlight dockerfile"><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><span class="line"><span class="keyword">FROM</span> python:<span class="number">3.11</span>-slim</span><br><span class="line"></span><br><span class="line"><span class="keyword">WORKDIR</span><span class="language-bash"> /app</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 先复制依赖文件，利用 Docker 缓存层</span></span><br><span class="line"><span class="keyword">COPY</span><span class="language-bash"> requirements.txt .</span></span><br><span class="line"><span class="keyword">RUN</span><span class="language-bash"> pip install --no-cache-dir -r requirements.txt</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 再复制源代码</span></span><br><span class="line"><span class="keyword">COPY</span><span class="language-bash"> . .</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 健康检查</span></span><br><span class="line"><span class="keyword">HEALTHCHECK</span><span class="language-bash"> --interval=30s --<span class="built_in">timeout</span>=10s --retries=3 \</span></span><br><span class="line"><span class="language-bash">    CMD python -c <span class="string">&quot;import urllib.request; urllib.request.urlopen(&#x27;http://localhost:8000/health&#x27;)&quot;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">CMD</span><span class="language-bash"> [<span class="string">&quot;uvicorn&quot;</span>, <span class="string">&quot;src.main:app&quot;</span>, <span class="string">&quot;--host&quot;</span>, <span class="string">&quot;0.0.0.0&quot;</span>, <span class="string">&quot;--port&quot;</span>, <span class="string">&quot;8000&quot;</span>]</span></span><br></pre></td></tr></table></figure><blockquote><p><strong>上手步骤</strong>：</p><ol><li>在 GitHub 创建仓库，把代码推上去</li><li>在仓库 Settings → Secrets 中添加 <code>DOCKER_USER</code> 和 <code>DOCKER_PASS</code></li><li>推送代码到 main 分支，GitHub Actions 会自动运行</li><li>在仓库的 “Actions” 标签页查看运行结果</li></ol></blockquote><h3 id="4-5-常见新手坑"><a href="#4-5-常见新手坑" class="headerlink" title="4.5 常见新手坑"></a>4.5 常见新手坑</h3><table><thead><tr><th>坑</th><th>说明</th><th>解决方案</th></tr></thead><tbody><tr><td><strong>subprocess 乱码</strong></td><td><code>subprocess.run</code> 返回中文乱码</td><td>加 <code>encoding=&quot;utf-8&quot;</code> 参数</td></tr><tr><td><strong>硬编码路径</strong></td><td>Windows 和 Linux 路径格式不同</td><td>用 <code>pathlib.Path</code> 代替字符串拼接</td></tr><tr><td><strong>权限不足</strong></td><td>操作系统文件&#x2F;目录没有权限</td><td>用 <code>try-except PermissionError</code> 捕获</td></tr><tr><td><strong>忘记 Docker 启动</strong></td><td>代码连接 Docker 失败</td><td>先确认 <code>docker info</code> 能正常运行</td></tr><tr><td><strong>GitHub Actions Secret 泄露</strong></td><td>把密码写进 yml 明文</td><td>使用 <code>secrets.XXX</code> 引用，不要硬编码</td></tr><tr><td><strong>psutil 跨平台差异</strong></td><td>部分字段在 Windows 上返回 None</td><td>做好 <code>None</code> 检查，参考 psutil 官方文档</td></tr><tr><td><strong>日志正则不匹配</strong></td><td>不同应用的日志格式不一样</td><td>先用 <code>head</code> 查看几行日志，再写正则</td></tr><tr><td><strong>Docker 镜像太大</strong></td><td>基础镜像用了完整版 Python</td><td>使用 <code>python:3.11-slim</code> 或 Alpine 版</td></tr></tbody></table><hr><h2 id="5-人工智能与机器学习"><a href="#5-人工智能与机器学习" class="headerlink" title="5. 人工智能与机器学习"></a>5. 人工智能与机器学习</h2><h3 id="5-1-AI-ML-是什么？岗位方向"><a href="#5-1-AI-ML-是什么？岗位方向" class="headerlink" title="5.1 AI&#x2F;ML 是什么？岗位方向"></a>5.1 AI&#x2F;ML 是什么？岗位方向</h3><p><strong>AI &#x2F; ML &#x2F; DL 的关系</strong>：</p><figure class="highlight plaintext"><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><span class="line">人工智能 (AI)</span><br><span class="line">└── 机器学习 (ML)         ← 用数据训练模型，让机器&quot;学会&quot;规则</span><br><span class="line">    └── 深度学习 (DL)     ← 用神经网络自动提取特征</span><br><span class="line">        ├── CNN（图像）</span><br><span class="line">        ├── RNN/LSTM（序列）</span><br><span class="line">        └── Transformer（NLP / LLM）</span><br></pre></td></tr></table></figure><p><strong>不同岗位的工作内容</strong>：</p><table><thead><tr><th>岗位</th><th>日常工作</th><th>核心技能</th><th>典型产出</th></tr></thead><tbody><tr><td><strong>算法工程师</strong></td><td>研究和改进算法，提升模型指标</td><td>数学推导、PyTorch&#x2F;TensorFlow、论文阅读</td><td>算法专利、顶会论文、SOTA 模型</td></tr><tr><td><strong>ML 工程师</strong></td><td>将模型从实验推向生产环境</td><td>Scikit-learn、MLOps、模型部署、数据管道</td><td>可用的 ML 服务、自动化流水线</td></tr><tr><td><strong>数据科学家</strong></td><td>从数据中提取洞察，辅助业务决策</td><td>SQL、Pandas、统计分析、可视化、A&#x2F;B 测试</td><td>分析报告、业务建议、预测模型</td></tr><tr><td><strong>AI 应用开发</strong></td><td>基于 LLM 等模型构建应用产品</td><td>LangChain、Prompt Engineering、API 调用、后端开发</td><td>RAG 应用、智能助手、AI 工具</td></tr></tbody></table><blockquote><p>💡 <strong>新手建议</strong>：从”ML 工程师”或”数据科学家”切入最实际——数学门槛比算法工程师低，就业面广。有一定基础后再考虑算法方向。</p></blockquote><h3 id="5-2-从零上手：Scikit-learn-完整流程"><a href="#5-2-从零上手：Scikit-learn-完整流程" class="headerlink" title="5.2 从零上手：Scikit-learn 完整流程"></a>5.2 从零上手：Scikit-learn 完整流程</h3><p><strong>安装和导入</strong>：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pip install scikit-learn pandas numpy matplotlib</span><br></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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"><span class="keyword">import</span> pandas <span class="keyword">as</span> pd</span><br><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line"><span class="keyword">from</span> sklearn.datasets <span class="keyword">import</span> load_iris</span><br><span class="line"><span class="keyword">from</span> sklearn.model_selection <span class="keyword">import</span> train_test_split</span><br><span class="line"><span class="keyword">from</span> sklearn.preprocessing <span class="keyword">import</span> StandardScaler</span><br><span class="line"><span class="keyword">from</span> sklearn.linear_model <span class="keyword">import</span> LogisticRegression</span><br><span class="line"><span class="keyword">from</span> sklearn.ensemble <span class="keyword">import</span> RandomForestClassifier</span><br><span class="line"><span class="keyword">from</span> sklearn.svm <span class="keyword">import</span> SVC</span><br><span class="line"><span class="keyword">from</span> sklearn.neighbors <span class="keyword">import</span> KNeighborsClassifier</span><br><span class="line"><span class="keyword">from</span> sklearn.metrics <span class="keyword">import</span> (</span><br><span class="line">    accuracy_score,</span><br><span class="line">    confusion_matrix,</span><br><span class="line">    classification_report,</span><br><span class="line">)</span><br><span class="line"><span class="keyword">import</span> joblib</span><br></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><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 加载鸢尾花数据集（经典入门数据集）</span></span><br><span class="line">iris = load_iris()</span><br><span class="line">X = iris.data          <span class="comment"># 特征：花萼长度、花萼宽度、花瓣长度、花瓣宽度</span></span><br><span class="line">y = iris.target        <span class="comment"># 标签：0=Setosa, 1=Versicolor, 2=Virginica</span></span><br><span class="line">feature_names = iris.feature_names</span><br><span class="line">target_names = iris.target_names</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;数据形状: <span class="subst">&#123;X.shape&#125;</span>&quot;</span>)          <span class="comment"># (150, 4) —— 150 条数据，4 个特征</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;特征名称: <span class="subst">&#123;feature_names&#125;</span>&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;类别名称: <span class="subst">&#123;target_names&#125;</span>&quot;</span>)      <span class="comment"># [&#x27;setosa&#x27; &#x27;versicolor&#x27; &#x27;virginica&#x27;]</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;类别分布: <span class="subst">&#123;np.bincount(y)&#125;</span>&quot;</span>)    <span class="comment"># [50, 50, 50] —— 每类 50 条，均衡数据集</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 转为 DataFrame 方便查看</span></span><br><span class="line">df = pd.DataFrame(X, columns=feature_names)</span><br><span class="line">df[<span class="string">&quot;target&quot;</span>] = y</span><br><span class="line">df[<span class="string">&quot;species&quot;</span>] = [target_names[i] <span class="keyword">for</span> i <span class="keyword">in</span> y]</span><br><span class="line"><span class="built_in">print</span>(df.head())</span><br><span class="line"><span class="comment">#    sepal length (cm)  sepal width (cm)  petal length (cm)  petal width (cm)  target   species</span></span><br><span class="line"><span class="comment"># 0                5.1               3.5                1.4               0.2        0    setosa</span></span><br><span class="line"><span class="comment"># 1                4.9               3.0                1.4               0.2        0    setosa</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 简单可视化</span></span><br><span class="line">plt.figure(figsize=(<span class="number">12</span>, <span class="number">5</span>))</span><br><span class="line"></span><br><span class="line">plt.subplot(<span class="number">1</span>, <span class="number">2</span>, <span class="number">1</span>)</span><br><span class="line"><span class="keyword">for</span> i, species <span class="keyword">in</span> <span class="built_in">enumerate</span>(target_names):</span><br><span class="line">    mask = y == i</span><br><span class="line">    plt.scatter(X[mask, <span class="number">0</span>], X[mask, <span class="number">1</span>], label=species)</span><br><span class="line">plt.xlabel(<span class="string">&quot;花萼长度 (cm)&quot;</span>)</span><br><span class="line">plt.ylabel(<span class="string">&quot;花萼宽度 (cm)&quot;</span>)</span><br><span class="line">plt.title(<span class="string">&quot;花萼特征散点图&quot;</span>)</span><br><span class="line">plt.legend()</span><br><span class="line"></span><br><span class="line">plt.subplot(<span class="number">1</span>, <span class="number">2</span>, <span class="number">2</span>)</span><br><span class="line"><span class="keyword">for</span> i, species <span class="keyword">in</span> <span class="built_in">enumerate</span>(target_names):</span><br><span class="line">    mask = y == i</span><br><span class="line">    plt.scatter(X[mask, <span class="number">2</span>], X[mask, <span class="number">3</span>], label=species)</span><br><span class="line">plt.xlabel(<span class="string">&quot;花瓣长度 (cm)&quot;</span>)</span><br><span class="line">plt.ylabel(<span class="string">&quot;花瓣宽度 (cm)&quot;</span>)</span><br><span class="line">plt.title(<span class="string">&quot;花瓣特征散点图&quot;</span>)</span><br><span class="line">plt.legend()</span><br><span class="line"></span><br><span class="line">plt.tight_layout()</span><br><span class="line">plt.savefig(<span class="string">&quot;iris_scatter.png&quot;</span>)</span><br><span class="line">plt.show()</span><br></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><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><span class="line"><span class="comment"># 拆分训练集和测试集（80% 训练，20% 测试）</span></span><br><span class="line">X_train, X_test, y_train, y_test = train_test_split(</span><br><span class="line">    X, y, test_size=<span class="number">0.2</span>, random_state=<span class="number">42</span>, stratify=y</span><br><span class="line">)</span><br><span class="line"><span class="comment"># stratify=y 保证训练集和测试集中各类别比例一致</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;训练集大小: <span class="subst">&#123;X_train.shape[<span class="number">0</span>]&#125;</span>&quot;</span>)  <span class="comment"># 120</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;测试集大小: <span class="subst">&#123;X_test.shape[<span class="number">0</span>]&#125;</span>&quot;</span>)   <span class="comment"># 30</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 标准化（让每个特征的均值为 0，标准差为 1）</span></span><br><span class="line"><span class="comment"># 注意：fit 只在训练集上做，然后 transform 训练集和测试集</span></span><br><span class="line">scaler = StandardScaler()</span><br><span class="line">X_train_scaled = scaler.fit_transform(X_train)   <span class="comment"># 在训练集上计算均值和标准差，并转换</span></span><br><span class="line">X_test_scaled = scaler.transform(X_test)          <span class="comment"># 用训练集的均值和标准差转换测试集</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;标准化前（训练集前 5 行）:&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(X_train[:<span class="number">5</span>])</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;标准化后（训练集前 5 行）:&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(X_train_scaled[:<span class="number">5</span>])</span><br></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><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><span class="line"><span class="comment"># 定义多个模型，方便对比</span></span><br><span class="line">models = &#123;</span><br><span class="line">    <span class="string">&quot;逻辑回归&quot;</span>: LogisticRegression(max_iter=<span class="number">200</span>, random_state=<span class="number">42</span>),</span><br><span class="line">    <span class="string">&quot;随机森林&quot;</span>: RandomForestClassifier(n_estimators=<span class="number">100</span>, random_state=<span class="number">42</span>),</span><br><span class="line">    <span class="string">&quot;SVM&quot;</span>: SVC(kernel=<span class="string">&quot;rbf&quot;</span>, random_state=<span class="number">42</span>),</span><br><span class="line">    <span class="string">&quot;KNN&quot;</span>: KNeighborsClassifier(n_neighbors=<span class="number">5</span>),</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 训练并评估每个模型</span></span><br><span class="line">results = &#123;&#125;</span><br><span class="line"><span class="keyword">for</span> name, model <span class="keyword">in</span> models.items():</span><br><span class="line">    <span class="comment"># 训练模型</span></span><br><span class="line">    model.fit(X_train_scaled, y_train)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 预测测试集</span></span><br><span class="line">    y_pred = model.predict(X_test_scaled)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 计算准确率</span></span><br><span class="line">    acc = accuracy_score(y_test, y_pred)</span><br><span class="line">    results[name] = &#123;<span class="string">&quot;model&quot;</span>: model, <span class="string">&quot;accuracy&quot;</span>: acc, <span class="string">&quot;predictions&quot;</span>: y_pred&#125;</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;<span class="subst">&#123;name&#125;</span> 准确率: <span class="subst">&#123;acc:<span class="number">.4</span>f&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 对比结果</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;\n===== 模型对比 =====&quot;</span>)</span><br><span class="line"><span class="keyword">for</span> name, result <span class="keyword">in</span> <span class="built_in">sorted</span>(results.items(), key=<span class="keyword">lambda</span> x: x[<span class="number">1</span>][<span class="string">&quot;accuracy&quot;</span>], reverse=<span class="literal">True</span>):</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;<span class="subst">&#123;name&#125;</span>: <span class="subst">&#123;result[<span class="string">&#x27;accuracy&#x27;</span>]:<span class="number">.4</span>f&#125;</span>&quot;</span>)</span><br></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><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><span class="line"><span class="comment"># 选择表现最好的模型进行详细评估</span></span><br><span class="line">best_name = <span class="built_in">max</span>(results, key=<span class="keyword">lambda</span> x: results[x][<span class="string">&quot;accuracy&quot;</span>])</span><br><span class="line">best_model = results[best_name][<span class="string">&quot;model&quot;</span>]</span><br><span class="line">y_pred = results[best_name][<span class="string">&quot;predictions&quot;</span>]</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;\n最佳模型: <span class="subst">&#123;best_name&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># ===== 1. 混淆矩阵 =====</span></span><br><span class="line">cm = confusion_matrix(y_test, y_pred)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;\n混淆矩阵:&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(cm)</span><br><span class="line"><span class="comment"># 每行代表真实类别，每列代表预测类别</span></span><br><span class="line"><span class="comment"># 对角线是正确预测的数量，非对角线是错误预测</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 可视化混淆矩阵</span></span><br><span class="line">plt.figure(figsize=(<span class="number">6</span>, <span class="number">5</span>))</span><br><span class="line">plt.imshow(cm, cmap=<span class="string">&quot;Blues&quot;</span>)</span><br><span class="line">plt.title(<span class="string">f&quot;混淆矩阵 - <span class="subst">&#123;best_name&#125;</span>&quot;</span>)</span><br><span class="line">plt.xlabel(<span class="string">&quot;预测类别&quot;</span>)</span><br><span class="line">plt.ylabel(<span class="string">&quot;真实类别&quot;</span>)</span><br><span class="line">plt.xticks(<span class="built_in">range</span>(<span class="number">3</span>), target_names)</span><br><span class="line">plt.yticks(<span class="built_in">range</span>(<span class="number">3</span>), target_names)</span><br><span class="line"><span class="comment"># 在每个格子中显示数字</span></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">3</span>):</span><br><span class="line">    <span class="keyword">for</span> j <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">3</span>):</span><br><span class="line">        plt.text(j, i, cm[i, j], ha=<span class="string">&quot;center&quot;</span>, va=<span class="string">&quot;center&quot;</span>, fontsize=<span class="number">14</span>)</span><br><span class="line">plt.colorbar()</span><br><span class="line">plt.tight_layout()</span><br><span class="line">plt.savefig(<span class="string">&quot;confusion_matrix.png&quot;</span>)</span><br><span class="line">plt.show()</span><br><span class="line"></span><br><span class="line"><span class="comment"># ===== 2. 分类报告 =====</span></span><br><span class="line">report = classification_report(y_test, y_pred, target_names=target_names)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;\n分类报告:&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(report)</span><br><span class="line"><span class="comment"># precision（精确率）: 预测为正的样本中，实际为正的比例</span></span><br><span class="line"><span class="comment"># recall（召回率）:    实际为正的样本中，被预测为正的比例</span></span><br><span class="line"><span class="comment"># f1-score:           精确率和召回率的调和平均</span></span><br><span class="line"><span class="comment"># support:            每个类别的样本数</span></span><br></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><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><span class="line"><span class="comment"># 保存模型和标准化器（部署时需要用相同的标准化参数）</span></span><br><span class="line">joblib.dump(best_model, <span class="string">&quot;iris_model.pkl&quot;</span>)</span><br><span class="line">joblib.dump(scaler, <span class="string">&quot;iris_scaler.pkl&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;模型已保存到 iris_model.pkl&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 加载模型并预测新数据</span></span><br><span class="line">loaded_model = joblib.load(<span class="string">&quot;iris_model.pkl&quot;</span>)</span><br><span class="line">loaded_scaler = joblib.load(<span class="string">&quot;iris_scaler.pkl&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 模拟新数据：花萼长 5.0、宽 3.6、花瓣长 1.4、宽 0.2</span></span><br><span class="line">new_sample = np.array([[<span class="number">5.0</span>, <span class="number">3.6</span>, <span class="number">1.4</span>, <span class="number">0.2</span>]])</span><br><span class="line">new_sample_scaled = loaded_scaler.transform(new_sample)  <span class="comment"># 必须用同样的标准化参数</span></span><br><span class="line">prediction = loaded_model.predict(new_sample_scaled)</span><br><span class="line">probability = loaded_model.predict_proba(new_sample_scaled)</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;预测类别: <span class="subst">&#123;target_names[prediction[<span class="number">0</span>]]&#125;</span>&quot;</span>)         <span class="comment"># setosa</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;各类别概率: <span class="subst">&#123;<span class="built_in">dict</span>(<span class="built_in">zip</span>(target_names, probability[<span class="number">0</span>]))&#125;</span>&quot;</span>)</span><br></pre></td></tr></table></figure><h3 id="5-3-深度学习入门：PyTorch-基础"><a href="#5-3-深度学习入门：PyTorch-基础" class="headerlink" title="5.3 深度学习入门：PyTorch 基础"></a>5.3 深度学习入门：PyTorch 基础</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pip install torch torchvision</span><br></pre></td></tr></table></figure><p><strong>Tensor 操作</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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> torch</span><br><span class="line"></span><br><span class="line"><span class="comment"># ===== 创建 Tensor =====</span></span><br><span class="line"><span class="comment"># 从列表创建</span></span><br><span class="line">a = torch.tensor([<span class="number">1.0</span>, <span class="number">2.0</span>, <span class="number">3.0</span>])</span><br><span class="line"><span class="built_in">print</span>(a)            <span class="comment"># tensor([1., 2., 3.])</span></span><br><span class="line"><span class="built_in">print</span>(a.dtype)      <span class="comment"># torch.float32</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建全零、全一、随机 Tensor</span></span><br><span class="line">zeros = torch.zeros(<span class="number">3</span>, <span class="number">4</span>)           <span class="comment"># 3x4 全零矩阵</span></span><br><span class="line">ones = torch.ones(<span class="number">2</span>, <span class="number">3</span>)             <span class="comment"># 2x3 全一矩阵</span></span><br><span class="line">random = torch.randn(<span class="number">2</span>, <span class="number">3</span>)          <span class="comment"># 2x3 标准正态分布随机数</span></span><br><span class="line">arange = torch.arange(<span class="number">0</span>, <span class="number">10</span>, <span class="number">2</span>)     <span class="comment"># [0, 2, 4, 6, 8]</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;zeros shape: <span class="subst">&#123;zeros.shape&#125;</span>&quot;</span>)   <span class="comment"># torch.Size([3, 4])</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ===== Tensor 运算 =====</span></span><br><span class="line">x = torch.tensor([<span class="number">1.0</span>, <span class="number">2.0</span>, <span class="number">3.0</span>])</span><br><span class="line">y = torch.tensor([<span class="number">4.0</span>, <span class="number">5.0</span>, <span class="number">6.0</span>])</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(x + y)         <span class="comment"># 加法: tensor([5., 7., 9.])</span></span><br><span class="line"><span class="built_in">print</span>(x * y)         <span class="comment"># 逐元素乘法: tensor([4., 10., 18.])</span></span><br><span class="line"><span class="built_in">print</span>(x.dot(y))      <span class="comment"># 点积: tensor(32.)</span></span><br><span class="line"><span class="built_in">print</span>(x.<span class="built_in">sum</span>())       <span class="comment"># 求和: tensor(6.)</span></span><br><span class="line"><span class="built_in">print</span>(x.mean())      <span class="comment"># 均值: tensor(2.)</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ===== 矩阵运算 =====</span></span><br><span class="line">A = torch.randn(<span class="number">3</span>, <span class="number">4</span>)</span><br><span class="line">B = torch.randn(<span class="number">4</span>, <span class="number">2</span>)</span><br><span class="line">C = A @ B            <span class="comment"># 矩阵乘法，结果形状 (3, 2)</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;矩阵乘法结果 shape: <span class="subst">&#123;C.shape&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># ===== 形状操作 =====</span></span><br><span class="line">t = torch.arange(<span class="number">12</span>)</span><br><span class="line"><span class="built_in">print</span>(t.shape)                    <span class="comment"># torch.Size([12])</span></span><br><span class="line"><span class="built_in">print</span>(t.reshape(<span class="number">3</span>, <span class="number">4</span>).shape)      <span class="comment"># torch.Size([3, 4])</span></span><br><span class="line"><span class="built_in">print</span>(t.view(<span class="number">2</span>, <span class="number">6</span>).shape)         <span class="comment"># torch.Size([2, 6])</span></span><br><span class="line"><span class="built_in">print</span>(t.reshape(<span class="number">3</span>, -<span class="number">1</span>).shape)     <span class="comment"># torch.Size([3, 4])，-1 自动计算</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ===== GPU 加速 =====</span></span><br><span class="line"><span class="keyword">if</span> torch.cuda.is_available():</span><br><span class="line">    device = torch.device(<span class="string">&quot;cuda&quot;</span>)</span><br><span class="line">    x = x.to(device)           <span class="comment"># 移到 GPU</span></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;使用设备: <span class="subst">&#123;x.device&#125;</span>&quot;</span>)</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;没有 GPU，使用 CPU 训练（入门阶段足够）&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># ===== 与 NumPy 互转 =====</span></span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line">arr = np.array([<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>])</span><br><span class="line">t = torch.from_numpy(arr)        <span class="comment"># NumPy → Tensor</span></span><br><span class="line">arr_back = t.numpy()             <span class="comment"># Tensor → NumPy</span></span><br></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><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><span class="line"><span class="keyword">import</span> torch</span><br><span class="line"></span><br><span class="line"><span class="comment"># requires_grad=True 表示需要计算梯度</span></span><br><span class="line">x = torch.tensor([<span class="number">2.0</span>, <span class="number">3.0</span>], requires_grad=<span class="literal">True</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 前向计算</span></span><br><span class="line">y = x ** <span class="number">2</span>           <span class="comment"># y = [4, 9]</span></span><br><span class="line">z = y.<span class="built_in">sum</span>()          <span class="comment"># z = 13</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 反向传播（自动计算梯度）</span></span><br><span class="line">z.backward()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看梯度：dz/dx = 2x</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;x 的梯度: <span class="subst">&#123;x.grad&#125;</span>&quot;</span>)  <span class="comment"># tensor([4., 6.])</span></span><br><span class="line"><span class="comment"># 解释：z = x1² + x2²，∂z/∂x1 = 2*2 = 4，∂z/∂x2 = 2*3 = 6</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ===== 在训练中的使用模式 =====</span></span><br><span class="line">weights = torch.randn(<span class="number">3</span>, requires_grad=<span class="literal">True</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 前向计算</span></span><br><span class="line">loss = (weights ** <span class="number">2</span>).<span class="built_in">sum</span>()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 反向传播</span></span><br><span class="line">loss.backward()</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;梯度: <span class="subst">&#123;weights.grad&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 更新参数（注意：更新参数时不参与计算图）</span></span><br><span class="line"><span class="keyword">with</span> torch.no_grad():</span><br><span class="line">    weights -= <span class="number">0.1</span> * weights.grad   <span class="comment"># 梯度下降：w = w - lr * grad</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 清零梯度（每次迭代前必须清零，否则梯度会累加）</span></span><br><span class="line">weights.grad.zero_()</span><br></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><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><span class="line"><span class="keyword">import</span> torch</span><br><span class="line"><span class="keyword">import</span> torch.nn <span class="keyword">as</span> nn</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">SimpleNet</span>(nn.Module):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;一个简单的全连接神经网络。&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, input_size=<span class="number">4</span>, hidden_size=<span class="number">16</span>, num_classes=<span class="number">3</span></span>):</span><br><span class="line">        <span class="built_in">super</span>().__init__()</span><br><span class="line">        <span class="comment"># 定义网络层</span></span><br><span class="line">        <span class="variable language_">self</span>.fc1 = nn.Linear(input_size, hidden_size)   <span class="comment"># 输入层 → 隐藏层</span></span><br><span class="line">        <span class="variable language_">self</span>.relu = nn.ReLU()                           <span class="comment"># 激活函数</span></span><br><span class="line">        <span class="variable language_">self</span>.dropout = nn.Dropout(<span class="number">0.2</span>)                  <span class="comment"># Dropout 防过拟合</span></span><br><span class="line">        <span class="variable language_">self</span>.fc2 = nn.Linear(hidden_size, num_classes)  <span class="comment"># 隐藏层 → 输出层</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">forward</span>(<span class="params">self, x</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;前向传播：定义数据如何流过网络。&quot;&quot;&quot;</span></span><br><span class="line">        x = <span class="variable language_">self</span>.fc1(x)        <span class="comment"># 线性变换: (batch, 4) → (batch, 16)</span></span><br><span class="line">        x = <span class="variable language_">self</span>.relu(x)       <span class="comment"># 激活: 引入非线性</span></span><br><span class="line">        x = <span class="variable language_">self</span>.dropout(x)    <span class="comment"># 随机丢弃 20% 神经元（训练时生效）</span></span><br><span class="line">        x = <span class="variable language_">self</span>.fc2(x)        <span class="comment"># 线性变换: (batch, 16) → (batch, 3)</span></span><br><span class="line">        <span class="keyword">return</span> x</span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建模型</span></span><br><span class="line">model = SimpleNet(input_size=<span class="number">4</span>, hidden_size=<span class="number">16</span>, num_classes=<span class="number">3</span>)</span><br><span class="line"><span class="built_in">print</span>(model)</span><br><span class="line"><span class="comment"># 打印模型结构和参数数量</span></span><br><span class="line">total_params = <span class="built_in">sum</span>(p.numel() <span class="keyword">for</span> p <span class="keyword">in</span> model.parameters())</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;总参数量: <span class="subst">&#123;total_params&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 测试前向传播</span></span><br><span class="line">dummy_input = torch.randn(<span class="number">5</span>, <span class="number">4</span>)   <span class="comment"># batch_size=5，4 个特征</span></span><br><span class="line">output = model(dummy_input)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;输出形状: <span class="subst">&#123;output.shape&#125;</span>&quot;</span>)  <span class="comment"># torch.Size([5, 3])</span></span><br></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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> torch</span><br><span class="line"><span class="keyword">import</span> torch.nn <span class="keyword">as</span> nn</span><br><span class="line"><span class="keyword">import</span> torch.optim <span class="keyword">as</span> optim</span><br><span class="line"><span class="keyword">from</span> torch.utils.data <span class="keyword">import</span> DataLoader, TensorDataset</span><br><span class="line"><span class="keyword">from</span> sklearn.datasets <span class="keyword">import</span> load_iris</span><br><span class="line"><span class="keyword">from</span> sklearn.model_selection <span class="keyword">import</span> train_test_split</span><br><span class="line"><span class="keyword">from</span> sklearn.preprocessing <span class="keyword">import</span> StandardScaler</span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"></span><br><span class="line"><span class="comment"># ===== 1. 准备数据 =====</span></span><br><span class="line">iris = load_iris()</span><br><span class="line">X, y = iris.data, iris.target</span><br><span class="line"></span><br><span class="line">X_train, X_test, y_train, y_test = train_test_split(</span><br><span class="line">    X, y, test_size=<span class="number">0.2</span>, random_state=<span class="number">42</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 标准化</span></span><br><span class="line">scaler = StandardScaler()</span><br><span class="line">X_train = scaler.fit_transform(X_train)</span><br><span class="line">X_test = scaler.transform(X_test)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 转为 PyTorch Tensor</span></span><br><span class="line">X_train_tensor = torch.FloatTensor(X_train)</span><br><span class="line">y_train_tensor = torch.LongTensor(y_train)</span><br><span class="line">X_test_tensor = torch.FloatTensor(X_test)</span><br><span class="line">y_test_tensor = torch.LongTensor(y_test)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建 DataLoader（自动分批）</span></span><br><span class="line">train_dataset = TensorDataset(X_train_tensor, y_train_tensor)</span><br><span class="line">train_loader = DataLoader(train_dataset, batch_size=<span class="number">16</span>, shuffle=<span class="literal">True</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># ===== 2. 创建模型、损失函数、优化器 =====</span></span><br><span class="line">model = SimpleNet(input_size=<span class="number">4</span>, hidden_size=<span class="number">16</span>, num_classes=<span class="number">3</span>)</span><br><span class="line"></span><br><span class="line">criterion = nn.CrossEntropyLoss()                        <span class="comment"># 多分类用交叉熵损失</span></span><br><span class="line">optimizer = optim.Adam(model.parameters(), lr=<span class="number">0.01</span>)      <span class="comment"># Adam 优化器</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ===== 3. 训练循环 =====</span></span><br><span class="line">num_epochs = <span class="number">50</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> epoch <span class="keyword">in</span> <span class="built_in">range</span>(num_epochs):</span><br><span class="line">    model.train()  <span class="comment"># 设置为训练模式（启用 Dropout 等）</span></span><br><span class="line">    running_loss = <span class="number">0.0</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> batch_X, batch_y <span class="keyword">in</span> train_loader:</span><br><span class="line">        <span class="comment"># 第 1 步：清零梯度（防止梯度累加）</span></span><br><span class="line">        optimizer.zero_grad()</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 第 2 步：前向传播（计算预测值）</span></span><br><span class="line">        outputs = model(batch_X)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 第 3 步：计算损失（预测值 vs 真实值）</span></span><br><span class="line">        loss = criterion(outputs, batch_y)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 第 4 步：反向传播（计算梯度）</span></span><br><span class="line">        loss.backward()</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 第 5 步：更新参数（w = w - lr * grad）</span></span><br><span class="line">        optimizer.step()</span><br><span class="line"></span><br><span class="line">        running_loss += loss.item()</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 每 10 个 epoch 打印一次训练损失</span></span><br><span class="line">    <span class="keyword">if</span> (epoch + <span class="number">1</span>) % <span class="number">10</span> == <span class="number">0</span>:</span><br><span class="line">        avg_loss = running_loss / <span class="built_in">len</span>(train_loader)</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;Epoch [<span class="subst">&#123;epoch + <span class="number">1</span>&#125;</span>/<span class="subst">&#123;num_epochs&#125;</span>], 损失: <span class="subst">&#123;avg_loss:<span class="number">.4</span>f&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># ===== 4. 评估模型 =====</span></span><br><span class="line">model.<span class="built_in">eval</span>()  <span class="comment"># 设置为评估模式（关闭 Dropout 等）</span></span><br><span class="line"><span class="keyword">with</span> torch.no_grad():  <span class="comment"># 评估时不计算梯度，节省内存</span></span><br><span class="line">    outputs = model(X_test_tensor)</span><br><span class="line">    _, predicted = torch.<span class="built_in">max</span>(outputs, <span class="number">1</span>)  <span class="comment"># 取最大概率的类别</span></span><br><span class="line">    correct = (predicted == y_test_tensor).<span class="built_in">sum</span>().item()</span><br><span class="line">    accuracy = correct / y_test_tensor.size(<span class="number">0</span>)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;\n测试集准确率: <span class="subst">&#123;accuracy:<span class="number">.4</span>f&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># ===== 5. 保存模型 =====</span></span><br><span class="line">torch.save(model.state_dict(), <span class="string">&quot;iris_net.pth&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;模型已保存到 iris_net.pth&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 加载模型</span></span><br><span class="line">loaded_model = SimpleNet(input_size=<span class="number">4</span>, hidden_size=<span class="number">16</span>, num_classes=<span class="number">3</span>)</span><br><span class="line">loaded_model.load_state_dict(torch.load(<span class="string">&quot;iris_net.pth&quot;</span>))</span><br><span class="line">loaded_model.<span class="built_in">eval</span>()</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;模型加载成功&quot;</span>)</span><br></pre></td></tr></table></figure><h3 id="5-4-LLM-应用开发实战"><a href="#5-4-LLM-应用开发实战" class="headerlink" title="5.4 LLM 应用开发实战"></a>5.4 LLM 应用开发实战</h3><p>使用 LangChain 构建 RAG（Retrieval-Augmented Generation，检索增强生成）应用：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pip install langchain langchain-openai langchain-community chromadb sentence-transformers</span><br></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><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><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">RAG 应用：让 LLM 基于你自己的文档回答问题</span></span><br><span class="line"><span class="string">流程：文档加载 → 文档分割 → 向量化存储 → 检索 → 生成回答</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> langchain_community.document_loaders <span class="keyword">import</span> TextLoader, PyPDFLoader</span><br><span class="line"><span class="keyword">from</span> langchain_text_splitters <span class="keyword">import</span> RecursiveCharacterTextSplitter</span><br><span class="line"><span class="keyword">from</span> langchain_community.embeddings <span class="keyword">import</span> HuggingFaceEmbeddings</span><br><span class="line"><span class="keyword">from</span> langchain_community.vectorstores <span class="keyword">import</span> Chroma</span><br><span class="line"><span class="keyword">from</span> langchain_openai <span class="keyword">import</span> ChatOpenAI</span><br><span class="line"><span class="keyword">from</span> langchain.chains <span class="keyword">import</span> RetrievalQA</span><br><span class="line"><span class="keyword">from</span> langchain.prompts <span class="keyword">import</span> PromptTemplate</span><br><span class="line"></span><br><span class="line"><span class="comment"># ===== 1. 文档加载和分割 =====</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 加载文本文件</span></span><br><span class="line">loader = TextLoader(<span class="string">&quot;company_faq.txt&quot;</span>, encoding=<span class="string">&quot;utf-8&quot;</span>)</span><br><span class="line">documents = loader.load()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 也可以加载 PDF</span></span><br><span class="line"><span class="comment"># loader = PyPDFLoader(&quot;company_manual.pdf&quot;)</span></span><br><span class="line"><span class="comment"># documents = loader.load()</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;加载了 <span class="subst">&#123;<span class="built_in">len</span>(documents)&#125;</span> 个文档&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 文档分割：把长文档切成小段，便于检索</span></span><br><span class="line">text_splitter = RecursiveCharacterTextSplitter(</span><br><span class="line">    chunk_size=<span class="number">500</span>,       <span class="comment"># 每段最多 500 个字符</span></span><br><span class="line">    chunk_overlap=<span class="number">50</span>,     <span class="comment"># 相邻段落重叠 50 个字符，保证上下文连贯</span></span><br><span class="line">    separators=[<span class="string">&quot;\n\n&quot;</span>, <span class="string">&quot;\n&quot;</span>, <span class="string">&quot;。&quot;</span>, <span class="string">&quot;！&quot;</span>, <span class="string">&quot;？&quot;</span>, <span class="string">&quot;，&quot;</span>, <span class="string">&quot; &quot;</span>, <span class="string">&quot;&quot;</span>],  <span class="comment"># 按优先级切分</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line">splits = text_splitter.split_documents(documents)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;分割为 <span class="subst">&#123;<span class="built_in">len</span>(splits)&#125;</span> 个文本块&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># ===== 2. 向量存储和检索 =====</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用本地嵌入模型（不依赖 OpenAI API）</span></span><br><span class="line"><span class="comment"># 首次运行会自动下载模型，约 400MB</span></span><br><span class="line">embeddings = HuggingFaceEmbeddings(</span><br><span class="line">    model_name=<span class="string">&quot;shibing624/text2vec-base-chinese&quot;</span>,  <span class="comment"># 中文嵌入模型</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建向量数据库并存储文档</span></span><br><span class="line">vectorstore = Chroma.from_documents(</span><br><span class="line">    documents=splits,</span><br><span class="line">    embedding=embeddings,</span><br><span class="line">    persist_directory=<span class="string">&quot;./chroma_db&quot;</span>,  <span class="comment"># 持久化到本地</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 测试检索：找出与问题最相关的文本块</span></span><br><span class="line">query = <span class="string">&quot;公司的年假有多少天？&quot;</span></span><br><span class="line">results = vectorstore.similarity_search(query, k=<span class="number">3</span>)  <span class="comment"># 返回最相关的 3 段</span></span><br><span class="line"><span class="keyword">for</span> i, doc <span class="keyword">in</span> <span class="built_in">enumerate</span>(results):</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;\n--- 相关片段 <span class="subst">&#123;i + <span class="number">1</span>&#125;</span> ---&quot;</span>)</span><br><span class="line">    <span class="built_in">print</span>(doc.page_content[:<span class="number">200</span>])</span><br><span class="line"></span><br><span class="line"><span class="comment"># ===== 3. 问答链 =====</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 初始化 LLM（需要设置 OPENAI_API_KEY 环境变量）</span></span><br><span class="line"><span class="comment"># 也可以换成其他模型，如 ChatOllama 使用本地模型</span></span><br><span class="line">llm = ChatOpenAI(</span><br><span class="line">    model=<span class="string">&quot;gpt-4o-mini&quot;</span>,</span><br><span class="line">    temperature=<span class="number">0</span>,  <span class="comment"># 温度越低，回答越确定</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 自定义提示词模板</span></span><br><span class="line">prompt_template = <span class="string">&quot;&quot;&quot;请根据以下参考内容回答问题。如果参考内容中没有相关信息，</span></span><br><span class="line"><span class="string">请回答&quot;根据已有信息无法回答该问题&quot;，不要编造内容。</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">参考内容：</span></span><br><span class="line"><span class="string">&#123;context&#125;</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">问题：&#123;question&#125;</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">回答：&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">prompt = PromptTemplate(</span><br><span class="line">    template=prompt_template,</span><br><span class="line">    input_variables=[<span class="string">&quot;context&quot;</span>, <span class="string">&quot;question&quot;</span>],</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建 RAG 问答链</span></span><br><span class="line">qa_chain = RetrievalQA.from_chain_type(</span><br><span class="line">    llm=llm,</span><br><span class="line">    chain_type=<span class="string">&quot;stuff&quot;</span>,                        <span class="comment"># 把所有检索到的片段塞进提示词</span></span><br><span class="line">    retriever=vectorstore.as_retriever(        <span class="comment"># 检索器</span></span><br><span class="line">        search_type=<span class="string">&quot;similarity&quot;</span>,              <span class="comment"># 相似度搜索</span></span><br><span class="line">        search_kwargs=&#123;<span class="string">&quot;k&quot;</span>: <span class="number">3</span>&#125;,                <span class="comment"># 返回 3 个最相关的片段</span></span><br><span class="line">    ),</span><br><span class="line">    return_source_documents=<span class="literal">True</span>,              <span class="comment"># 返回来源文档</span></span><br><span class="line">    chain_type_kwargs=&#123;<span class="string">&quot;prompt&quot;</span>: prompt&#125;,      <span class="comment"># 使用自定义提示词</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment"># ===== 4. 提问 =====</span></span><br><span class="line"></span><br><span class="line">questions = [</span><br><span class="line">    <span class="string">&quot;公司的年假有多少天？&quot;</span>,</span><br><span class="line">    <span class="string">&quot;迟到怎么扣款？&quot;</span>,</span><br><span class="line">    <span class="string">&quot;公司提供什么培训？&quot;</span>,</span><br><span class="line">]</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> question <span class="keyword">in</span> questions:</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;\n<span class="subst">&#123;<span class="string">&#x27;=&#x27;</span>*<span class="number">50</span>&#125;</span>&quot;</span>)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;问题: <span class="subst">&#123;question&#125;</span>&quot;</span>)</span><br><span class="line">    result = qa_chain.invoke(&#123;<span class="string">&quot;query&quot;</span>: question&#125;)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;回答: <span class="subst">&#123;result[<span class="string">&#x27;result&#x27;</span>]&#125;</span>&quot;</span>)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;来源: <span class="subst">&#123;result[<span class="string">&#x27;source_documents&#x27;</span>][<span class="number">0</span>].page_content[:<span class="number">100</span>]&#125;</span>...&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># ===== 5. 对话模式（保留上下文） =====</span></span><br><span class="line"><span class="keyword">from</span> langchain.memory <span class="keyword">import</span> ConversationBufferMemory</span><br><span class="line"><span class="keyword">from</span> langchain.chains <span class="keyword">import</span> ConversationalRetrievalChain</span><br><span class="line"></span><br><span class="line">memory = ConversationBufferMemory(</span><br><span class="line">    memory_key=<span class="string">&quot;chat_history&quot;</span>,</span><br><span class="line">    return_messages=<span class="literal">True</span>,</span><br><span class="line">    output_key=<span class="string">&quot;answer&quot;</span>,</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line">conv_chain = ConversationalRetrievalChain.from_llm(</span><br><span class="line">    llm=llm,</span><br><span class="line">    retriever=vectorstore.as_retriever(search_kwargs=&#123;<span class="string">&quot;k&quot;</span>: <span class="number">3</span>&#125;),</span><br><span class="line">    memory=memory,</span><br><span class="line">    return_source_documents=<span class="literal">True</span>,</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 多轮对话</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;\n&quot;</span> + <span class="string">&quot;=&quot;</span> * <span class="number">50</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;对话模式:&quot;</span>)</span><br><span class="line">response1 = conv_chain.invoke(&#123;<span class="string">&quot;question&quot;</span>: <span class="string">&quot;公司的年假有多少天？&quot;</span>&#125;)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;Q: 公司的年假有多少天？&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;A: <span class="subst">&#123;response1[<span class="string">&#x27;answer&#x27;</span>]&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line">response2 = conv_chain.invoke(&#123;<span class="string">&quot;question&quot;</span>: <span class="string">&quot;那病假呢？&quot;</span>&#125;)  <span class="comment"># 会理解&quot;病假&quot;是追问</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;Q: 那病假呢？&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;A: <span class="subst">&#123;response2[<span class="string">&#x27;answer&#x27;</span>]&#125;</span>&quot;</span>)</span><br></pre></td></tr></table></figure><h3 id="5-5-常见新手坑"><a href="#5-5-常见新手坑" class="headerlink" title="5.5 常见新手坑"></a>5.5 常见新手坑</h3><table><thead><tr><th>坑</th><th>说明</th><th>解决方案</th></tr></thead><tbody><tr><td><strong>跳过数学直接调包</strong></td><td>不理解原理，遇到问题无法排查</td><td>至少理解线性回归、梯度下降、损失函数的含义</td></tr><tr><td><strong>不做数据预处理</strong></td><td>直接把原始数据喂给模型</td><td>先清洗、标准化、处理缺失值</td></tr><tr><td><strong>测试集泄漏</strong></td><td>在全数据上标准化&#x2F;调参，再用测试集评估</td><td>预处理只 fit 训练集，测试集只 transform</td></tr><tr><td><strong>只用准确率评估</strong></td><td>数据不均衡时准确率有误导性</td><td>同时看精确率、召回率、F1 和混淆矩阵</td></tr><tr><td><strong>过拟合不处理</strong></td><td>训练集准确率 99%，测试集 60%</td><td>加 Dropout、正则化、数据增强、早停</td></tr><tr><td><strong>一上来就用深度学习</strong></td><td>简单问题用复杂模型，训练慢效果不一定好</td><td>先试传统 ML（逻辑回归&#x2F;随机森林），不够再用 DL</td></tr><tr><td><strong>忽略随机种子</strong></td><td>每次运行结果不同，无法复现</td><td>设置 <code>random_state=42</code> &#x2F; <code>torch.manual_seed(42)</code></td></tr><tr><td><strong>直接上生产</strong></td><td>模型没有持久化，重启后丢失</td><td>用 <code>joblib.dump</code> &#x2F; <code>torch.save</code> 保存模型</td></tr></tbody></table><hr><h2 id="6-桌面应用与-GUI"><a href="#6-桌面应用与-GUI" class="headerlink" title="6. 桌面应用与 GUI"></a>6. 桌面应用与 GUI</h2><h3 id="6-1-GUI-开发是什么？选择框架"><a href="#6-1-GUI-开发是什么？选择框架" class="headerlink" title="6.1 GUI 开发是什么？选择框架"></a>6.1 GUI 开发是什么？选择框架</h3><p>GUI（Graphical User Interface，图形用户界面）开发就是做”带窗口、按钮、输入框的桌面软件”。和命令行不同，GUI 让用户通过鼠标点击、拖拽来操作程序。</p><p><strong>框架选择指南</strong>：</p><table><thead><tr><th>框架</th><th>特点</th><th>适合谁</th><th>安装方式</th></tr></thead><tbody><tr><td><strong>Tkinter</strong></td><td>Python 标准库内置，无需安装</td><td>零基础入门、小型工具</td><td>无需安装，Python 自带</td></tr><tr><td><strong>PyQt &#x2F; PySide</strong></td><td>功能最强大，组件丰富</td><td>想做专业桌面应用的人</td><td><code>pip install PyQt6</code> 或 <code>pip install PySide6</code></td></tr><tr><td><strong>Kivy</strong></td><td>支持触屏，可打包手机 App</td><td>想做跨平台移动应用</td><td><code>pip install kivy</code></td></tr><tr><td><strong>Dear PyGui</strong></td><td>GPU 加速，适合数据可视化</td><td>做实时数据展示工具</td><td><code>pip install dearpygui</code></td></tr><tr><td><strong>Flet</strong></td><td>类 Flutter 语法，Web+桌面</td><td>想做现代风格应用</td><td><code>pip install flet</code></td></tr></tbody></table><blockquote><p><strong>新手建议</strong>：先学 Tkinter（不用装任何东西），再做 PyQt 项目。这两个覆盖 90% 的桌面开发需求。</p></blockquote><h3 id="6-2-Tkinter-完整项目：文件搜索工具"><a href="#6-2-Tkinter-完整项目：文件搜索工具" class="headerlink" title="6.2 Tkinter 完整项目：文件搜索工具"></a>6.2 Tkinter 完整项目：文件搜索工具</h3><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><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><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">文件搜索工具 - Tkinter 完整项目</span></span><br><span class="line"><span class="string">功能：按文件名关键词和大小范围搜索文件，支持双击打开文件所在目录</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"><span class="keyword">import</span> tkinter <span class="keyword">as</span> tk</span><br><span class="line"><span class="keyword">from</span> tkinter <span class="keyword">import</span> ttk, messagebox, filedialog</span><br><span class="line"><span class="keyword">from</span> pathlib <span class="keyword">import</span> Path</span><br><span class="line"><span class="keyword">from</span> datetime <span class="keyword">import</span> datetime</span><br><span class="line"><span class="keyword">import</span> threading</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">FileSearchApp</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;文件搜索工具应用。&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, root: tk.Tk</span>):</span><br><span class="line">        <span class="variable language_">self</span>.root = root</span><br><span class="line">        <span class="variable language_">self</span>.root.title(<span class="string">&quot;文件搜索工具&quot;</span>)</span><br><span class="line">        <span class="variable language_">self</span>.root.geometry(<span class="string">&quot;750x520&quot;</span>)</span><br><span class="line">        <span class="variable language_">self</span>.root.minsize(<span class="number">600</span>, <span class="number">400</span>)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 搜索状态</span></span><br><span class="line">        <span class="variable language_">self</span>.is_searching = <span class="literal">False</span></span><br><span class="line"></span><br><span class="line">        <span class="comment"># 构建界面</span></span><br><span class="line">        <span class="variable language_">self</span>._build_ui()</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">_build_ui</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;构建完整的用户界面。&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment"># ===== 顶部：搜索条件区域 =====</span></span><br><span class="line">        top_frame = ttk.LabelFrame(<span class="variable language_">self</span>.root, text=<span class="string">&quot;搜索条件&quot;</span>, padding=<span class="number">10</span>)</span><br><span class="line">        top_frame.pack(fill=<span class="string">&quot;x&quot;</span>, padx=<span class="number">10</span>, pady=(<span class="number">10</span>, <span class="number">5</span>))</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 第1行：搜索目录</span></span><br><span class="line">        dir_frame = ttk.Frame(top_frame)</span><br><span class="line">        dir_frame.pack(fill=<span class="string">&quot;x&quot;</span>, pady=<span class="number">2</span>)</span><br><span class="line"></span><br><span class="line">        ttk.Label(dir_frame, text=<span class="string">&quot;搜索目录：&quot;</span>).pack(side=<span class="string">&quot;left&quot;</span>)</span><br><span class="line">        <span class="variable language_">self</span>.dir_var = tk.StringVar(value=os.path.expanduser(<span class="string">&quot;~&quot;</span>))</span><br><span class="line">        <span class="variable language_">self</span>.dir_entry = ttk.Entry(dir_frame, textvariable=<span class="variable language_">self</span>.dir_var, width=<span class="number">55</span>)</span><br><span class="line">        <span class="variable language_">self</span>.dir_entry.pack(side=<span class="string">&quot;left&quot;</span>, padx=<span class="number">5</span>, fill=<span class="string">&quot;x&quot;</span>, expand=<span class="literal">True</span>)</span><br><span class="line">        ttk.Button(dir_frame, text=<span class="string">&quot;浏览&quot;</span>, command=<span class="variable language_">self</span>._browse_dir).pack(side=<span class="string">&quot;left&quot;</span>)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 第2行：文件名关键词</span></span><br><span class="line">        name_frame = ttk.Frame(top_frame)</span><br><span class="line">        name_frame.pack(fill=<span class="string">&quot;x&quot;</span>, pady=<span class="number">2</span>)</span><br><span class="line"></span><br><span class="line">        ttk.Label(name_frame, text=<span class="string">&quot;文件名关键词：&quot;</span>).pack(side=<span class="string">&quot;left&quot;</span>)</span><br><span class="line">        <span class="variable language_">self</span>.keyword_var = tk.StringVar()</span><br><span class="line">        keyword_entry = ttk.Entry(name_frame, textvariable=<span class="variable language_">self</span>.keyword_var, width=<span class="number">30</span>)</span><br><span class="line">        keyword_entry.pack(side=<span class="string">&quot;left&quot;</span>, padx=<span class="number">5</span>)</span><br><span class="line">        keyword_entry.bind(<span class="string">&quot;&lt;Return&gt;&quot;</span>, <span class="keyword">lambda</span> e: <span class="variable language_">self</span>.start_search())  <span class="comment"># 回车搜索</span></span><br><span class="line"></span><br><span class="line">        <span class="comment"># 第3行：文件大小范围</span></span><br><span class="line">        size_frame = ttk.Frame(top_frame)</span><br><span class="line">        size_frame.pack(fill=<span class="string">&quot;x&quot;</span>, pady=<span class="number">2</span>)</span><br><span class="line"></span><br><span class="line">        ttk.Label(size_frame, text=<span class="string">&quot;最小大小(MB)：&quot;</span>).pack(side=<span class="string">&quot;left&quot;</span>)</span><br><span class="line">        <span class="variable language_">self</span>.min_size_var = tk.StringVar(value=<span class="string">&quot;0&quot;</span>)</span><br><span class="line">        ttk.Entry(size_frame, textvariable=<span class="variable language_">self</span>.min_size_var, width=<span class="number">8</span>).pack(side=<span class="string">&quot;left&quot;</span>, padx=<span class="number">5</span>)</span><br><span class="line"></span><br><span class="line">        ttk.Label(size_frame, text=<span class="string">&quot;最大大小(MB)：&quot;</span>).pack(side=<span class="string">&quot;left&quot;</span>, padx=(<span class="number">15</span>, <span class="number">0</span>))</span><br><span class="line">        <span class="variable language_">self</span>.max_size_var = tk.StringVar(value=<span class="string">&quot;0&quot;</span>)</span><br><span class="line">        ttk.Entry(size_frame, textvariable=<span class="variable language_">self</span>.max_size_var, width=<span class="number">8</span>).pack(side=<span class="string">&quot;left&quot;</span>, padx=<span class="number">5</span>)</span><br><span class="line">        ttk.Label(size_frame, text=<span class="string">&quot;（0 表示不限）&quot;</span>).pack(side=<span class="string">&quot;left&quot;</span>)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 搜索按钮</span></span><br><span class="line">        btn_frame = ttk.Frame(top_frame)</span><br><span class="line">        btn_frame.pack(fill=<span class="string">&quot;x&quot;</span>, pady=(<span class="number">8</span>, <span class="number">0</span>))</span><br><span class="line"></span><br><span class="line">        <span class="variable language_">self</span>.search_btn = ttk.Button(btn_frame, text=<span class="string">&quot;🔍 开始搜索&quot;</span>, command=<span class="variable language_">self</span>.start_search)</span><br><span class="line">        <span class="variable language_">self</span>.search_btn.pack(side=<span class="string">&quot;left&quot;</span>, padx=<span class="number">5</span>)</span><br><span class="line"></span><br><span class="line">        <span class="variable language_">self</span>.stop_btn = ttk.Button(btn_frame, text=<span class="string">&quot;⏹ 停止&quot;</span>, command=<span class="variable language_">self</span>.stop_search, state=<span class="string">&quot;disabled&quot;</span>)</span><br><span class="line">        <span class="variable language_">self</span>.stop_btn.pack(side=<span class="string">&quot;left&quot;</span>, padx=<span class="number">5</span>)</span><br><span class="line"></span><br><span class="line">        <span class="variable language_">self</span>.status_var = tk.StringVar(value=<span class="string">&quot;就绪&quot;</span>)</span><br><span class="line">        ttk.Label(btn_frame, textvariable=<span class="variable language_">self</span>.status_var).pack(side=<span class="string">&quot;right&quot;</span>, padx=<span class="number">5</span>)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># ===== 中部：搜索结果列表 =====</span></span><br><span class="line">        result_frame = ttk.LabelFrame(<span class="variable language_">self</span>.root, text=<span class="string">&quot;搜索结果&quot;</span>, padding=<span class="number">5</span>)</span><br><span class="line">        result_frame.pack(fill=<span class="string">&quot;both&quot;</span>, expand=<span class="literal">True</span>, padx=<span class="number">10</span>, pady=<span class="number">5</span>)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 使用 Treeview 显示表格数据</span></span><br><span class="line">        columns = (<span class="string">&quot;name&quot;</span>, <span class="string">&quot;size&quot;</span>, <span class="string">&quot;modified&quot;</span>, <span class="string">&quot;path&quot;</span>)</span><br><span class="line">        <span class="variable language_">self</span>.tree = ttk.Treeview(result_frame, columns=columns, show=<span class="string">&quot;headings&quot;</span>, height=<span class="number">15</span>)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 设置列标题和宽度</span></span><br><span class="line">        <span class="variable language_">self</span>.tree.heading(<span class="string">&quot;name&quot;</span>, text=<span class="string">&quot;文件名&quot;</span>)</span><br><span class="line">        <span class="variable language_">self</span>.tree.heading(<span class="string">&quot;size&quot;</span>, text=<span class="string">&quot;大小&quot;</span>)</span><br><span class="line">        <span class="variable language_">self</span>.tree.heading(<span class="string">&quot;modified&quot;</span>, text=<span class="string">&quot;修改时间&quot;</span>)</span><br><span class="line">        <span class="variable language_">self</span>.tree.heading(<span class="string">&quot;path&quot;</span>, text=<span class="string">&quot;完整路径&quot;</span>)</span><br><span class="line"></span><br><span class="line">        <span class="variable language_">self</span>.tree.column(<span class="string">&quot;name&quot;</span>, width=<span class="number">180</span>)</span><br><span class="line">        <span class="variable language_">self</span>.tree.column(<span class="string">&quot;size&quot;</span>, width=<span class="number">80</span>, anchor=<span class="string">&quot;e&quot;</span>)</span><br><span class="line">        <span class="variable language_">self</span>.tree.column(<span class="string">&quot;modified&quot;</span>, width=<span class="number">140</span>, anchor=<span class="string">&quot;center&quot;</span>)</span><br><span class="line">        <span class="variable language_">self</span>.tree.column(<span class="string">&quot;path&quot;</span>, width=<span class="number">300</span>)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 滚动条</span></span><br><span class="line">        scrollbar = ttk.Scrollbar(result_frame, orient=<span class="string">&quot;vertical&quot;</span>, command=<span class="variable language_">self</span>.tree.yview)</span><br><span class="line">        <span class="variable language_">self</span>.tree.configure(yscrollcommand=scrollbar.<span class="built_in">set</span>)</span><br><span class="line"></span><br><span class="line">        <span class="variable language_">self</span>.tree.pack(side=<span class="string">&quot;left&quot;</span>, fill=<span class="string">&quot;both&quot;</span>, expand=<span class="literal">True</span>)</span><br><span class="line">        scrollbar.pack(side=<span class="string">&quot;right&quot;</span>, fill=<span class="string">&quot;y&quot;</span>)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 双击打开文件所在目录</span></span><br><span class="line">        <span class="variable language_">self</span>.tree.bind(<span class="string">&quot;&lt;Double-1&gt;&quot;</span>, <span class="variable language_">self</span>._on_double_click)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># ===== 底部：状态栏 =====</span></span><br><span class="line">        <span class="variable language_">self</span>.count_var = tk.StringVar(value=<span class="string">&quot;共 0 个结果&quot;</span>)</span><br><span class="line">        ttk.Label(<span class="variable language_">self</span>.root, textvariable=<span class="variable language_">self</span>.count_var, relief=<span class="string">&quot;sunken&quot;</span>).pack(</span><br><span class="line">            fill=<span class="string">&quot;x&quot;</span>, padx=<span class="number">10</span>, pady=(<span class="number">0</span>, <span class="number">10</span>)</span><br><span class="line">        )</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">_browse_dir</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;打开文件夹选择对话框。&quot;&quot;&quot;</span></span><br><span class="line">        selected = filedialog.askdirectory(initialdir=<span class="variable language_">self</span>.dir_var.get())</span><br><span class="line">        <span class="keyword">if</span> selected:</span><br><span class="line">            <span class="variable language_">self</span>.dir_var.<span class="built_in">set</span>(selected)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">_on_double_click</span>(<span class="params">self, event</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;双击结果行，打开文件所在目录。&quot;&quot;&quot;</span></span><br><span class="line">        selected = <span class="variable language_">self</span>.tree.selection()</span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> selected:</span><br><span class="line">            <span class="keyword">return</span></span><br><span class="line">        item = <span class="variable language_">self</span>.tree.item(selected[<span class="number">0</span>])</span><br><span class="line">        filepath = item[<span class="string">&quot;values&quot;</span>][<span class="number">3</span>]  <span class="comment"># 完整路径</span></span><br><span class="line">        dir_path = os.path.dirname(filepath)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 用系统默认方式打开目录</span></span><br><span class="line">        <span class="keyword">if</span> os.path.exists(dir_path):</span><br><span class="line">            os.startfile(dir_path)  <span class="comment"># Windows</span></span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            messagebox.showwarning(<span class="string">&quot;提示&quot;</span>, <span class="string">f&quot;目录不存在：<span class="subst">&#123;dir_path&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">start_search</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;开始搜索（在新线程中执行，避免界面卡死）。&quot;&quot;&quot;</span></span><br><span class="line">        search_dir = <span class="variable language_">self</span>.dir_var.get()</span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> os.path.isdir(search_dir):</span><br><span class="line">            messagebox.showerror(<span class="string">&quot;错误&quot;</span>, <span class="string">f&quot;目录不存在：<span class="subst">&#123;search_dir&#125;</span>&quot;</span>)</span><br><span class="line">            <span class="keyword">return</span></span><br><span class="line"></span><br><span class="line">        keyword = <span class="variable language_">self</span>.keyword_var.get().strip().lower()</span><br><span class="line">        <span class="keyword">try</span>:</span><br><span class="line">            min_size = <span class="built_in">float</span>(<span class="variable language_">self</span>.min_size_var.get()) * <span class="number">1024</span> * <span class="number">1024</span>  <span class="comment"># 转字节</span></span><br><span class="line">            max_size = <span class="built_in">float</span>(<span class="variable language_">self</span>.max_size_var.get()) * <span class="number">1024</span> * <span class="number">1024</span></span><br><span class="line">        <span class="keyword">except</span> ValueError:</span><br><span class="line">            messagebox.showerror(<span class="string">&quot;错误&quot;</span>, <span class="string">&quot;文件大小请输入数字&quot;</span>)</span><br><span class="line">            <span class="keyword">return</span></span><br><span class="line"></span><br><span class="line">        <span class="comment"># 清空之前的结果</span></span><br><span class="line">        <span class="keyword">for</span> item <span class="keyword">in</span> <span class="variable language_">self</span>.tree.get_children():</span><br><span class="line">            <span class="variable language_">self</span>.tree.delete(item)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 更新状态</span></span><br><span class="line">        <span class="variable language_">self</span>.is_searching = <span class="literal">True</span></span><br><span class="line">        <span class="variable language_">self</span>.search_btn.config(state=<span class="string">&quot;disabled&quot;</span>)</span><br><span class="line">        <span class="variable language_">self</span>.stop_btn.config(state=<span class="string">&quot;normal&quot;</span>)</span><br><span class="line">        <span class="variable language_">self</span>.status_var.<span class="built_in">set</span>(<span class="string">&quot;搜索中...&quot;</span>)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 在新线程中搜索，避免界面卡死</span></span><br><span class="line">        thread = threading.Thread(</span><br><span class="line">            target=<span class="variable language_">self</span>._do_search,</span><br><span class="line">            args=(search_dir, keyword, min_size, max_size),</span><br><span class="line">            daemon=<span class="literal">True</span>,</span><br><span class="line">        )</span><br><span class="line">        thread.start()</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">_do_search</span>(<span class="params">self, search_dir: <span class="built_in">str</span>, keyword: <span class="built_in">str</span>, min_size: <span class="built_in">float</span>, max_size: <span class="built_in">float</span></span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;在后台线程中执行搜索。&quot;&quot;&quot;</span></span><br><span class="line">        found = <span class="number">0</span></span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span> root, dirs, files <span class="keyword">in</span> os.walk(search_dir):</span><br><span class="line">            <span class="keyword">if</span> <span class="keyword">not</span> <span class="variable language_">self</span>.is_searching:</span><br><span class="line">                <span class="keyword">break</span>  <span class="comment"># 用户点了停止</span></span><br><span class="line"></span><br><span class="line">            <span class="keyword">for</span> filename <span class="keyword">in</span> files:</span><br><span class="line">                <span class="keyword">if</span> <span class="keyword">not</span> <span class="variable language_">self</span>.is_searching:</span><br><span class="line">                    <span class="keyword">break</span></span><br><span class="line"></span><br><span class="line">                <span class="comment"># 按文件名过滤</span></span><br><span class="line">                <span class="keyword">if</span> keyword <span class="keyword">and</span> keyword <span class="keyword">not</span> <span class="keyword">in</span> filename.lower():</span><br><span class="line">                    <span class="keyword">continue</span></span><br><span class="line"></span><br><span class="line">                filepath = os.path.join(root, filename)</span><br><span class="line">                <span class="keyword">try</span>:</span><br><span class="line">                    file_size = os.path.getsize(filepath)</span><br><span class="line">                <span class="keyword">except</span> (PermissionError, FileNotFoundError):</span><br><span class="line">                    <span class="keyword">continue</span></span><br><span class="line"></span><br><span class="line">                <span class="comment"># 按大小过滤</span></span><br><span class="line">                <span class="keyword">if</span> min_size &gt; <span class="number">0</span> <span class="keyword">and</span> file_size &lt; min_size:</span><br><span class="line">                    <span class="keyword">continue</span></span><br><span class="line">                <span class="keyword">if</span> max_size &gt; <span class="number">0</span> <span class="keyword">and</span> file_size &gt; max_size:</span><br><span class="line">                    <span class="keyword">continue</span></span><br><span class="line"></span><br><span class="line">                <span class="comment"># 格式化显示</span></span><br><span class="line">                size_str = <span class="variable language_">self</span>._format_size(file_size)</span><br><span class="line">                mtime = os.path.getmtime(filepath)</span><br><span class="line">                modified = datetime.fromtimestamp(mtime).strftime(<span class="string">&quot;%Y-%m-%d %H:%M&quot;</span>)</span><br><span class="line"></span><br><span class="line">                <span class="comment"># 在主线程中更新界面（线程安全）</span></span><br><span class="line">                <span class="variable language_">self</span>.root.after(<span class="number">0</span>, <span class="variable language_">self</span>._add_result, filename, size_str, modified, filepath)</span><br><span class="line">                found += <span class="number">1</span></span><br><span class="line"></span><br><span class="line">        <span class="comment"># 搜索完成，恢复界面状态</span></span><br><span class="line">        <span class="variable language_">self</span>.root.after(<span class="number">0</span>, <span class="variable language_">self</span>._search_done, found)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">_add_result</span>(<span class="params">self, name: <span class="built_in">str</span>, size: <span class="built_in">str</span>, modified: <span class="built_in">str</span>, path: <span class="built_in">str</span></span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;向结果列表添加一行（在主线程中调用）。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="variable language_">self</span>.tree.insert(<span class="string">&quot;&quot;</span>, <span class="string">&quot;end&quot;</span>, values=(name, size, modified, path))</span><br><span class="line">        <span class="variable language_">self</span>.count_var.<span class="built_in">set</span>(<span class="string">f&quot;已找到 <span class="subst">&#123;<span class="built_in">len</span>(self.tree.get_children())&#125;</span> 个结果&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">_search_done</span>(<span class="params">self, found: <span class="built_in">int</span></span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;搜索完成后的回调。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="variable language_">self</span>.is_searching = <span class="literal">False</span></span><br><span class="line">        <span class="variable language_">self</span>.search_btn.config(state=<span class="string">&quot;normal&quot;</span>)</span><br><span class="line">        <span class="variable language_">self</span>.stop_btn.config(state=<span class="string">&quot;disabled&quot;</span>)</span><br><span class="line">        total = <span class="built_in">len</span>(<span class="variable language_">self</span>.tree.get_children())</span><br><span class="line">        <span class="variable language_">self</span>.status_var.<span class="built_in">set</span>(<span class="string">f&quot;搜索完成，共找到 <span class="subst">&#123;total&#125;</span> 个文件&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">stop_search</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;停止搜索。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="variable language_">self</span>.is_searching = <span class="literal">False</span></span><br><span class="line">        <span class="variable language_">self</span>.status_var.<span class="built_in">set</span>(<span class="string">&quot;搜索已停止&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="meta">    @staticmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">_format_size</span>(<span class="params">size_bytes: <span class="built_in">int</span></span>) -&gt; <span class="built_in">str</span>:</span><br><span class="line">        <span class="string">&quot;&quot;&quot;将字节数格式化为可读字符串。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">if</span> size_bytes &lt; <span class="number">1024</span>:</span><br><span class="line">            <span class="keyword">return</span> <span class="string">f&quot;<span class="subst">&#123;size_bytes&#125;</span> B&quot;</span></span><br><span class="line">        <span class="keyword">elif</span> size_bytes &lt; <span class="number">1024</span> * <span class="number">1024</span>:</span><br><span class="line">            <span class="keyword">return</span> <span class="string">f&quot;<span class="subst">&#123;size_bytes / <span class="number">1024</span>:<span class="number">.1</span>f&#125;</span> KB&quot;</span></span><br><span class="line">        <span class="keyword">elif</span> size_bytes &lt; <span class="number">1024</span> * <span class="number">1024</span> * <span class="number">1024</span>:</span><br><span class="line">            <span class="keyword">return</span> <span class="string">f&quot;<span class="subst">&#123;size_bytes / (<span class="number">1024</span> * <span class="number">1024</span>):<span class="number">.1</span>f&#125;</span> MB&quot;</span></span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            <span class="keyword">return</span> <span class="string">f&quot;<span class="subst">&#123;size_bytes / (<span class="number">1024</span> * <span class="number">1024</span> * <span class="number">1024</span>):<span class="number">.2</span>f&#125;</span> GB&quot;</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># ===== 启动应用 =====</span></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    root = tk.Tk()</span><br><span class="line">    app = FileSearchApp(root)</span><br><span class="line">    root.mainloop()</span><br></pre></td></tr></table></figure><blockquote><p><strong>这个项目学到了什么</strong>：</p><ul><li><strong>布局</strong>：用 <code>Frame</code> + <code>LabelFrame</code> 分区，<code>pack</code> 排列组件</li><li><strong>输入组件</strong>：<code>Entry</code>（文本框）、<code>Button</code>（按钮）、<code>filedialog</code>（文件对话框）</li><li><strong>表格展示</strong>：<code>Treeview</code> 显示多列数据，带滚动条</li><li><strong>多线程</strong>：搜索放在后台线程，用 <code>root.after()</code> 更新界面，避免卡死</li><li><strong>事件绑定</strong>：<code>&lt;Double-1&gt;</code> 双击事件、<code>&lt;Return&gt;</code> 回车事件</li></ul></blockquote><h3 id="6-3-PyQt-快速入门"><a href="#6-3-PyQt-快速入门" class="headerlink" title="6.3 PyQt 快速入门"></a>6.3 PyQt 快速入门</h3><h4 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pip install PyQt6</span><br></pre></td></tr></table></figure><h4 id="基础组件与信号槽机制"><a href="#基础组件与信号槽机制" class="headerlink" title="基础组件与信号槽机制"></a>基础组件与信号槽机制</h4><p>PyQt 的核心概念是<strong>信号（Signal）和槽（Slot）</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><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><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">PyQt6 快速入门</span></span><br><span class="line"><span class="string">核心概念：信号（Signal）与 槽（Slot）</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="keyword">import</span> sys</span><br><span class="line"><span class="keyword">from</span> PyQt6.QtWidgets <span class="keyword">import</span> (</span><br><span class="line">    QApplication, QMainWindow, QWidget,</span><br><span class="line">    QVBoxLayout, QHBoxLayout,</span><br><span class="line">    QLabel, QLineEdit, QPushButton, QTextEdit,</span><br><span class="line">    QComboBox, QSpinBox, QCheckBox,</span><br><span class="line">    QGroupBox, QMessageBox,</span><br><span class="line">)</span><br><span class="line"><span class="keyword">from</span> PyQt6.QtCore <span class="keyword">import</span> Qt</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">QuickStartWindow</span>(<span class="title class_ inherited__">QMainWindow</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;PyQt6 快速入门窗口。&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="built_in">super</span>().__init__()</span><br><span class="line">        <span class="variable language_">self</span>.setWindowTitle(<span class="string">&quot;PyQt6 快速入门&quot;</span>)</span><br><span class="line">        <span class="variable language_">self</span>.setGeometry(<span class="number">100</span>, <span class="number">100</span>, <span class="number">500</span>, <span class="number">400</span>)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 创建中心部件和布局</span></span><br><span class="line">        central = QWidget()</span><br><span class="line">        <span class="variable language_">self</span>.setCentralWidget(central)</span><br><span class="line">        layout = QVBoxLayout(central)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># ===== 1. 输入区域 =====</span></span><br><span class="line">        input_group = QGroupBox(<span class="string">&quot;个人信息&quot;</span>)</span><br><span class="line">        input_layout = QVBoxLayout()</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 文本输入</span></span><br><span class="line">        name_layout = QHBoxLayout()</span><br><span class="line">        name_layout.addWidget(QLabel(<span class="string">&quot;姓名：&quot;</span>))</span><br><span class="line">        <span class="variable language_">self</span>.name_input = QLineEdit()</span><br><span class="line">        <span class="variable language_">self</span>.name_input.setPlaceholderText(<span class="string">&quot;请输入姓名&quot;</span>)</span><br><span class="line">        name_layout.addWidget(<span class="variable language_">self</span>.name_input)</span><br><span class="line">        input_layout.addLayout(name_layout)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 下拉选择</span></span><br><span class="line">        city_layout = QHBoxLayout()</span><br><span class="line">        city_layout.addWidget(QLabel(<span class="string">&quot;城市：&quot;</span>))</span><br><span class="line">        <span class="variable language_">self</span>.city_combo = QComboBox()</span><br><span class="line">        <span class="variable language_">self</span>.city_combo.addItems([<span class="string">&quot;北京&quot;</span>, <span class="string">&quot;上海&quot;</span>, <span class="string">&quot;广州&quot;</span>, <span class="string">&quot;深圳&quot;</span>, <span class="string">&quot;杭州&quot;</span>])</span><br><span class="line">        city_layout.addWidget(<span class="variable language_">self</span>.city_combo)</span><br><span class="line">        input_layout.addLayout(city_layout)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 数字输入</span></span><br><span class="line">        age_layout = QHBoxLayout()</span><br><span class="line">        age_layout.addWidget(QLabel(<span class="string">&quot;年龄：&quot;</span>))</span><br><span class="line">        <span class="variable language_">self</span>.age_spin = QSpinBox()</span><br><span class="line">        <span class="variable language_">self</span>.age_spin.setRange(<span class="number">1</span>, <span class="number">150</span>)</span><br><span class="line">        <span class="variable language_">self</span>.age_spin.setValue(<span class="number">25</span>)</span><br><span class="line">        age_layout.addWidget(<span class="variable language_">self</span>.age_spin)</span><br><span class="line">        input_layout.addLayout(age_layout)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 复选框</span></span><br><span class="line">        <span class="variable language_">self</span>.agree_check = QCheckBox(<span class="string">&quot;我同意用户协议&quot;</span>)</span><br><span class="line">        input_layout.addWidget(<span class="variable language_">self</span>.agree_check)</span><br><span class="line"></span><br><span class="line">        input_group.setLayout(input_layout)</span><br><span class="line">        layout.addWidget(input_group)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># ===== 2. 按钮区域 =====</span></span><br><span class="line">        btn_layout = QHBoxLayout()</span><br><span class="line"></span><br><span class="line">        submit_btn = QPushButton(<span class="string">&quot;提交&quot;</span>)</span><br><span class="line">        clear_btn = QPushButton(<span class="string">&quot;清空&quot;</span>)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 【核心】信号与槽的连接</span></span><br><span class="line">        <span class="comment"># 当按钮被点击（clicked 信号），调用对应的槽函数</span></span><br><span class="line">        submit_btn.clicked.connect(<span class="variable language_">self</span>.on_submit)   <span class="comment"># 点击提交 → 调用 on_submit</span></span><br><span class="line">        clear_btn.clicked.connect(<span class="variable language_">self</span>.on_clear)     <span class="comment"># 点击清空 → 调用 on_clear</span></span><br><span class="line"></span><br><span class="line">        <span class="comment"># 其他常用信号示例</span></span><br><span class="line">        <span class="variable language_">self</span>.name_input.textChanged.connect(<span class="variable language_">self</span>.on_text_changed)  <span class="comment"># 文本变化 → on_text_changed</span></span><br><span class="line">        <span class="variable language_">self</span>.city_combo.currentTextChanged.connect(<span class="variable language_">self</span>.on_city_changed)  <span class="comment"># 下拉变化</span></span><br><span class="line"></span><br><span class="line">        btn_layout.addWidget(submit_btn)</span><br><span class="line">        btn_layout.addWidget(clear_btn)</span><br><span class="line">        layout.addLayout(btn_layout)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># ===== 3. 输出区域 =====</span></span><br><span class="line">        <span class="variable language_">self</span>.output = QTextEdit()</span><br><span class="line">        <span class="variable language_">self</span>.output.setReadOnly(<span class="literal">True</span>)</span><br><span class="line">        <span class="variable language_">self</span>.output.setPlaceholderText(<span class="string">&quot;输出信息将显示在这里...&quot;</span>)</span><br><span class="line">        layout.addWidget(<span class="variable language_">self</span>.output)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># ===== 槽函数（响应信号） =====</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">on_submit</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;提交按钮的槽函数。&quot;&quot;&quot;</span></span><br><span class="line">        name = <span class="variable language_">self</span>.name_input.text().strip()</span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> name:</span><br><span class="line">            QMessageBox.warning(<span class="variable language_">self</span>, <span class="string">&quot;提示&quot;</span>, <span class="string">&quot;请输入姓名！&quot;</span>)</span><br><span class="line">            <span class="keyword">return</span></span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> <span class="variable language_">self</span>.agree_check.isChecked():</span><br><span class="line">            QMessageBox.warning(<span class="variable language_">self</span>, <span class="string">&quot;提示&quot;</span>, <span class="string">&quot;请先同意用户协议！&quot;</span>)</span><br><span class="line">            <span class="keyword">return</span></span><br><span class="line"></span><br><span class="line">        city = <span class="variable language_">self</span>.city_combo.currentText()</span><br><span class="line">        age = <span class="variable language_">self</span>.age_spin.value()</span><br><span class="line"></span><br><span class="line">        result = <span class="string">f&quot;✅ 提交成功！\n姓名：<span class="subst">&#123;name&#125;</span>\n城市：<span class="subst">&#123;city&#125;</span>\n年龄：<span class="subst">&#123;age&#125;</span>&quot;</span></span><br><span class="line">        <span class="variable language_">self</span>.output.append(result)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">on_clear</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;清空按钮的槽函数。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="variable language_">self</span>.name_input.clear()</span><br><span class="line">        <span class="variable language_">self</span>.city_combo.setCurrentIndex(<span class="number">0</span>)</span><br><span class="line">        <span class="variable language_">self</span>.age_spin.setValue(<span class="number">25</span>)</span><br><span class="line">        <span class="variable language_">self</span>.agree_check.setChecked(<span class="literal">False</span>)</span><br><span class="line">        <span class="variable language_">self</span>.output.clear()</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">on_text_changed</span>(<span class="params">self, text: <span class="built_in">str</span></span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;文本变化时的槽函数。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="variable language_">self</span>.output.append(<span class="string">f&quot;📝 文本变化：<span class="subst">&#123;text&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">on_city_changed</span>(<span class="params">self, city: <span class="built_in">str</span></span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;城市选择变化时的槽函数。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="variable language_">self</span>.output.append(<span class="string">f&quot;🏙 城市切换：<span class="subst">&#123;city&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># ===== 启动应用 =====</span></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    app = QApplication(sys.argv)</span><br><span class="line">    window = QuickStartWindow()</span><br><span class="line">    window.show()</span><br><span class="line">    sys.exit(app.<span class="built_in">exec</span>())</span><br></pre></td></tr></table></figure><blockquote><p><strong>Tkinter vs PyQt 对比</strong>：</p><table><thead><tr><th>对比项</th><th>Tkinter</th><th>PyQt</th></tr></thead><tbody><tr><td>安装</td><td>自带</td><td>需 pip install</td></tr><tr><td>组件丰富度</td><td>基础</td><td>非常丰富（表格、图表、富文本）</td></tr><tr><td>信号槽机制</td><td><code>command=</code> 回调</td><td><code>signal.connect(slot)</code> 更灵活</td></tr><tr><td>界面美观度</td><td>一般</td><td>更现代</td></tr><tr><td>打包体积</td><td>小</td><td>较大</td></tr><tr><td>学习曲线</td><td>低</td><td>中等</td></tr></tbody></table></blockquote><h3 id="6-4-常见新手坑"><a href="#6-4-常见新手坑" class="headerlink" title="6.4 常见新手坑"></a>6.4 常见新手坑</h3><table><thead><tr><th>坑</th><th>说明</th><th>解决方案</th></tr></thead><tbody><tr><td><strong>界面卡死</strong></td><td>在主线程做耗时操作（网络请求、大量计算）</td><td>用 <code>threading</code> 做后台任务，<code>root.after()</code> 更新界面</td></tr><tr><td><strong>Tkinter 中文乱码</strong></td><td>Windows 上部分中文字体显示异常</td><td>设置字体：<code>font=(&quot;微软雅黑&quot;, 10)</code></td></tr><tr><td><strong>PyQt 打包太大</strong></td><td>PyInstaller 打包 PyQt 应用超过 100MB</td><td>用 <code>--exclude-module</code> 排除不需要的模块，或用 Nuitka</td></tr><tr><td><strong>忘记 <code>mainloop()</code></strong></td><td>窗口一闪而过</td><td>必须在最后调用 <code>root.mainloop()</code> 进入事件循环</td></tr><tr><td><strong>布局混乱</strong></td><td><code>pack</code> 和 <code>grid</code> 混用在同一个容器</td><td>同一容器只用一种布局方式</td></tr><tr><td><strong>Tkinter 无表格</strong></td><td>想显示多列数据没有现成组件</td><td>用 <code>ttk.Treeview</code> 实现表格</td></tr><tr><td><strong>PyQt 信号槽断开</strong></td><td>连接后信号没有触发槽函数</td><td>检查信号名称拼写，确认 <code>connect()</code> 在 <code>show()</code> 之前</td></tr></tbody></table><hr><h2 id="7-游戏开发"><a href="#7-游戏开发" class="headerlink" title="7. 游戏开发"></a>7. 游戏开发</h2><h3 id="7-1-Python-游戏开发适合什么？"><a href="#7-1-Python-游戏开发适合什么？" class="headerlink" title="7.1 Python 游戏开发适合什么？"></a>7.1 Python 游戏开发适合什么？</h3><p>Python 不是做 3A 大作的语言（那是 C++&#x2F;C# 的领域），但 Python 在以下场景非常合适：</p><table><thead><tr><th>适合做什么</th><th>不适合做什么</th></tr></thead><tbody><tr><td>2D 休闲游戏（弹球、贪吃蛇、俄罗斯方块）</td><td>3A 3D 大作</td></tr><tr><td>游戏原型验证（快速试玩）</td><td>高性能实时渲染</td></tr><tr><td>游戏工具开发（地图编辑器、关卡生成器）</td><td>大型网游服务端</td></tr><tr><td>AI 游戏对战（强化学习训练）</td><td>手机游戏（除非用 Kivy）</td></tr><tr><td>教学和学习游戏开发原理</td><td>商业级游戏引擎</td></tr></tbody></table><p><strong>工具选择</strong>：</p><figure class="highlight plaintext"><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><span class="line">Python 游戏开发工具链：</span><br><span class="line">├── Pygame    ← 新手首选，2D 游戏学习，社区资源最多</span><br><span class="line">├── Arcade    ← Pygame 的现代替代，API 更友好</span><br><span class="line">├── Panda3D   ← 迪士尼开发的 3D 引擎，适合学 3D</span><br><span class="line">├── Godot     ← 用 GDScript（类 Python）开发，真正的游戏引擎</span><br><span class="line">└── Ren&#x27;Py    ← 视觉小说/文字冒险游戏专用</span><br></pre></td></tr></table></figure><blockquote><p><strong>新手建议</strong>：从 Pygame 开始，做出第一个完整游戏比选工具更重要。</p></blockquote><h3 id="7-2-Pygame-完整项目：弹球游戏"><a href="#7-2-Pygame-完整项目：弹球游戏" class="headerlink" title="7.2 Pygame 完整项目：弹球游戏"></a>7.2 Pygame 完整项目：弹球游戏</h3><p>包含碰撞检测、计分、游戏循环、暂停&#x2F;重开等完整功能。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pip install pygame</span><br></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><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><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br><span class="line">297</span><br><span class="line">298</span><br><span class="line">299</span><br><span class="line">300</span><br><span class="line">301</span><br><span class="line">302</span><br><span class="line">303</span><br><span class="line">304</span><br><span class="line">305</span><br><span class="line">306</span><br><span class="line">307</span><br><span class="line">308</span><br><span class="line">309</span><br><span class="line">310</span><br><span class="line">311</span><br><span class="line">312</span><br><span class="line">313</span><br><span class="line">314</span><br><span class="line">315</span><br><span class="line">316</span><br><span class="line">317</span><br><span class="line">318</span><br><span class="line">319</span><br><span class="line">320</span><br><span class="line">321</span><br><span class="line">322</span><br><span class="line">323</span><br><span class="line">324</span><br><span class="line">325</span><br><span class="line">326</span><br><span class="line">327</span><br><span class="line">328</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">弹球游戏 - Pygame 完整项目</span></span><br><span class="line"><span class="string">功能：挡板接球、碰撞反弹、计分、暂停、重新开始</span></span><br><span class="line"><span class="string">操作：← → 移动挡板，空格暂停，R 重新开始</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="keyword">import</span> pygame</span><br><span class="line"><span class="keyword">import</span> sys</span><br><span class="line"><span class="keyword">import</span> random</span><br><span class="line"><span class="keyword">import</span> math</span><br><span class="line"></span><br><span class="line"><span class="comment"># ===== 初始化 =====</span></span><br><span class="line">pygame.init()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 常量定义</span></span><br><span class="line">SCREEN_WIDTH = <span class="number">800</span></span><br><span class="line">SCREEN_HEIGHT = <span class="number">600</span></span><br><span class="line">FPS = <span class="number">60</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 颜色定义（R, G, B）</span></span><br><span class="line">COLOR_BG = (<span class="number">15</span>, <span class="number">15</span>, <span class="number">35</span>)         <span class="comment"># 深蓝黑背景</span></span><br><span class="line">COLOR_WHITE = (<span class="number">255</span>, <span class="number">255</span>, <span class="number">255</span>)</span><br><span class="line">COLOR_BALL = (<span class="number">255</span>, <span class="number">100</span>, <span class="number">80</span>)     <span class="comment"># 橙红色球</span></span><br><span class="line">COLOR_PADDLE = (<span class="number">80</span>, <span class="number">200</span>, <span class="number">255</span>)   <span class="comment"># 蓝色挡板</span></span><br><span class="line">COLOR_BRICK = []                 <span class="comment"># 砖块颜色（稍后填充）</span></span><br><span class="line">COLOR_SCORE = (<span class="number">255</span>, <span class="number">220</span>, <span class="number">80</span>)    <span class="comment"># 黄色分数</span></span><br><span class="line">COLOR_TEXT = (<span class="number">200</span>, <span class="number">200</span>, <span class="number">200</span>)    <span class="comment"># 灰色文字</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 砖块颜色方案（每行一种颜色）</span></span><br><span class="line">BRICK_COLORS = [</span><br><span class="line">    (<span class="number">255</span>, <span class="number">80</span>, <span class="number">80</span>),    <span class="comment"># 红</span></span><br><span class="line">    (<span class="number">255</span>, <span class="number">160</span>, <span class="number">60</span>),   <span class="comment"># 橙</span></span><br><span class="line">    (<span class="number">255</span>, <span class="number">220</span>, <span class="number">80</span>),   <span class="comment"># 黄</span></span><br><span class="line">    (<span class="number">80</span>, <span class="number">220</span>, <span class="number">100</span>),   <span class="comment"># 绿</span></span><br><span class="line">    (<span class="number">80</span>, <span class="number">160</span>, <span class="number">255</span>),   <span class="comment"># 蓝</span></span><br><span class="line">]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 砖块参数</span></span><br><span class="line">BRICK_ROWS = <span class="number">5</span>          <span class="comment"># 行数</span></span><br><span class="line">BRICK_COLS = <span class="number">10</span>         <span class="comment"># 列数</span></span><br><span class="line">BRICK_WIDTH = <span class="number">70</span>        <span class="comment"># 砖块宽度</span></span><br><span class="line">BRICK_HEIGHT = <span class="number">25</span>       <span class="comment"># 砖块高度</span></span><br><span class="line">BRICK_PADDING = <span class="number">5</span>       <span class="comment"># 砖块间距</span></span><br><span class="line">BRICK_TOP = <span class="number">60</span>          <span class="comment"># 砖块区域顶部偏移</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Ball</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;球。&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="variable language_">self</span>.radius = <span class="number">8</span></span><br><span class="line">        <span class="variable language_">self</span>.reset()</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">reset</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;重置球的位置和速度。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="variable language_">self</span>.x = SCREEN_WIDTH / <span class="number">2</span></span><br><span class="line">        <span class="variable language_">self</span>.y = SCREEN_HEIGHT - <span class="number">80</span></span><br><span class="line">        <span class="comment"># 随机向上发射，速度固定</span></span><br><span class="line">        angle = random.uniform(math.radians(<span class="number">220</span>), math.radians(<span class="number">320</span>))</span><br><span class="line">        speed = <span class="number">5</span></span><br><span class="line">        <span class="variable language_">self</span>.vx = speed * math.cos(angle)</span><br><span class="line">        <span class="variable language_">self</span>.vy = speed * math.sin(angle)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">update</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;更新球的位置。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="variable language_">self</span>.x += <span class="variable language_">self</span>.vx</span><br><span class="line">        <span class="variable language_">self</span>.y += <span class="variable language_">self</span>.vy</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 左右墙壁碰撞</span></span><br><span class="line">        <span class="keyword">if</span> <span class="variable language_">self</span>.x - <span class="variable language_">self</span>.radius &lt;= <span class="number">0</span>:</span><br><span class="line">            <span class="variable language_">self</span>.x = <span class="variable language_">self</span>.radius</span><br><span class="line">            <span class="variable language_">self</span>.vx = <span class="built_in">abs</span>(<span class="variable language_">self</span>.vx)  <span class="comment"># 向右反弹</span></span><br><span class="line">        <span class="keyword">elif</span> <span class="variable language_">self</span>.x + <span class="variable language_">self</span>.radius &gt;= SCREEN_WIDTH:</span><br><span class="line">            <span class="variable language_">self</span>.x = SCREEN_WIDTH - <span class="variable language_">self</span>.radius</span><br><span class="line">            <span class="variable language_">self</span>.vx = -<span class="built_in">abs</span>(<span class="variable language_">self</span>.vx)  <span class="comment"># 向左反弹</span></span><br><span class="line"></span><br><span class="line">        <span class="comment"># 顶部墙壁碰撞</span></span><br><span class="line">        <span class="keyword">if</span> <span class="variable language_">self</span>.y - <span class="variable language_">self</span>.radius &lt;= <span class="number">0</span>:</span><br><span class="line">            <span class="variable language_">self</span>.y = <span class="variable language_">self</span>.radius</span><br><span class="line">            <span class="variable language_">self</span>.vy = <span class="built_in">abs</span>(<span class="variable language_">self</span>.vy)  <span class="comment"># 向下反弹</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">draw</span>(<span class="params">self, surface: pygame.Surface</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;绘制球。&quot;&quot;&quot;</span></span><br><span class="line">        pygame.draw.circle(surface, COLOR_BALL, (<span class="built_in">int</span>(<span class="variable language_">self</span>.x), <span class="built_in">int</span>(<span class="variable language_">self</span>.y)), <span class="variable language_">self</span>.radius)</span><br><span class="line">        <span class="comment"># 高光效果</span></span><br><span class="line">        highlight_pos = (<span class="built_in">int</span>(<span class="variable language_">self</span>.x - <span class="number">2</span>), <span class="built_in">int</span>(<span class="variable language_">self</span>.y - <span class="number">2</span>))</span><br><span class="line">        pygame.draw.circle(surface, (<span class="number">255</span>, <span class="number">200</span>, <span class="number">180</span>), highlight_pos, <span class="number">3</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Paddle</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;挡板。&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="variable language_">self</span>.width = <span class="number">120</span></span><br><span class="line">        <span class="variable language_">self</span>.height = <span class="number">14</span></span><br><span class="line">        <span class="variable language_">self</span>.x = SCREEN_WIDTH / <span class="number">2</span> - <span class="variable language_">self</span>.width / <span class="number">2</span></span><br><span class="line">        <span class="variable language_">self</span>.y = SCREEN_HEIGHT - <span class="number">40</span></span><br><span class="line">        <span class="variable language_">self</span>.speed = <span class="number">8</span>  <span class="comment"># 移动速度</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">move_left</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;向左移动。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="variable language_">self</span>.x = <span class="built_in">max</span>(<span class="number">0</span>, <span class="variable language_">self</span>.x - <span class="variable language_">self</span>.speed)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">move_right</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;向右移动。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="variable language_">self</span>.x = <span class="built_in">min</span>(SCREEN_WIDTH - <span class="variable language_">self</span>.width, <span class="variable language_">self</span>.x + <span class="variable language_">self</span>.speed)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">draw</span>(<span class="params">self, surface: pygame.Surface</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;绘制挡板。&quot;&quot;&quot;</span></span><br><span class="line">        rect = pygame.Rect(<span class="built_in">int</span>(<span class="variable language_">self</span>.x), <span class="built_in">int</span>(<span class="variable language_">self</span>.y), <span class="variable language_">self</span>.width, <span class="variable language_">self</span>.height)</span><br><span class="line">        pygame.draw.rect(surface, COLOR_PADDLE, rect, border_radius=<span class="number">7</span>)</span><br><span class="line">        <span class="comment"># 高光条</span></span><br><span class="line">        highlight = pygame.Rect(<span class="built_in">int</span>(<span class="variable language_">self</span>.x + <span class="number">4</span>), <span class="built_in">int</span>(<span class="variable language_">self</span>.y + <span class="number">2</span>), <span class="variable language_">self</span>.width - <span class="number">8</span>, <span class="number">4</span>)</span><br><span class="line">        pygame.draw.rect(surface, (<span class="number">150</span>, <span class="number">230</span>, <span class="number">255</span>), highlight, border_radius=<span class="number">2</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_rect</span>(<span class="params">self</span>) -&gt; pygame.Rect:</span><br><span class="line">        <span class="string">&quot;&quot;&quot;获取挡板的碰撞矩形。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">return</span> pygame.Rect(<span class="built_in">int</span>(<span class="variable language_">self</span>.x), <span class="built_in">int</span>(<span class="variable language_">self</span>.y), <span class="variable language_">self</span>.width, <span class="variable language_">self</span>.height)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Brick</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;砖块。&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, x: <span class="built_in">int</span>, y: <span class="built_in">int</span>, color: <span class="built_in">tuple</span></span>):</span><br><span class="line">        <span class="variable language_">self</span>.rect = pygame.Rect(x, y, BRICK_WIDTH, BRICK_HEIGHT)</span><br><span class="line">        <span class="variable language_">self</span>.color = color</span><br><span class="line">        <span class="variable language_">self</span>.alive = <span class="literal">True</span>  <span class="comment"># 是否还在</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">draw</span>(<span class="params">self, surface: pygame.Surface</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;绘制砖块。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">if</span> <span class="variable language_">self</span>.alive:</span><br><span class="line">            pygame.draw.rect(surface, <span class="variable language_">self</span>.color, <span class="variable language_">self</span>.rect, border_radius=<span class="number">3</span>)</span><br><span class="line">            <span class="comment"># 高光效果</span></span><br><span class="line">            highlight = pygame.Rect(</span><br><span class="line">                <span class="variable language_">self</span>.rect.x + <span class="number">3</span>, <span class="variable language_">self</span>.rect.y + <span class="number">2</span>,</span><br><span class="line">                <span class="variable language_">self</span>.rect.width - <span class="number">6</span>, <span class="number">6</span></span><br><span class="line">            )</span><br><span class="line">            lighter = <span class="built_in">tuple</span>(<span class="built_in">min</span>(<span class="number">255</span>, c + <span class="number">60</span>) <span class="keyword">for</span> c <span class="keyword">in</span> <span class="variable language_">self</span>.color)</span><br><span class="line">            pygame.draw.rect(surface, lighter, highlight, border_radius=<span class="number">2</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Game</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;弹球游戏主类。&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="variable language_">self</span>.screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))</span><br><span class="line">        pygame.display.set_caption(<span class="string">&quot;弹球游戏 - Pygame&quot;</span>)</span><br><span class="line">        <span class="variable language_">self</span>.clock = pygame.time.Clock()</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 字体（用系统默认字体）</span></span><br><span class="line">        <span class="variable language_">self</span>.font_large = pygame.font.SysFont(<span class="string">&quot;microsoftyahei&quot;</span>, <span class="number">36</span>)</span><br><span class="line">        <span class="variable language_">self</span>.font_medium = pygame.font.SysFont(<span class="string">&quot;microsoftyahei&quot;</span>, <span class="number">24</span>)</span><br><span class="line">        <span class="variable language_">self</span>.font_small = pygame.font.SysFont(<span class="string">&quot;microsoftyahei&quot;</span>, <span class="number">18</span>)</span><br><span class="line"></span><br><span class="line">        <span class="variable language_">self</span>.reset_game()</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">reset_game</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;重置游戏状态。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="variable language_">self</span>.ball = Ball()</span><br><span class="line">        <span class="variable language_">self</span>.paddle = Paddle()</span><br><span class="line">        <span class="variable language_">self</span>.bricks = <span class="variable language_">self</span>._create_bricks()</span><br><span class="line">        <span class="variable language_">self</span>.score = <span class="number">0</span></span><br><span class="line">        <span class="variable language_">self</span>.lives = <span class="number">3</span>          <span class="comment"># 剩余生命数</span></span><br><span class="line">        <span class="variable language_">self</span>.paused = <span class="literal">False</span>     <span class="comment"># 是否暂停</span></span><br><span class="line">        <span class="variable language_">self</span>.game_over = <span class="literal">False</span>  <span class="comment"># 是否结束</span></span><br><span class="line">        <span class="variable language_">self</span>.won = <span class="literal">False</span>        <span class="comment"># 是否胜利</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">_create_bricks</span>(<span class="params">self</span>) -&gt; <span class="built_in">list</span>[Brick]:</span><br><span class="line">        <span class="string">&quot;&quot;&quot;创建砖块阵列。&quot;&quot;&quot;</span></span><br><span class="line">        bricks = []</span><br><span class="line">        <span class="comment"># 计算砖块区域的起始位置（使其居中）</span></span><br><span class="line">        total_width = BRICK_COLS * (BRICK_WIDTH + BRICK_PADDING) - BRICK_PADDING</span><br><span class="line">        start_x = (SCREEN_WIDTH - total_width) / <span class="number">2</span></span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span> row <span class="keyword">in</span> <span class="built_in">range</span>(BRICK_ROWS):</span><br><span class="line">            color = BRICK_COLORS[row % <span class="built_in">len</span>(BRICK_COLORS)]</span><br><span class="line">            <span class="keyword">for</span> col <span class="keyword">in</span> <span class="built_in">range</span>(BRICK_COLS):</span><br><span class="line">                x = start_x + col * (BRICK_WIDTH + BRICK_PADDING)</span><br><span class="line">                y = BRICK_TOP + row * (BRICK_HEIGHT + BRICK_PADDING)</span><br><span class="line">                bricks.append(Brick(x, y, color))</span><br><span class="line">        <span class="keyword">return</span> bricks</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">handle_input</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;处理键盘输入。&quot;&quot;&quot;</span></span><br><span class="line">        keys = pygame.key.get_pressed()</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> <span class="variable language_">self</span>.paused <span class="keyword">and</span> <span class="keyword">not</span> <span class="variable language_">self</span>.game_over:</span><br><span class="line">            <span class="keyword">if</span> keys[pygame.K_LEFT] <span class="keyword">or</span> keys[pygame.K_a]:</span><br><span class="line">                <span class="variable language_">self</span>.paddle.move_left()</span><br><span class="line">            <span class="keyword">if</span> keys[pygame.K_RIGHT] <span class="keyword">or</span> keys[pygame.K_d]:</span><br><span class="line">                <span class="variable language_">self</span>.paddle.move_right()</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">check_collisions</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;碰撞检测。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="comment"># 1. 球与挡板碰撞</span></span><br><span class="line">        paddle_rect = <span class="variable language_">self</span>.paddle.get_rect()</span><br><span class="line">        ball_rect = pygame.Rect(</span><br><span class="line">            <span class="variable language_">self</span>.ball.x - <span class="variable language_">self</span>.ball.radius,</span><br><span class="line">            <span class="variable language_">self</span>.ball.y - <span class="variable language_">self</span>.ball.radius,</span><br><span class="line">            <span class="variable language_">self</span>.ball.radius * <span class="number">2</span>,</span><br><span class="line">            <span class="variable language_">self</span>.ball.radius * <span class="number">2</span>,</span><br><span class="line">        )</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> ball_rect.colliderect(paddle_rect) <span class="keyword">and</span> <span class="variable language_">self</span>.ball.vy &gt; <span class="number">0</span>:</span><br><span class="line">            <span class="comment"># 根据球击中挡板的位置，改变水平反弹角度</span></span><br><span class="line">            <span class="comment"># 击中挡板左侧 → 向左反弹，右侧 → 向右反弹</span></span><br><span class="line">            relative_x = (<span class="variable language_">self</span>.ball.x - <span class="variable language_">self</span>.paddle.x) / <span class="variable language_">self</span>.paddle.width  <span class="comment"># 0~1</span></span><br><span class="line">            bounce_angle = (relative_x - <span class="number">0.5</span>) * <span class="number">2.5</span>  <span class="comment"># -1.25 ~ 1.25</span></span><br><span class="line"></span><br><span class="line">            speed = math.sqrt(<span class="variable language_">self</span>.ball.vx ** <span class="number">2</span> + <span class="variable language_">self</span>.ball.vy ** <span class="number">2</span>)</span><br><span class="line">            <span class="comment"># 确保速度不低于最小值</span></span><br><span class="line">            speed = <span class="built_in">max</span>(speed, <span class="number">5</span>)</span><br><span class="line">            <span class="variable language_">self</span>.ball.vx = bounce_angle * speed</span><br><span class="line">            <span class="variable language_">self</span>.ball.vy = -<span class="built_in">abs</span>(<span class="variable language_">self</span>.ball.vy)  <span class="comment"># 保证向上反弹</span></span><br><span class="line"></span><br><span class="line">            <span class="comment"># 把球放到挡板上方，防止穿透</span></span><br><span class="line">            <span class="variable language_">self</span>.ball.y = <span class="variable language_">self</span>.paddle.y - <span class="variable language_">self</span>.ball.radius</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 2. 球与砖块碰撞</span></span><br><span class="line">        <span class="keyword">for</span> brick <span class="keyword">in</span> <span class="variable language_">self</span>.bricks:</span><br><span class="line">            <span class="keyword">if</span> <span class="keyword">not</span> brick.alive:</span><br><span class="line">                <span class="keyword">continue</span></span><br><span class="line">            <span class="keyword">if</span> ball_rect.colliderect(brick.rect):</span><br><span class="line">                brick.alive = <span class="literal">False</span></span><br><span class="line">                <span class="variable language_">self</span>.score += <span class="number">10</span>  <span class="comment"># 每击碎一块砖块加 10 分</span></span><br><span class="line"></span><br><span class="line">                <span class="comment"># 判断碰撞方向：从哪个面撞入</span></span><br><span class="line">                <span class="comment"># 简化处理：反转 vy</span></span><br><span class="line">                <span class="variable language_">self</span>.ball.vy = -<span class="variable language_">self</span>.ball.vy</span><br><span class="line">                <span class="keyword">break</span>  <span class="comment"># 一帧只处理一次砖块碰撞</span></span><br><span class="line"></span><br><span class="line">        <span class="comment"># 3. 球掉出底部 → 扣命</span></span><br><span class="line">        <span class="keyword">if</span> <span class="variable language_">self</span>.ball.y - <span class="variable language_">self</span>.ball.radius &gt; SCREEN_HEIGHT:</span><br><span class="line">            <span class="variable language_">self</span>.lives -= <span class="number">1</span></span><br><span class="line">            <span class="keyword">if</span> <span class="variable language_">self</span>.lives &lt;= <span class="number">0</span>:</span><br><span class="line">                <span class="variable language_">self</span>.game_over = <span class="literal">True</span></span><br><span class="line">            <span class="keyword">else</span>:</span><br><span class="line">                <span class="variable language_">self</span>.ball.reset()  <span class="comment"># 重新发球</span></span><br><span class="line"></span><br><span class="line">        <span class="comment"># 4. 所有砖块消除 → 胜利</span></span><br><span class="line">        <span class="keyword">if</span> <span class="built_in">all</span>(<span class="keyword">not</span> brick.alive <span class="keyword">for</span> brick <span class="keyword">in</span> <span class="variable language_">self</span>.bricks):</span><br><span class="line">            <span class="variable language_">self</span>.won = <span class="literal">True</span></span><br><span class="line">            <span class="variable language_">self</span>.game_over = <span class="literal">True</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">update</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;更新游戏逻辑。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">if</span> <span class="variable language_">self</span>.paused <span class="keyword">or</span> <span class="variable language_">self</span>.game_over:</span><br><span class="line">            <span class="keyword">return</span></span><br><span class="line">        <span class="variable language_">self</span>.ball.update()</span><br><span class="line">        <span class="variable language_">self</span>.check_collisions()</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">draw</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;绘制所有游戏元素。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="comment"># 背景</span></span><br><span class="line">        <span class="variable language_">self</span>.screen.fill(COLOR_BG)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 砖块</span></span><br><span class="line">        <span class="keyword">for</span> brick <span class="keyword">in</span> <span class="variable language_">self</span>.bricks:</span><br><span class="line">            brick.draw(<span class="variable language_">self</span>.screen)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 球和挡板</span></span><br><span class="line">        <span class="variable language_">self</span>.ball.draw(<span class="variable language_">self</span>.screen)</span><br><span class="line">        <span class="variable language_">self</span>.paddle.draw(<span class="variable language_">self</span>.screen)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 分数</span></span><br><span class="line">        score_text = <span class="variable language_">self</span>.font_medium.render(<span class="string">f&quot;分数: <span class="subst">&#123;self.score&#125;</span>&quot;</span>, <span class="literal">True</span>, COLOR_SCORE)</span><br><span class="line">        <span class="variable language_">self</span>.screen.blit(score_text, (<span class="number">15</span>, <span class="number">15</span>))</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 生命值（用圆圈表示）</span></span><br><span class="line">        <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="variable language_">self</span>.lives):</span><br><span class="line">            cx = SCREEN_WIDTH - <span class="number">25</span> - i * <span class="number">28</span></span><br><span class="line">            pygame.draw.circle(<span class="variable language_">self</span>.screen, COLOR_BALL, (cx, <span class="number">25</span>), <span class="number">10</span>)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 暂停提示</span></span><br><span class="line">        <span class="keyword">if</span> <span class="variable language_">self</span>.paused <span class="keyword">and</span> <span class="keyword">not</span> <span class="variable language_">self</span>.game_over:</span><br><span class="line">            overlay = pygame.Surface((SCREEN_WIDTH, SCREEN_HEIGHT), pygame.SRCALPHA)</span><br><span class="line">            overlay.fill((<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">120</span>))  <span class="comment"># 半透明遮罩</span></span><br><span class="line">            <span class="variable language_">self</span>.screen.blit(overlay, (<span class="number">0</span>, <span class="number">0</span>))</span><br><span class="line">            pause_text = <span class="variable language_">self</span>.font_large.render(<span class="string">&quot;暂停中&quot;</span>, <span class="literal">True</span>, COLOR_WHITE)</span><br><span class="line">            tip_text = <span class="variable language_">self</span>.font_small.render(<span class="string">&quot;按空格继续&quot;</span>, <span class="literal">True</span>, COLOR_TEXT)</span><br><span class="line">            <span class="variable language_">self</span>.screen.blit(pause_text, (SCREEN_WIDTH // <span class="number">2</span> - pause_text.get_width() // <span class="number">2</span>, <span class="number">250</span>))</span><br><span class="line">            <span class="variable language_">self</span>.screen.blit(tip_text, (SCREEN_WIDTH // <span class="number">2</span> - tip_text.get_width() // <span class="number">2</span>, <span class="number">300</span>))</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 游戏结束/胜利</span></span><br><span class="line">        <span class="keyword">if</span> <span class="variable language_">self</span>.game_over:</span><br><span class="line">            overlay = pygame.Surface((SCREEN_WIDTH, SCREEN_HEIGHT), pygame.SRCALPHA)</span><br><span class="line">            overlay.fill((<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">150</span>))</span><br><span class="line">            <span class="variable language_">self</span>.screen.blit(overlay, (<span class="number">0</span>, <span class="number">0</span>))</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> <span class="variable language_">self</span>.won:</span><br><span class="line">                msg = <span class="variable language_">self</span>.font_large.render(<span class="string">&quot;恭喜通关！&quot;</span>, <span class="literal">True</span>, COLOR_SCORE)</span><br><span class="line">            <span class="keyword">else</span>:</span><br><span class="line">                msg = <span class="variable language_">self</span>.font_large.render(<span class="string">&quot;游戏结束&quot;</span>, <span class="literal">True</span>, (<span class="number">255</span>, <span class="number">80</span>, <span class="number">80</span>))</span><br><span class="line">            score_msg = <span class="variable language_">self</span>.font_medium.render(<span class="string">f&quot;最终分数: <span class="subst">&#123;self.score&#125;</span>&quot;</span>, <span class="literal">True</span>, COLOR_WHITE)</span><br><span class="line">            tip_msg = <span class="variable language_">self</span>.font_small.render(<span class="string">&quot;按 R 重新开始&quot;</span>, <span class="literal">True</span>, COLOR_TEXT)</span><br><span class="line"></span><br><span class="line">            <span class="variable language_">self</span>.screen.blit(msg, (SCREEN_WIDTH // <span class="number">2</span> - msg.get_width() // <span class="number">2</span>, <span class="number">220</span>))</span><br><span class="line">            <span class="variable language_">self</span>.screen.blit(score_msg, (SCREEN_WIDTH // <span class="number">2</span> - score_msg.get_width() // <span class="number">2</span>, <span class="number">280</span>))</span><br><span class="line">            <span class="variable language_">self</span>.screen.blit(tip_msg, (SCREEN_WIDTH // <span class="number">2</span> - tip_msg.get_width() // <span class="number">2</span>, <span class="number">330</span>))</span><br><span class="line"></span><br><span class="line">        pygame.display.flip()</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">run</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;游戏主循环。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">            <span class="comment"># 事件处理</span></span><br><span class="line">            <span class="keyword">for</span> event <span class="keyword">in</span> pygame.event.get():</span><br><span class="line">                <span class="keyword">if</span> event.<span class="built_in">type</span> == pygame.QUIT:</span><br><span class="line">                    pygame.quit()</span><br><span class="line">                    sys.exit()</span><br><span class="line">                <span class="keyword">elif</span> event.<span class="built_in">type</span> == pygame.KEYDOWN:</span><br><span class="line">                    <span class="keyword">if</span> event.key == pygame.K_SPACE <span class="keyword">and</span> <span class="keyword">not</span> <span class="variable language_">self</span>.game_over:</span><br><span class="line">                        <span class="variable language_">self</span>.paused = <span class="keyword">not</span> <span class="variable language_">self</span>.paused  <span class="comment"># 空格暂停/继续</span></span><br><span class="line">                    <span class="keyword">elif</span> event.key == pygame.K_r <span class="keyword">and</span> <span class="variable language_">self</span>.game_over:</span><br><span class="line">                        <span class="variable language_">self</span>.reset_game()  <span class="comment"># R 重新开始</span></span><br><span class="line">                    <span class="keyword">elif</span> event.key == pygame.K_ESCAPE:</span><br><span class="line">                        pygame.quit()</span><br><span class="line">                        sys.exit()</span><br><span class="line"></span><br><span class="line">            <span class="variable language_">self</span>.handle_input()</span><br><span class="line">            <span class="variable language_">self</span>.update()</span><br><span class="line">            <span class="variable language_">self</span>.draw()</span><br><span class="line">            <span class="variable language_">self</span>.clock.tick(FPS)  <span class="comment"># 限制帧率为 60 FPS</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># ===== 启动游戏 =====</span></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    game = Game()</span><br><span class="line">    game.run()</span><br></pre></td></tr></table></figure><blockquote><p><strong>这个项目学到了什么</strong>：</p><ul><li><strong>游戏循环</strong>：事件处理 → 逻辑更新 → 画面绘制 → 帧率控制，这是所有游戏的基本结构</li><li><strong>碰撞检测</strong>：<code>Rect.colliderect()</code> 矩形碰撞判断</li><li><strong>状态管理</strong>：暂停、游戏结束、胜利等状态的控制</li><li><strong>键盘输入</strong>：<code>pygame.key.get_pressed()</code> 持续按键检测</li><li><strong>面向对象</strong>：Ball、Paddle、Brick 各自管理自己的数据和绘制</li></ul></blockquote><h3 id="7-3-常见新手坑"><a href="#7-3-常见新手坑" class="headerlink" title="7.3 常见新手坑"></a>7.3 常见新手坑</h3><table><thead><tr><th>坑</th><th>说明</th><th>解决方案</th></tr></thead><tbody><tr><td><strong>帧率不稳定</strong></td><td>没有调用 <code>clock.tick(FPS)</code></td><td>在主循环末尾加 <code>clock.tick(60)</code> 锁定帧率</td></tr><tr><td><strong>球穿透挡板</strong></td><td>球速度太快，一帧移动距离超过挡板厚度</td><td>碰撞后将球放到挡板上方，或限制球速度</td></tr><tr><td><strong>忘记 <code>pygame.display.flip()</code></strong></td><td>画面不更新</td><td>每帧绘制完后必须调用</td></tr><tr><td><strong>图片加载失败</strong></td><td><code>pygame.image.load()</code> 报错</td><td>用 <code>os.path.join()</code> 拼路径，检查文件是否存在</td></tr><tr><td><strong>中文显示乱码</strong></td><td><code>pygame.font</code> 默认不支持中文</td><td>用 <code>pygame.font.SysFont(&quot;microsoftyahei&quot;, 24)</code> 指定中文字体</td></tr><tr><td><strong>游戏无法退出</strong></td><td>关闭窗口后程序卡住</td><td>在事件循环中处理 <code>QUIT</code> 事件，调用 <code>pygame.quit()</code></td></tr><tr><td><strong>碰撞检测不精确</strong></td><td>矩形碰撞对圆形球不精确</td><td>简单游戏用矩形即可，精确碰撞用距离公式 <code>math.hypot()</code></td></tr></tbody></table><hr><h2 id="8-物联网与嵌入式"><a href="#8-物联网与嵌入式" class="headerlink" title="8. 物联网与嵌入式"></a>8. 物联网与嵌入式</h2><h3 id="8-1-IoT-开发是什么？硬件选购指南"><a href="#8-1-IoT-开发是什么？硬件选购指南" class="headerlink" title="8.1 IoT 开发是什么？硬件选购指南"></a>8.1 IoT 开发是什么？硬件选购指南</h3><p>物联网（IoT，Internet of Things）就是让日常设备连上网——温度传感器把数据传到云端、手机远程控制灯泡、智能门锁自动开锁，都是 IoT。</p><p><strong>Python 在 IoT 中的角色</strong>：</p><figure class="highlight plaintext"><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><span class="line">Python 在 IoT 中的应用：</span><br><span class="line">├── MicroPython  ← 运行在微控制器上（ESP32），直接控制硬件</span><br><span class="line">├── 树莓派       ← 运行完整 Linux + Python，适合复杂项目</span><br><span class="line">├── 数据后端     ← 用 FastAPI/Flask 接收传感器数据</span><br><span class="line">└── 数据分析     ← 用 Pandas/NumPy 分析 IoT 数据</span><br></pre></td></tr></table></figure><p><strong>硬件选购指南</strong>：</p><table><thead><tr><th>硬件</th><th>价格</th><th>适合谁</th><th>说明</th></tr></thead><tbody><tr><td><strong>ESP32</strong></td><td>￥20~40</td><td>入门首选</td><td>带 WiFi+蓝牙，MicroPython 支持，便宜好用</td></tr><tr><td><strong>ESP8266</strong></td><td>￥10~20</td><td>预算极低</td><td>只有 WiFi，比 ESP32 弱，但够用</td></tr><tr><td><strong>树莓派 5</strong></td><td>￥400~600</td><td>进阶项目</td><td>完整 Linux 系统，可接摄像头、屏幕</td></tr><tr><td><strong>Arduino</strong></td><td>￥30~80</td><td>C 语言爱好者</td><td>不运行 Python，但和 Python 可串口通信</td></tr><tr><td><strong>Micro:bit</strong></td><td>￥80~120</td><td>青少年教育</td><td>图形化+Python，适合教学</td></tr></tbody></table><blockquote><p><strong>新手建议</strong>：花 ￥30 买一块 ESP32 开发板 + 一个 DHT11 温湿度传感器，跟着下面的项目做，30 分钟上手。</p></blockquote><p><strong>你需要准备的（ESP32 入门套装）</strong>：</p><figure class="highlight plaintext"><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><span class="line">ESP32 入门必备：</span><br><span class="line">├── ESP32 开发板              ￥25</span><br><span class="line">├── DHT11/DHT22 温湿度传感器   ￥5~15</span><br><span class="line">├── LED 灯 + 220Ω 电阻        ￥1</span><br><span class="line">├── 面包板 + 杜邦线           ￥10</span><br><span class="line">└── USB 数据线                ￥5</span><br><span class="line">                        总计：约 ￥50</span><br></pre></td></tr></table></figure><p><strong>开发环境搭建</strong>：</p><ol><li>安装 <a href="https://thonny.org/">Thonny IDE</a>（专为 MicroPython 设计的编辑器）</li><li>给 ESP32 烧录 MicroPython 固件（Thonny 内置一键烧录）</li><li>USB 连接 ESP32，在 Thonny 中选择”MicroPython (ESP32)”解释器</li><li>开始写代码！</li></ol><h3 id="8-2-MicroPython-入门：ESP32-温湿度监控"><a href="#8-2-MicroPython-入门：ESP32-温湿度监控" class="headerlink" title="8.2 MicroPython 入门：ESP32 温湿度监控"></a>8.2 MicroPython 入门：ESP32 温湿度监控</h3><p>用 ESP32 + DHT11 传感器，每 5 秒采集温湿度数据并通过串口输出。</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><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></pre></td><td class="code"><pre><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">ESP32 温湿度监控 - MicroPython 完整项目</span></span><br><span class="line"><span class="string">硬件连接：</span></span><br><span class="line"><span class="string">  - DHT11 数据引脚 → ESP32 的 GPIO 4</span></span><br><span class="line"><span class="string">  - DHT11 VCC → ESP32 的 3.3V</span></span><br><span class="line"><span class="string">  - DHT11 GND → ESP32 的 GND</span></span><br><span class="line"><span class="string">功能：每 5 秒采集温湿度，LED 闪烁表示正在工作，超温报警</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> machine <span class="keyword">import</span> Pin</span><br><span class="line"><span class="keyword">import</span> dht</span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"></span><br><span class="line"><span class="comment"># ===== 硬件初始化 =====</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># DHT11 传感器，接 GPIO4</span></span><br><span class="line">dht_sensor = dht.DHT11(Pin(<span class="number">4</span>))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 板载 LED（ESP32 通常是 GPIO2）</span></span><br><span class="line">led = Pin(<span class="number">2</span>, Pin.OUT)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 蜂鸣器（可选，接 GPIO5），超温报警用</span></span><br><span class="line">buzzer = Pin(<span class="number">5</span>, Pin.OUT)</span><br><span class="line"></span><br><span class="line"><span class="comment"># ===== 报警阈值 =====</span></span><br><span class="line">TEMP_HIGH = <span class="number">35.0</span>   <span class="comment"># 温度超过 35°C 报警</span></span><br><span class="line">HUMIDITY_HIGH = <span class="number">80.0</span>  <span class="comment"># 湿度超过 80% 报警</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">read_sensor</span>():</span><br><span class="line">    <span class="string">&quot;&quot;&quot;读取传感器数据，返回 (温度, 湿度)。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        dht_sensor.measure()  <span class="comment"># 触发测量</span></span><br><span class="line">        temp = dht_sensor.temperature()  <span class="comment"># 温度（°C）</span></span><br><span class="line">        humi = dht_sensor.humidity()     <span class="comment"># 湿度（%）</span></span><br><span class="line">        <span class="keyword">return</span> temp, humi</span><br><span class="line">    <span class="keyword">except</span> OSError <span class="keyword">as</span> e:</span><br><span class="line">        <span class="comment"># DHT 传感器偶尔读取失败是正常的，重试即可</span></span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;传感器读取失败: <span class="subst">&#123;e&#125;</span>&quot;</span>)</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">None</span>, <span class="literal">None</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">check_alert</span>(<span class="params">temp, humi</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;检查是否超阈值，返回报警列表。&quot;&quot;&quot;</span></span><br><span class="line">    alerts = []</span><br><span class="line">    <span class="keyword">if</span> temp <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">None</span> <span class="keyword">and</span> temp &gt; TEMP_HIGH:</span><br><span class="line">        alerts.append(<span class="string">f&quot;高温报警: <span class="subst">&#123;temp&#125;</span>°C &gt; <span class="subst">&#123;TEMP_HIGH&#125;</span>°C&quot;</span>)</span><br><span class="line">    <span class="keyword">if</span> humi <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">None</span> <span class="keyword">and</span> humi &gt; HUMIDITY_HIGH:</span><br><span class="line">        alerts.append(<span class="string">f&quot;高湿报警: <span class="subst">&#123;humi&#125;</span>% &gt; <span class="subst">&#123;HUMIDITY_HIGH&#125;</span>%&quot;</span>)</span><br><span class="line">    <span class="keyword">return</span> alerts</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">blink_led</span>(<span class="params">times=<span class="number">1</span>, interval=<span class="number">0.1</span></span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;LED 闪烁，表示正在工作。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">for</span> _ <span class="keyword">in</span> <span class="built_in">range</span>(times):</span><br><span class="line">        led.value(<span class="number">1</span>)</span><br><span class="line">        time.sleep(interval)</span><br><span class="line">        led.value(<span class="number">0</span>)</span><br><span class="line">        time.sleep(interval)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">alarm_buzzer</span>(<span class="params">times=<span class="number">3</span></span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;蜂鸣器报警。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">for</span> _ <span class="keyword">in</span> <span class="built_in">range</span>(times):</span><br><span class="line">        buzzer.value(<span class="number">1</span>)</span><br><span class="line">        time.sleep(<span class="number">0.2</span>)</span><br><span class="line">        buzzer.value(<span class="number">0</span>)</span><br><span class="line">        time.sleep(<span class="number">0.1</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># ===== 主循环 =====</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;=&quot;</span> * <span class="number">40</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;ESP32 温湿度监控系统启动&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;报警阈值: 温度 &gt; <span class="subst">&#123;TEMP_HIGH&#125;</span>°C, 湿度 &gt; <span class="subst">&#123;HUMIDITY_HIGH&#125;</span>%&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;=&quot;</span> * <span class="number">40</span>)</span><br><span class="line"></span><br><span class="line">count = <span class="number">0</span></span><br><span class="line"><span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">    count += <span class="number">1</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 读取传感器</span></span><br><span class="line">    temp, humi = read_sensor()</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> temp <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">None</span> <span class="keyword">and</span> humi <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">None</span>:</span><br><span class="line">        <span class="comment"># 显示数据</span></span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;[<span class="subst">&#123;count:04d&#125;</span>] 温度: <span class="subst">&#123;temp:<span class="number">.1</span>f&#125;</span>°C  湿度: <span class="subst">&#123;humi:<span class="number">.1</span>f&#125;</span>%&quot;</span>)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 检查报警</span></span><br><span class="line">        alerts = check_alert(temp, humi)</span><br><span class="line">        <span class="keyword">if</span> alerts:</span><br><span class="line">            <span class="keyword">for</span> alert <span class="keyword">in</span> alerts:</span><br><span class="line">                <span class="built_in">print</span>(<span class="string">f&quot;  ⚠ <span class="subst">&#123;alert&#125;</span>&quot;</span>)</span><br><span class="line">            alarm_buzzer()  <span class="comment"># 蜂鸣器报警</span></span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">&quot;  ✅ 正常&quot;</span>)</span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;[<span class="subst">&#123;count:04d&#125;</span>] 读取失败，5秒后重试...&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># LED 闪烁表示在工作</span></span><br><span class="line">    blink_led(<span class="number">1</span>, <span class="number">0.05</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 等待 5 秒</span></span><br><span class="line">    time.sleep(<span class="number">5</span>)</span><br></pre></td></tr></table></figure><blockquote><p><strong>如果 DHT11 读取总是失败</strong>：检查接线是否正确，DHT11 的数据引脚需要接上拉电阻（有的模块自带），确保使用 3.3V 供电。</p></blockquote><p><strong>进阶：把数据上传到电脑（串口通信）</strong>：</p><p>在电脑上用 Python 接收 ESP32 的数据并保存到 CSV 文件：</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></pre></td><td class="code"><pre><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">电脑端：接收 ESP32 串口数据并保存</span></span><br><span class="line"><span class="string">安装：pip install pyserial</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="keyword">import</span> serial</span><br><span class="line"><span class="keyword">import</span> csv</span><br><span class="line"><span class="keyword">from</span> datetime <span class="keyword">import</span> datetime</span><br><span class="line"></span><br><span class="line"><span class="comment"># 修改为你的 ESP32 串口</span></span><br><span class="line"><span class="comment"># Windows: &quot;COM3&quot;, &quot;COM4&quot; 等</span></span><br><span class="line"><span class="comment"># Linux/Mac: &quot;/dev/ttyUSB0&quot; 或 &quot;/dev/tty.usbserial-xxx&quot;</span></span><br><span class="line">SERIAL_PORT = <span class="string">&quot;COM3&quot;</span></span><br><span class="line">BAUD_RATE = <span class="number">115200</span></span><br><span class="line"></span><br><span class="line">csv_file = <span class="built_in">open</span>(<span class="string">&quot;sensor_data.csv&quot;</span>, <span class="string">&quot;a&quot;</span>, newline=<span class="string">&quot;&quot;</span>, encoding=<span class="string">&quot;utf-8&quot;</span>)</span><br><span class="line">writer = csv.writer(csv_file)</span><br><span class="line">writer.writerow([<span class="string">&quot;时间&quot;</span>, <span class="string">&quot;温度(°C)&quot;</span>, <span class="string">&quot;湿度(%)&quot;</span>])  <span class="comment"># 写入表头</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">try</span>:</span><br><span class="line">    ser = serial.Serial(SERIAL_PORT, BAUD_RATE, timeout=<span class="number">1</span>)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;已连接 <span class="subst">&#123;SERIAL_PORT&#125;</span>，等待数据...&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">        line = ser.readline().decode(<span class="string">&quot;utf-8&quot;</span>, errors=<span class="string">&quot;ignore&quot;</span>).strip()</span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> line:</span><br><span class="line">            <span class="keyword">continue</span></span><br><span class="line"></span><br><span class="line">        <span class="built_in">print</span>(line)  <span class="comment"># 打印原始数据</span></span><br><span class="line"></span><br><span class="line">        <span class="comment"># 简单解析：从 &quot;[0001] 温度: 25.3°C  湿度: 60.5%&quot; 中提取数值</span></span><br><span class="line">        <span class="keyword">if</span> <span class="string">&quot;温度:&quot;</span> <span class="keyword">in</span> line <span class="keyword">and</span> <span class="string">&quot;湿度:&quot;</span> <span class="keyword">in</span> line:</span><br><span class="line">            <span class="keyword">try</span>:</span><br><span class="line">                temp_str = line.split(<span class="string">&quot;温度:&quot;</span>)[<span class="number">1</span>].split(<span class="string">&quot;°C&quot;</span>)[<span class="number">0</span>].strip()</span><br><span class="line">                humi_str = line.split(<span class="string">&quot;湿度:&quot;</span>)[<span class="number">1</span>].split(<span class="string">&quot;%&quot;</span>)[<span class="number">0</span>].strip()</span><br><span class="line">                now = datetime.now().strftime(<span class="string">&quot;%Y-%m-%d %H:%M:%S&quot;</span>)</span><br><span class="line">                writer.writerow([now, temp_str, humi_str])</span><br><span class="line">                csv_file.flush()  <span class="comment"># 立即写入磁盘</span></span><br><span class="line">            <span class="keyword">except</span> (IndexError, ValueError):</span><br><span class="line">                <span class="keyword">continue</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">except</span> KeyboardInterrupt:</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;\n数据采集已停止&quot;</span>)</span><br><span class="line"><span class="keyword">finally</span>:</span><br><span class="line">    csv_file.close()</span><br><span class="line">    <span class="keyword">if</span> <span class="string">&quot;ser&quot;</span> <span class="keyword">in</span> <span class="built_in">locals</span>():</span><br><span class="line">        ser.close()</span><br></pre></td></tr></table></figure><h3 id="8-3-树莓派项目：智能家居控制"><a href="#8-3-树莓派项目：智能家居控制" class="headerlink" title="8.3 树莓派项目：智能家居控制"></a>8.3 树莓派项目：智能家居控制</h3><p>树莓派比 ESP32 强大得多——它运行完整的 Linux 系统，可以装 Python、跑 Web 服务、接摄像头。下面用树莓派的 GPIO 控制继电器，实现手机远程控制家电。</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><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><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">树莓派智能家居控制</span></span><br><span class="line"><span class="string">硬件连接：</span></span><br><span class="line"><span class="string">  - 继电器模块 IN → GPIO 18（控制灯）</span></span><br><span class="line"><span class="string">  - 继电器模块 IN → GPIO 23（控制风扇）</span></span><br><span class="line"><span class="string">  - DHT22 数据引脚 → GPIO 4（温湿度传感器）</span></span><br><span class="line"><span class="string">功能：</span></span><br><span class="line"><span class="string">  1. 读取室内温湿度</span></span><br><span class="line"><span class="string">  2. 温度高于阈值自动开风扇</span></span><br><span class="line"><span class="string">  3. 提供 Web 接口，手机远程控制</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="keyword">import</span> RPi.GPIO <span class="keyword">as</span> GPIO</span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"><span class="keyword">import</span> json</span><br><span class="line"><span class="keyword">from</span> http.server <span class="keyword">import</span> HTTPServer, BaseHTTPRequestHandler</span><br><span class="line"><span class="keyword">from</span> datetime <span class="keyword">import</span> datetime</span><br><span class="line"></span><br><span class="line"><span class="comment"># ===== GPIO 配置 =====</span></span><br><span class="line">RELAY_LIGHT = <span class="number">18</span>    <span class="comment"># 灯的继电器引脚</span></span><br><span class="line">RELAY_FAN = <span class="number">23</span>      <span class="comment"># 风扇的继电器引脚</span></span><br><span class="line">DHT_PIN = <span class="number">4</span>         <span class="comment"># DHT22 传感器引脚</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 继电器控制模式：低电平触发（大多数继电器模块）</span></span><br><span class="line">RELAY_ON = GPIO.LOW</span><br><span class="line">RELAY_OFF = GPIO.HIGH</span><br><span class="line"></span><br><span class="line"><span class="comment"># 自动控制阈值</span></span><br><span class="line">FAN_ON_TEMP = <span class="number">28.0</span>   <span class="comment"># 温度超过 28°C 自动开风扇</span></span><br><span class="line">FAN_OFF_TEMP = <span class="number">25.0</span>   <span class="comment"># 温度低于 25°C 自动关风扇</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ===== 设备状态 =====</span></span><br><span class="line">device_state = &#123;</span><br><span class="line">    <span class="string">&quot;light&quot;</span>: <span class="literal">False</span>,   <span class="comment"># 灯：关</span></span><br><span class="line">    <span class="string">&quot;fan&quot;</span>: <span class="literal">False</span>,     <span class="comment"># 风扇：关</span></span><br><span class="line">    <span class="string">&quot;auto_fan&quot;</span>: <span class="literal">True</span>,  <span class="comment"># 自动风扇控制：开</span></span><br><span class="line">    <span class="string">&quot;temperature&quot;</span>: <span class="literal">None</span>,</span><br><span class="line">    <span class="string">&quot;humidity&quot;</span>: <span class="literal">None</span>,</span><br><span class="line">    <span class="string">&quot;last_update&quot;</span>: <span class="string">&quot;&quot;</span>,</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">setup_gpio</span>():</span><br><span class="line">    <span class="string">&quot;&quot;&quot;初始化 GPIO。&quot;&quot;&quot;</span></span><br><span class="line">    GPIO.setmode(GPIO.BCM)</span><br><span class="line">    GPIO.setwarnings(<span class="literal">False</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 设置继电器引脚为输出</span></span><br><span class="line">    GPIO.setup(RELAY_LIGHT, GPIO.OUT)</span><br><span class="line">    GPIO.setup(RELAY_FAN, GPIO.OUT)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 初始状态：全部关闭</span></span><br><span class="line">    GPIO.output(RELAY_LIGHT, RELAY_OFF)</span><br><span class="line">    GPIO.output(RELAY_FAN, RELAY_OFF)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">control_light</span>(<span class="params">on: <span class="built_in">bool</span></span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;控制灯。&quot;&quot;&quot;</span></span><br><span class="line">    GPIO.output(RELAY_LIGHT, RELAY_ON <span class="keyword">if</span> on <span class="keyword">else</span> RELAY_OFF)</span><br><span class="line">    device_state[<span class="string">&quot;light&quot;</span>] = on</span><br><span class="line">    status = <span class="string">&quot;开&quot;</span> <span class="keyword">if</span> on <span class="keyword">else</span> <span class="string">&quot;关&quot;</span></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;[<span class="subst">&#123;datetime.now():%H:%M:%S&#125;</span>] 灯: <span class="subst">&#123;status&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">control_fan</span>(<span class="params">on: <span class="built_in">bool</span></span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;控制风扇。&quot;&quot;&quot;</span></span><br><span class="line">    GPIO.output(RELAY_FAN, RELAY_ON <span class="keyword">if</span> on <span class="keyword">else</span> RELAY_OFF)</span><br><span class="line">    device_state[<span class="string">&quot;fan&quot;</span>] = on</span><br><span class="line">    status = <span class="string">&quot;开&quot;</span> <span class="keyword">if</span> on <span class="keyword">else</span> <span class="string">&quot;关&quot;</span></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;[<span class="subst">&#123;datetime.now():%H:%M:%S&#125;</span>] 风扇: <span class="subst">&#123;status&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">read_dht22</span>():</span><br><span class="line">    <span class="string">&quot;&quot;&quot;读取 DHT22 温湿度传感器。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        <span class="comment"># 方式1：使用 adafruit-circuitpython-dht 库</span></span><br><span class="line">        <span class="comment"># import adafruit_dht</span></span><br><span class="line">        <span class="comment"># dht_device = adafruit_dht.DHT22(board.D4)</span></span><br><span class="line">        <span class="comment"># temp = dht_device.temperature</span></span><br><span class="line">        <span class="comment"># humi = dht_device.humidity</span></span><br><span class="line"></span><br><span class="line">        <span class="comment"># 方式2：使用系统命令读取（更稳定）</span></span><br><span class="line">        <span class="keyword">import</span> subprocess</span><br><span class="line">        result = subprocess.run(</span><br><span class="line">            [<span class="string">&quot;python3&quot;</span>, <span class="string">&quot;-c&quot;</span>,</span><br><span class="line">             <span class="string">&quot;import adafruit_dht, board; &quot;</span></span><br><span class="line">             <span class="string">&quot;d = adafruit_dht.DHT22(board.D4); &quot;</span></span><br><span class="line">             <span class="string">&quot;print(f&#x27;&#123;d.temperature&#125;,&#123;d.humidity&#125;&#x27;)&quot;</span>],</span><br><span class="line">            capture_output=<span class="literal">True</span>, text=<span class="literal">True</span>, timeout=<span class="number">5</span></span><br><span class="line">        )</span><br><span class="line">        parts = result.stdout.strip().split(<span class="string">&quot;,&quot;</span>)</span><br><span class="line">        <span class="keyword">if</span> <span class="built_in">len</span>(parts) == <span class="number">2</span>:</span><br><span class="line">            temp = <span class="built_in">float</span>(parts[<span class="number">0</span>])</span><br><span class="line">            humi = <span class="built_in">float</span>(parts[<span class="number">1</span>])</span><br><span class="line">            <span class="keyword">return</span> temp, humi</span><br><span class="line">    <span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;DHT22 读取失败: <span class="subst">&#123;e&#125;</span>&quot;</span>)</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">None</span>, <span class="literal">None</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">auto_control</span>():</span><br><span class="line">    <span class="string">&quot;&quot;&quot;自动控制逻辑：根据温度自动开关风扇。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> device_state[<span class="string">&quot;auto_fan&quot;</span>]:</span><br><span class="line">        <span class="keyword">return</span></span><br><span class="line"></span><br><span class="line">    temp = device_state[<span class="string">&quot;temperature&quot;</span>]</span><br><span class="line">    <span class="keyword">if</span> temp <span class="keyword">is</span> <span class="literal">None</span>:</span><br><span class="line">        <span class="keyword">return</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 温度超过阈值 → 开风扇</span></span><br><span class="line">    <span class="keyword">if</span> temp &gt; FAN_ON_TEMP <span class="keyword">and</span> <span class="keyword">not</span> device_state[<span class="string">&quot;fan&quot;</span>]:</span><br><span class="line">        control_fan(<span class="literal">True</span>)</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;  自动控制: 温度 <span class="subst">&#123;temp&#125;</span>°C &gt; <span class="subst">&#123;FAN_ON_TEMP&#125;</span>°C，开启风扇&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 温度低于阈值 → 关风扇</span></span><br><span class="line">    <span class="keyword">elif</span> temp &lt; FAN_OFF_TEMP <span class="keyword">and</span> device_state[<span class="string">&quot;fan&quot;</span>]:</span><br><span class="line">        control_fan(<span class="literal">False</span>)</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;  自动控制: 温度 <span class="subst">&#123;temp&#125;</span>°C &lt; <span class="subst">&#123;FAN_OFF_TEMP&#125;</span>°C，关闭风扇&quot;</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># ===== Web 控制接口 =====</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">SmartHomeHandler</span>(<span class="title class_ inherited__">BaseHTTPRequestHandler</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;智能家居 Web 控制处理器。&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">do_GET</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;处理 GET 请求。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">if</span> <span class="variable language_">self</span>.path == <span class="string">&quot;/&quot;</span> <span class="keyword">or</span> <span class="variable language_">self</span>.path == <span class="string">&quot;/index.html&quot;</span>:</span><br><span class="line">            <span class="comment"># 返回控制页面</span></span><br><span class="line">            html = <span class="variable language_">self</span>._get_control_page()</span><br><span class="line">            <span class="variable language_">self</span>._send_response(<span class="number">200</span>, html, <span class="string">&quot;text/html&quot;</span>)</span><br><span class="line"></span><br><span class="line">        <span class="keyword">elif</span> <span class="variable language_">self</span>.path == <span class="string">&quot;/api/status&quot;</span>:</span><br><span class="line">            <span class="comment"># 返回设备状态 JSON</span></span><br><span class="line">            <span class="variable language_">self</span>._send_response(<span class="number">200</span>, json.dumps(device_state, ensure_ascii=<span class="literal">False</span>), <span class="string">&quot;application/json&quot;</span>)</span><br><span class="line"></span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            <span class="variable language_">self</span>._send_response(<span class="number">404</span>, <span class="string">&quot;Not Found&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">do_POST</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;处理 POST 请求，控制设备。&quot;&quot;&quot;</span></span><br><span class="line">        content_length = <span class="built_in">int</span>(<span class="variable language_">self</span>.headers.get(<span class="string">&quot;Content-Length&quot;</span>, <span class="number">0</span>))</span><br><span class="line">        body = <span class="variable language_">self</span>.rfile.read(content_length).decode(<span class="string">&quot;utf-8&quot;</span>)</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span>:</span><br><span class="line">            data = json.loads(body)</span><br><span class="line">        <span class="keyword">except</span> json.JSONDecodeError:</span><br><span class="line">            <span class="variable language_">self</span>._send_response(<span class="number">400</span>, <span class="string">&#x27;&#123;&quot;error&quot;: &quot;Invalid JSON&quot;&#125;&#x27;</span>)</span><br><span class="line">            <span class="keyword">return</span></span><br><span class="line"></span><br><span class="line">        <span class="comment"># 控制灯</span></span><br><span class="line">        <span class="keyword">if</span> <span class="string">&quot;light&quot;</span> <span class="keyword">in</span> data:</span><br><span class="line">            control_light(data[<span class="string">&quot;light&quot;</span>])</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 控制风扇</span></span><br><span class="line">        <span class="keyword">if</span> <span class="string">&quot;fan&quot;</span> <span class="keyword">in</span> data:</span><br><span class="line">            control_fan(data[<span class="string">&quot;fan&quot;</span>])</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 自动模式</span></span><br><span class="line">        <span class="keyword">if</span> <span class="string">&quot;auto_fan&quot;</span> <span class="keyword">in</span> data:</span><br><span class="line">            device_state[<span class="string">&quot;auto_fan&quot;</span>] = data[<span class="string">&quot;auto_fan&quot;</span>]</span><br><span class="line"></span><br><span class="line">        <span class="variable language_">self</span>._send_response(<span class="number">200</span>, json.dumps(device_state, ensure_ascii=<span class="literal">False</span>), <span class="string">&quot;application/json&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">_send_response</span>(<span class="params">self, code: <span class="built_in">int</span>, content: <span class="built_in">str</span>, content_type: <span class="built_in">str</span> = <span class="string">&quot;text/plain&quot;</span></span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;发送 HTTP 响应。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="variable language_">self</span>.send_response(code)</span><br><span class="line">        <span class="variable language_">self</span>.send_header(<span class="string">&quot;Content-Type&quot;</span>, <span class="string">f&quot;<span class="subst">&#123;content_type&#125;</span>; charset=utf-8&quot;</span>)</span><br><span class="line">        <span class="variable language_">self</span>.send_header(<span class="string">&quot;Access-Control-Allow-Origin&quot;</span>, <span class="string">&quot;*&quot;</span>)  <span class="comment"># 允许跨域</span></span><br><span class="line">        <span class="variable language_">self</span>.end_headers()</span><br><span class="line">        <span class="variable language_">self</span>.wfile.write(content.encode(<span class="string">&quot;utf-8&quot;</span>))</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">_get_control_page</span>(<span class="params">self</span>) -&gt; <span class="built_in">str</span>:</span><br><span class="line">        <span class="string">&quot;&quot;&quot;生成简易控制页面。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;&quot;&quot;&lt;!DOCTYPE html&gt;</span></span><br><span class="line"><span class="string">&lt;html&gt;&lt;head&gt;&lt;meta charset=&quot;utf-8&quot;&gt;</span></span><br><span class="line"><span class="string">&lt;meta name=&quot;viewport&quot; content=&quot;width=device-width,initial-scale=1&quot;&gt;</span></span><br><span class="line"><span class="string">&lt;title&gt;智能家居控制&lt;/title&gt;</span></span><br><span class="line"><span class="string">&lt;style&gt;</span></span><br><span class="line"><span class="string">  body &#123; font-family: sans-serif; text-align: center; padding: 20px; &#125;</span></span><br><span class="line"><span class="string">  .device &#123; border: 2px solid #ddd; border-radius: 12px; padding: 20px;</span></span><br><span class="line"><span class="string">            margin: 10px; display: inline-block; min-width: 200px; &#125;</span></span><br><span class="line"><span class="string">  .btn &#123; padding: 12px 30px; font-size: 18px; border: none;</span></span><br><span class="line"><span class="string">         border-radius: 8px; cursor: pointer; margin: 5px; &#125;</span></span><br><span class="line"><span class="string">  .btn-on &#123; background: #4CAF50; color: white; &#125;</span></span><br><span class="line"><span class="string">  .btn-off &#123; background: #f44336; color: white; &#125;</span></span><br><span class="line"><span class="string">  #status &#123; margin-top: 20px; font-size: 16px; &#125;</span></span><br><span class="line"><span class="string">&lt;/style&gt;&lt;/head&gt;</span></span><br><span class="line"><span class="string">&lt;body&gt;</span></span><br><span class="line"><span class="string">&lt;h1&gt;🏠 智能家居控制&lt;/h1&gt;</span></span><br><span class="line"><span class="string">&lt;div class=&quot;device&quot;&gt;</span></span><br><span class="line"><span class="string">  &lt;h2&gt;💡 灯&lt;/h2&gt;</span></span><br><span class="line"><span class="string">  &lt;button class=&quot;btn btn-on&quot; onclick=&quot;control(&#x27;light&#x27;, true)&quot;&gt;开灯&lt;/button&gt;</span></span><br><span class="line"><span class="string">  &lt;button class=&quot;btn btn-off&quot; onclick=&quot;control(&#x27;light&#x27;, false)&quot;&gt;关灯&lt;/button&gt;</span></span><br><span class="line"><span class="string">&lt;/div&gt;</span></span><br><span class="line"><span class="string">&lt;div class=&quot;device&quot;&gt;</span></span><br><span class="line"><span class="string">  &lt;h2&gt;🌀 风扇&lt;/h2&gt;</span></span><br><span class="line"><span class="string">  &lt;button class=&quot;btn btn-on&quot; onclick=&quot;control(&#x27;fan&#x27;, true)&quot;&gt;开风扇&lt;/button&gt;</span></span><br><span class="line"><span class="string">  &lt;button class=&quot;btn btn-off&quot; onclick=&quot;control(&#x27;fan&#x27;, false)&quot;&gt;关风扇&lt;/button&gt;</span></span><br><span class="line"><span class="string">&lt;/div&gt;</span></span><br><span class="line"><span class="string">&lt;div id=&quot;status&quot;&gt;加载中...&lt;/div&gt;</span></span><br><span class="line"><span class="string">&lt;script&gt;</span></span><br><span class="line"><span class="string">function control(device, state) &#123;</span></span><br><span class="line"><span class="string">  fetch(&#x27;/api/status&#x27;, &#123;</span></span><br><span class="line"><span class="string">    method: &#x27;POST&#x27;,</span></span><br><span class="line"><span class="string">    headers: &#123;&#x27;Content-Type&#x27;: &#x27;application/json&#x27;&#125;,</span></span><br><span class="line"><span class="string">    body: JSON.stringify(&#123;[device]: state&#125;)</span></span><br><span class="line"><span class="string">  &#125;).then(r =&gt; r.json()).then(updateStatus);</span></span><br><span class="line"><span class="string">&#125;</span></span><br><span class="line"><span class="string">function updateStatus(s) &#123;</span></span><br><span class="line"><span class="string">  document.getElementById(&#x27;status&#x27;).innerHTML =</span></span><br><span class="line"><span class="string">    `🌡 温度: $&#123;s.temperature ?? &#x27;--&#x27;&#125;°C &amp;nbsp; 💧 湿度: $&#123;s.humidity ?? &#x27;--&#x27;&#125;%&lt;br&gt;`</span></span><br><span class="line"><span class="string">    + `💡 灯: $&#123;s.light ? &#x27;开&#x27; : &#x27;关&#x27;&#125; &amp;nbsp; 🌀 风扇: $&#123;s.fan ? &#x27;开&#x27; : &#x27;关&#x27;&#125;&lt;br&gt;`</span></span><br><span class="line"><span class="string">    + `🤖 自动风扇: $&#123;s.auto_fan ? &#x27;开&#x27; : &#x27;关&#x27;&#125;`;</span></span><br><span class="line"><span class="string">&#125;</span></span><br><span class="line"><span class="string">setInterval(() =&gt; fetch(&#x27;/api/status&#x27;).then(r=&gt;r.json()).then(updateStatus), 3000);</span></span><br><span class="line"><span class="string">fetch(&#x27;/api/status&#x27;).then(r=&gt;r.json()).then(updateStatus);</span></span><br><span class="line"><span class="string">&lt;/script&gt;</span></span><br><span class="line"><span class="string">&lt;/body&gt;&lt;/html&gt;&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># ===== 主程序 =====</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">main</span>():</span><br><span class="line">    <span class="string">&quot;&quot;&quot;主函数。&quot;&quot;&quot;</span></span><br><span class="line">    setup_gpio()</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;智能家居系统启动&quot;</span>)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;风扇自动控制: 温度 &gt; <span class="subst">&#123;FAN_ON_TEMP&#125;</span>°C 开启, &lt; <span class="subst">&#123;FAN_OFF_TEMP&#125;</span>°C 关闭&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 启动 Web 服务器（在独立线程中运行）</span></span><br><span class="line">    server = HTTPServer((<span class="string">&quot;0.0.0.0&quot;</span>, <span class="number">8080</span>), SmartHomeHandler)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;Web 控制面板: http://树莓派IP:8080&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">import</span> threading</span><br><span class="line">    server_thread = threading.Thread(target=server.serve_forever, daemon=<span class="literal">True</span>)</span><br><span class="line">    server_thread.start()</span><br><span class="line"></span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        <span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">            <span class="comment"># 读取传感器</span></span><br><span class="line">            temp, humi = read_dht22()</span><br><span class="line">            <span class="keyword">if</span> temp <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">None</span>:</span><br><span class="line">                device_state[<span class="string">&quot;temperature&quot;</span>] = <span class="built_in">round</span>(temp, <span class="number">1</span>)</span><br><span class="line">                device_state[<span class="string">&quot;humidity&quot;</span>] = <span class="built_in">round</span>(humi, <span class="number">1</span>)</span><br><span class="line">                device_state[<span class="string">&quot;last_update&quot;</span>] = datetime.now().strftime(<span class="string">&quot;%H:%M:%S&quot;</span>)</span><br><span class="line">                <span class="built_in">print</span>(<span class="string">f&quot;[<span class="subst">&#123;device_state[<span class="string">&#x27;last_update&#x27;</span>]&#125;</span>] &quot;</span></span><br><span class="line">                      <span class="string">f&quot;温度: <span class="subst">&#123;temp:<span class="number">.1</span>f&#125;</span>°C  湿度: <span class="subst">&#123;humi:<span class="number">.1</span>f&#125;</span>%&quot;</span>)</span><br><span class="line"></span><br><span class="line">            <span class="comment"># 自动控制</span></span><br><span class="line">            auto_control()</span><br><span class="line"></span><br><span class="line">            <span class="comment"># 每 10 秒采集一次</span></span><br><span class="line">            time.sleep(<span class="number">10</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">except</span> KeyboardInterrupt:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;\n正在关闭...&quot;</span>)</span><br><span class="line">    <span class="keyword">finally</span>:</span><br><span class="line">        GPIO.cleanup()</span><br><span class="line">        server.shutdown()</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;已清理 GPIO，程序退出&quot;</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    main()</span><br></pre></td></tr></table></figure><blockquote><p><strong>树莓派项目上手步骤</strong>：</p><ol><li>烧录 Raspberry Pi OS（官方系统）</li><li>连接 WiFi，SSH 登录</li><li><code>pip install RPi.GPIO adafruit-circuitpython-dht</code></li><li>按照接线说明连接继电器和传感器</li><li>运行脚本，手机浏览器访问 <code>http://树莓派IP:8080</code></li></ol></blockquote><h3 id="8-4-常见新手坑"><a href="#8-4-常见新手坑" class="headerlink" title="8.4 常见新手坑"></a>8.4 常见新手坑</h3><table><thead><tr><th>坑</th><th>说明</th><th>解决方案</th></tr></thead><tbody><tr><td><strong>ESP32 串口连不上</strong></td><td>Thonny 找不到 ESP32</td><td>安装 CP210x&#x2F;CH340 驱动，换 USB 线（有些线只能充电不能传数据）</td></tr><tr><td><strong>DHT 读取经常失败</strong></td><td>传感器偶尔返回错误</td><td>加 <code>try-except</code> 忽略失败，自动重试</td></tr><tr><td><strong>GPIO 编号混乱</strong></td><td>BCM 和 BOARD 两种编号方式</td><td>统一使用 <code>GPIO.BCM</code> 编号，和网上教程一致</td></tr><tr><td><strong>继电器不工作</strong></td><td>继电器没反应</td><td>确认是高电平还是低电平触发，大多数模块是低电平触发</td></tr><tr><td><strong>树莓派 GPIO 烧毁</strong></td><td>接错电压导致 GPIO 损坏</td><td>GPIO 只能输出 3.3V，接 5V 设备必须用继电器或电平转换</td></tr><tr><td><strong>MicroPython 固件版本不对</strong></td><td>导入模块报错</td><td>从 <a href="https://micropython.org/download/ESP32_GENERIC/">micropython.org</a> 下载最新固件重新烧录</td></tr><tr><td><strong>ESP32 重启循环</strong></td><td>程序报错导致反复重启</td><td>在 Thonny 中查看错误信息，修复后重新保存</td></tr><tr><td><strong>WiFi 连接不稳定</strong></td><td>ESP32 WiFi 频繁断开</td><td>添加自动重连逻辑，检查路由器信号强度</td></tr></tbody></table><hr><h2 id="9-各领域学习路径建议"><a href="#9-各领域学习路径建议" class="headerlink" title="9. 各领域学习路径建议"></a>9. 各领域学习路径建议</h2><blockquote><p>以下路径包含<strong>具体学习资源、推荐练手项目</strong>，帮你从”知道技术栈”到”真正能做项目”。</p></blockquote><h3 id="9-1-Web-后端工程师"><a href="#9-1-Web-后端工程师" class="headerlink" title="9.1 Web 后端工程师"></a>9.1 Web 后端工程师</h3><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">阶段 1：基础（2-4 周）</span><br><span class="line">  ├── Python 基础（阶段一~四）</span><br><span class="line">  ├── HTTP 协议理解（请求方法、状态码、Header）</span><br><span class="line">  └── SQL 基础（SELECT / JOIN / 索引）</span><br><span class="line"></span><br><span class="line">  📚 资源：</span><br><span class="line">  - MDN HTTP 教程：https://developer.mozilla.org/zh-CN/docs/Web/HTTP</span><br><span class="line">  - SQLZoo 在线练习：https://sqlzoo.net</span><br><span class="line">  - 练手项目：用 Flask 写一个 TODO API（增删改查）</span><br><span class="line"></span><br><span class="line">阶段 2：框架深入（4-6 周）</span><br><span class="line">  ├── FastAPI / Django 深入</span><br><span class="line">  ├── SQLAlchemy ORM</span><br><span class="line">  ├── 认证授权（JWT/OAuth2）</span><br><span class="line">  └── RESTful API 设计</span><br><span class="line"></span><br><span class="line">  📚 资源：</span><br><span class="line">  - FastAPI 官方教程：https://fastapi.tiangolo.com/zh/tutorial/</span><br><span class="line">  - Django 官方教程（Polls 应用）：https://docs.djangoproject.com/zh-hans/</span><br><span class="line">  - 练手项目：博客系统 API（用户注册/登录/文章CRUD/评论/分页）</span><br><span class="line"></span><br><span class="line">阶段 3：工程化（4-6 周）</span><br><span class="line">  ├── 测试（pytest + 覆盖率）</span><br><span class="line">  ├── Docker 容器化</span><br><span class="line">  ├── CI/CD（GitHub Actions）</span><br><span class="line">  └── 监控与日志</span><br><span class="line"></span><br><span class="line">  📚 资源：</span><br><span class="line">  - pytest 官方文档：https://docs.pytest.org/</span><br><span class="line">  - Docker 从入门到实践：https://yeasy.github.io/docker_practice/</span><br><span class="line">  - 练手项目：将阶段2的项目 Docker 化 + 添加 CI/CD + 部署到云服务器</span><br><span class="line"></span><br><span class="line">阶段 4：高级（持续学习）</span><br><span class="line">  ├── 微服务架构</span><br><span class="line">  ├── 分布式系统（消息队列/缓存）</span><br><span class="line">  ├── 性能优化（异步/数据库优化）</span><br><span class="line">  └── 安全加固</span><br><span class="line"></span><br><span class="line">  📚 资源：</span><br><span class="line">  - 《Python Web 开发实战》</span><br><span class="line">  - 练手项目：电商后端（多服务 + Redis 缓存 + Celery 异步任务）</span><br></pre></td></tr></table></figure><h3 id="9-2-数据工程师-数据分析师"><a href="#9-2-数据工程师-数据分析师" class="headerlink" title="9.2 数据工程师 &#x2F; 数据分析师"></a>9.2 数据工程师 &#x2F; 数据分析师</h3><figure class="highlight plaintext"><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><span class="line">阶段 1：基础（3-4 周）</span><br><span class="line">  ├── Python + NumPy + Pandas（参见本阶段第2节）</span><br><span class="line">  ├── SQL 高级（窗口函数/CTE/子查询）</span><br><span class="line">  └── 统计学基础（均值/方差/假设检验/相关系数）</span><br><span class="line"></span><br><span class="line">  📚 资源：</span><br><span class="line">  - Pandas 官方 10 分钟入门：https://pandas.pydata.org/docs/getting_started/intro_tutorials/</span><br><span class="line">  - LeetCode SQL 题库：https://leetcode.cn/problemset/database/</span><br><span class="line">  - 练手项目：用 Pandas 分析某电商真实数据集（Kaggle 下载）</span><br><span class="line"></span><br><span class="line">阶段 2：可视化与报告（2-3 周）</span><br><span class="line">  ├── Matplotlib / Seaborn / Plotly</span><br><span class="line">  ├── Jupyter Notebook 技巧</span><br><span class="line">  └── 数据报告撰写</span><br><span class="line"></span><br><span class="line">  📚 资源：</span><br><span class="line">  - Python Data Science Handbook：https://jakevdp.github.io/PythonDataScienceHandbook/</span><br><span class="line">  - 练手项目：某行业数据分析报告（含 5+ 种图表、结论、建议）</span><br><span class="line"></span><br><span class="line">阶段 3：机器学习基础（4-6 周）</span><br><span class="line">  ├── Scikit-learn 完整流程</span><br><span class="line">  ├── 特征工程</span><br><span class="line">  └── 模型评估与调优</span><br><span class="line"></span><br><span class="line">  📚 资源：</span><br><span class="line">  - Scikit-learn 官方教程：https://scikit-learn.org/stable/tutorial/</span><br><span class="line">  - Kaggle 入门竞赛：Titanic / House Prices</span><br><span class="line">  - 练手项目：Kaggle 首战（Titanic 生存预测，Top 50% 即可）</span><br><span class="line"></span><br><span class="line">阶段 4：大数据（进阶）</span><br><span class="line">  ├── ETL 流程（Airflow / Prefect）</span><br><span class="line">  ├── 大数据（PySpark / Dask）</span><br><span class="line">  ├── 数据仓库</span><br><span class="line">  └── 消息队列（Kafka）</span><br><span class="line"></span><br><span class="line">  📚 资源：</span><br><span class="line">  - PySpark 官方文档</span><br><span class="line">  - 练手项目：构建 ETL 管道（CSV → 清洗 → 聚合 → 入库）</span><br></pre></td></tr></table></figure><h3 id="9-3-AI-ML-工程师"><a href="#9-3-AI-ML-工程师" class="headerlink" title="9.3 AI&#x2F;ML 工程师"></a>9.3 AI&#x2F;ML 工程师</h3><figure class="highlight plaintext"><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><span class="line">阶段 1：基础（4-6 周）</span><br><span class="line">  ├── Python + NumPy + Pandas</span><br><span class="line">  ├── 线性代数（矩阵运算/特征值/向量空间）</span><br><span class="line">  ├── 概率统计（贝叶斯/分布/假设检验）</span><br><span class="line">  └── 机器学习理论（分类/回归/聚类/降维）</span><br><span class="line"></span><br><span class="line">  📚 资源：</span><br><span class="line">  - 吴恩达机器学习课程：https://www.coursera.org/learn/machine-learning</span><br><span class="line">  - 3Blue1Brown 线性代数（视频）：https://www.3blue1brown.com/topics/linear-algebra</span><br><span class="line">  - 练手项目：手写数字分类（MNIST，不使用框架，纯 NumPy 实现）</span><br><span class="line"></span><br><span class="line">阶段 2：实践（6-8 周）</span><br><span class="line">  ├── Scikit-learn 熟练使用</span><br><span class="line">  ├── PyTorch / TensorFlow 深度学习</span><br><span class="line">  ├── CNN / RNN / Transformer 原理</span><br><span class="line">  └── 模型部署（ONNX / TorchServe）</span><br><span class="line"></span><br><span class="line">  📚 资源：</span><br><span class="line">  - PyTorch 官方教程：https://pytorch.org/tutorials/</span><br><span class="line">  - 动手学深度学习（d2l.ai）：https://zh.d2l.ai/</span><br><span class="line">  - 练手项目：图像分类（CIFAR-10）+ 文本分类（IMDB 情感分析）</span><br><span class="line"></span><br><span class="line">阶段 3：应用（持续学习）</span><br><span class="line">  ├── LLM 应用开发（LangChain / LlamaIndex）</span><br><span class="line">  ├── 计算机视觉（YOLO / SAM）</span><br><span class="line">  ├── NLP（BERT / GPT 微调）</span><br><span class="line">  └── MLOps（MLflow / Weights &amp; Biases）</span><br><span class="line"></span><br><span class="line">  📚 资源：</span><br><span class="line">  - LangChain 官方文档：https://python.langchain.com/</span><br><span class="line">  - Hugging Face 教程：https://huggingface.co/learn</span><br><span class="line">  - 练手项目：RAG 知识库问答系统（用自己整理的文档构建）</span><br></pre></td></tr></table></figure><h3 id="9-4-爬虫工程师"><a href="#9-4-爬虫工程师" class="headerlink" title="9.4 爬虫工程师"></a>9.4 爬虫工程师</h3><figure class="highlight plaintext"><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><span class="line">阶段 1：基础（1-2 周）</span><br><span class="line">  ├── requests + BeautifulSoup（参见本阶段第3节）</span><br><span class="line">  ├── HTTP 协议和开发者工具</span><br><span class="line">  └── 数据存储</span><br><span class="line"></span><br><span class="line">  📚 资源：</span><br><span class="line">  - requests 官方文档：https://requests.readthedocs.io/</span><br><span class="line">  - 练手项目：爬取某新闻网站标题和链接，保存为 CSV</span><br><span class="line"></span><br><span class="line">阶段 2：进阶（2-3 周）</span><br><span class="line">  ├── 动态页面（Playwright）</span><br><span class="line">  ├── Scrapy 框架</span><br><span class="line">  └── 反反爬技术</span><br><span class="line"></span><br><span class="line">  📚 资源：</span><br><span class="line">  - Scrapy 官方教程：https://docs.scrapy.org/</span><br><span class="line">  - Playwright Python 文档：https://playwright.dev/python/</span><br><span class="line">  - 练手项目：爬取某电商商品信息（含翻页+异步+存数据库）</span><br><span class="line"></span><br><span class="line">阶段 3：规模化（进阶）</span><br><span class="line">  ├── 分布式爬虫（Scrapy-Redis）</span><br><span class="line">  ├── 代理池和验证码</span><br><span class="line">  └── 增量爬取和去重</span><br><span class="line"></span><br><span class="line">  📚 资源：</span><br><span class="line">  - 练手项目：分布式爬虫（多机协同爬取百万级数据）</span><br></pre></td></tr></table></figure><h3 id="9-5-综合练手项目推荐"><a href="#9-5-综合练手项目推荐" class="headerlink" title="9.5 综合练手项目推荐"></a>9.5 综合练手项目推荐</h3><table><thead><tr><th>难度</th><th>项目</th><th>涉及技术</th><th>预计时间</th></tr></thead><tbody><tr><td>入门</td><td>个人博客 API</td><td>FastAPI + SQLite + JWT</td><td>1 周</td></tr><tr><td>入门</td><td>豆瓣电影 Top250 爬虫</td><td>requests + BeautifulSoup</td><td>2 天</td></tr><tr><td>入门</td><td>CSV 数据分析报告</td><td>Pandas + Matplotlib</td><td>3 天</td></tr><tr><td>中级</td><td>电商后台管理系统</td><td>Django + ORM + Admin</td><td>2 周</td></tr><tr><td>中级</td><td>新闻聚合爬虫</td><td>Scrapy + Playwright + Pipeline</td><td>1 周</td></tr><tr><td>中级</td><td>销售数据仪表盘</td><td>Dash + Plotly + Pandas</td><td>1 周</td></tr><tr><td>高级</td><td>AI 客服聊天机器人</td><td>LangChain + Chroma + FastAPI</td><td>2 周</td></tr><tr><td>高级</td><td>股票分析系统</td><td>yfinance + Scikit-learn + Dash</td><td>3 周</td></tr><tr><td>高级</td><td>自动化部署平台</td><td>Docker SDK + Fabric + CI&#x2F;CD</td><td>3 周</td></tr></tbody></table><p><strong>项目来源</strong>：</p><ul><li>Kaggle 数据集：<a href="https://www.kaggle.com/datasets">https://www.kaggle.com/datasets</a></li><li>真实 API：GitHub API &#x2F; 天气 API &#x2F; 新闻 API</li><li>公开数据：国家统计局 &#x2F; 各城市开放数据平台</li></ul><hr><h2 id="附录：第十阶段自检清单"><a href="#附录：第十阶段自检清单" class="headerlink" title="附录：第十阶段自检清单"></a>附录：第十阶段自检清单</h2><ul><li><input disabled="" type="checkbox"> 能用 FastAPI&#x2F;Django&#x2F;Flask 搭建简单的 Web API</li><li><input disabled="" type="checkbox"> 能用 Pandas 完成数据清洗、筛选、聚合操作</li><li><input disabled="" type="checkbox"> 能用 requests + BeautifulSoup 抓取静态网页</li><li><input disabled="" type="checkbox"> 了解 Scikit-learn 的基本使用流程（训练&#x2F;预测&#x2F;评估）</li><li><input disabled="" type="checkbox"> 了解 PyTorch 的基本概念（Tensor、Model、Loss、Optimizer）</li><li><input disabled="" type="checkbox"> 了解 OpenAI API &#x2F; LangChain 的基本用法</li><li><input disabled="" type="checkbox"> 能用 subprocess &#x2F; Fabric 编写自动化运维脚本</li><li><input disabled="" type="checkbox"> 了解至少一种 GUI 框架的基本用法</li><li><input disabled="" type="checkbox"> 了解 Docker 的基本概念（镜像、容器、Dockerfile）</li><li><input disabled="" type="checkbox"> 能根据自身兴趣选择一个方向制定学习路径</li></ul><hr><h2 id="附录：Python-学习路线图总结"><a href="#附录：Python-学习路线图总结" class="headerlink" title="附录：Python 学习路线图总结"></a>附录：Python 学习路线图总结</h2><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">┌──────────────────────────────────────────────────────────────────┐</span><br><span class="line">│                    Python 学习路线图                             │</span><br><span class="line">├──────────────────────────────────────────────────────────────────┤</span><br><span class="line">│                                                                  │</span><br><span class="line">│  第一阶段：基础语法与核心编程                                     │</span><br><span class="line">│     ├── 变量、数据类型、运算符                                    │</span><br><span class="line">│     ├── 流程控制（if/for/while）                                 │</span><br><span class="line">│     ├── 函数定义与调用                                           │</span><br><span class="line">│     └── 列表、字典、集合                                         │</span><br><span class="line">│                                                                  │</span><br><span class="line">│  第二阶段：函数与函数式编程                                       │</span><br><span class="line">│     ├── 函数参数、返回值                                         │</span><br><span class="line">│     ├── 匿名函数、闭包、装饰器                                    │</span><br><span class="line">│     ├── 生成器、迭代器                                           │</span><br><span class="line">│     └── functools / itertools                                   │</span><br><span class="line">│                                                                  │</span><br><span class="line">│  第三阶段：面向对象编程                                           │</span><br><span class="line">│     ├── 类与对象                                                 │</span><br><span class="line">│     ├── 继承、多态                                               │</span><br><span class="line">│     ├── 封装、属性装饰器                                         │</span><br><span class="line">│     └── 设计模式入门                                             │</span><br><span class="line">│                                                                  │</span><br><span class="line">│  第四阶段：异常处理与调试                                         │</span><br><span class="line">│     ├── try-except 异常处理                                      │</span><br><span class="line">│     ├── 自定义异常                                               │</span><br><span class="line">│     ├── 日志记录                                                 │</span><br><span class="line">│     └── 调试技巧                                                 │</span><br><span class="line">│                                                                  │</span><br><span class="line">│  第五阶段：模块、包与项目结构                                     │</span><br><span class="line">│     ├── import 机制                                              │</span><br><span class="line">│     ├── 包管理                                                   │</span><br><span class="line">│     ├── 项目结构设计                                             │</span><br><span class="line">│     └── __init__.py / __main__.py                               │</span><br><span class="line">│                                                                  │</span><br><span class="line">│  第六阶段：文件 I/O 与序列化                                      │</span><br><span class="line">│     ├── 文件读写                                                 │</span><br><span class="line">│     ├── 上下文管理器                                             │</span><br><span class="line">│     ├── JSON / Pickle / CSV                                     │</span><br><span class="line">│     └── 压缩与归档                                               │</span><br><span class="line">│                                                                  │</span><br><span class="line">│  第七阶段：高级特性                                               │</span><br><span class="line">│     ├── 迭代器协议                                               │</span><br><span class="line">│     ├── 生成器（send/throw/close）                               │</span><br><span class="line">│     ├── 装饰器进阶                                               │</span><br><span class="line">│     ├── 描述符、元类                                             │</span><br><span class="line">│     └── async/await 协程                                        │</span><br><span class="line">│                                                                  │</span><br><span class="line">│  第八阶段：标准库核心模块                                         │</span><br><span class="line">│     ├── collections / itertools / functools                      │</span><br><span class="line">│     ├── re / datetime / pathlib                                  │</span><br><span class="line">│     ├── threading / multiprocessing / asyncio                    │</span><br><span class="line">│     └── unittest / argparse / logging                            │</span><br><span class="line">│                                                                  │</span><br><span class="line">│  第九阶段：工程化与代码质量                                       │</span><br><span class="line">│     ├── PEP 8 / black / isort / flake8                          │</span><br><span class="line">│     ├── pytest / Mock / 测试覆盖率                               │</span><br><span class="line">│     ├── cProfile / 性能优化                                      │</span><br><span class="line">│     ├── CI/CD（GitHub Actions）                                 │</span><br><span class="line">│     └── 打包发布（pyproject.toml / Docker）                      │</span><br><span class="line">│                                                                  │</span><br><span class="line">│  第十阶段：主流应用领域                                           │</span><br><span class="line">│     ├── Web 开发（FastAPI/Django/Flask）                         │</span><br><span class="line">│     ├── 数据科学（NumPy/Pandas/Scikit-learn）                    │</span><br><span class="line">│     ├── AI/ML（PyTorch/LangChain）                               │</span><br><span class="line">│     ├── 爬虫（requests/Scrapy）                                  │</span><br><span class="line">│     ├── 自动化运维（subprocess/Fabric）                          │</span><br><span class="line">│     └── 桌面应用 / 游戏开发 / 物联网                             │</span><br><span class="line">│                                                                  │</span><br><span class="line">└──────────────────────────────────────────────────────────────────┘</span><br></pre></td></tr></table></figure><hr><h2 id="10-AI-与数据科学实战进阶"><a href="#10-AI-与数据科学实战进阶" class="headerlink" title="10. AI 与数据科学实战进阶"></a>10. AI 与数据科学实战进阶</h2><h3 id="10-1-端到端机器学习项目"><a href="#10-1-端到端机器学习项目" class="headerlink" title="10.1 端到端机器学习项目"></a>10.1 端到端机器学习项目</h3><p><strong>目标</strong>：构建一个完整的机器学习项目，从数据收集到模型部署。</p><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><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><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 第一步：数据收集与探索</span></span><br><span class="line"><span class="keyword">import</span> pandas <span class="keyword">as</span> pd</span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line"><span class="keyword">import</span> seaborn <span class="keyword">as</span> sns</span><br><span class="line"><span class="keyword">from</span> sklearn.model_selection <span class="keyword">import</span> train_test_split</span><br><span class="line"><span class="keyword">from</span> sklearn.preprocessing <span class="keyword">import</span> StandardScaler, LabelEncoder</span><br><span class="line"><span class="keyword">from</span> sklearn.ensemble <span class="keyword">import</span> RandomForestRegressor, GradientBoostingRegressor</span><br><span class="line"><span class="keyword">from</span> sklearn.metrics <span class="keyword">import</span> mean_squared_error, r2_score</span><br><span class="line"><span class="keyword">import</span> joblib</span><br><span class="line"></span><br><span class="line"><span class="comment"># 加载数据</span></span><br><span class="line">df = pd.read_csv(<span class="string">&quot;house_prices.csv&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 数据探索</span></span><br><span class="line"><span class="built_in">print</span>(df.info())</span><br><span class="line"><span class="built_in">print</span>(df.describe())</span><br><span class="line"><span class="built_in">print</span>(df.isnull().<span class="built_in">sum</span>())</span><br><span class="line"></span><br><span class="line"><span class="comment"># 可视化</span></span><br><span class="line">plt.figure(figsize=(<span class="number">12</span>, <span class="number">8</span>))</span><br><span class="line">sns.heatmap(df.corr(), annot=<span class="literal">True</span>, cmap=<span class="string">&#x27;coolwarm&#x27;</span>)</span><br><span class="line">plt.title(<span class="string">&quot;特征相关性热力图&quot;</span>)</span><br><span class="line">plt.savefig(<span class="string">&quot;correlation_heatmap.png&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 第二步：数据预处理</span></span><br><span class="line"><span class="comment"># 处理缺失值</span></span><br><span class="line">df[<span class="string">&#x27;area&#x27;</span>] = df[<span class="string">&#x27;area&#x27;</span>].fillna(df[<span class="string">&#x27;area&#x27;</span>].median())</span><br><span class="line">df[<span class="string">&#x27;bedrooms&#x27;</span>] = df[<span class="string">&#x27;bedrooms&#x27;</span>].fillna(df[<span class="string">&#x27;bedrooms&#x27;</span>].mode()[<span class="number">0</span>])</span><br><span class="line"></span><br><span class="line"><span class="comment"># 特征工程</span></span><br><span class="line">df[<span class="string">&#x27;price_per_sqm&#x27;</span>] = df[<span class="string">&#x27;price&#x27;</span>] / df[<span class="string">&#x27;area&#x27;</span>]</span><br><span class="line">df[<span class="string">&#x27;age_category&#x27;</span>] = pd.cut(df[<span class="string">&#x27;age&#x27;</span>], bins=[<span class="number">0</span>, <span class="number">5</span>, <span class="number">15</span>, <span class="number">30</span>, <span class="number">100</span>], labels=[<span class="string">&#x27;new&#x27;</span>, <span class="string">&#x27;medium&#x27;</span>, <span class="string">&#x27;old&#x27;</span>, <span class="string">&#x27;very_old&#x27;</span>])</span><br><span class="line"></span><br><span class="line"><span class="comment"># 编码分类特征</span></span><br><span class="line">le = LabelEncoder()</span><br><span class="line">df[<span class="string">&#x27;location_encoded&#x27;</span>] = le.fit_transform(df[<span class="string">&#x27;location&#x27;</span>])</span><br><span class="line"></span><br><span class="line"><span class="comment"># 特征选择</span></span><br><span class="line">features = [<span class="string">&#x27;area&#x27;</span>, <span class="string">&#x27;bedrooms&#x27;</span>, <span class="string">&#x27;bathrooms&#x27;</span>, <span class="string">&#x27;age&#x27;</span>, <span class="string">&#x27;location_encoded&#x27;</span>, <span class="string">&#x27;price_per_sqm&#x27;</span>]</span><br><span class="line">X = df[features]</span><br><span class="line">y = df[<span class="string">&#x27;price&#x27;</span>]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 划分数据集</span></span><br><span class="line">X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=<span class="number">0.2</span>, random_state=<span class="number">42</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 特征缩放</span></span><br><span class="line">scaler = StandardScaler()</span><br><span class="line">X_train_scaled = scaler.fit_transform(X_train)</span><br><span class="line">X_test_scaled = scaler.transform(X_test)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 第三步：模型训练与对比</span></span><br><span class="line">models = &#123;</span><br><span class="line">    <span class="string">&#x27;RandomForest&#x27;</span>: RandomForestRegressor(n_estimators=<span class="number">100</span>, random_state=<span class="number">42</span>),</span><br><span class="line">    <span class="string">&#x27;GradientBoosting&#x27;</span>: GradientBoostingRegressor(n_estimators=<span class="number">100</span>, random_state=<span class="number">42</span>)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">results = &#123;&#125;</span><br><span class="line"><span class="keyword">for</span> name, model <span class="keyword">in</span> models.items():</span><br><span class="line">    model.fit(X_train_scaled, y_train)</span><br><span class="line">    y_pred = model.predict(X_test_scaled)</span><br><span class="line"></span><br><span class="line">    mse = mean_squared_error(y_test, y_pred)</span><br><span class="line">    r2 = r2_score(y_test, y_pred)</span><br><span class="line"></span><br><span class="line">    results[name] = &#123;</span><br><span class="line">        <span class="string">&#x27;model&#x27;</span>: model,</span><br><span class="line">        <span class="string">&#x27;mse&#x27;</span>: mse,</span><br><span class="line">        <span class="string">&#x27;r2&#x27;</span>: r2,</span><br><span class="line">        <span class="string">&#x27;predictions&#x27;</span>: y_pred</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;<span class="subst">&#123;name&#125;</span>:&quot;</span>)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;  MSE: <span class="subst">&#123;mse:<span class="number">.2</span>f&#125;</span>&quot;</span>)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;  R²: <span class="subst">&#123;r2:<span class="number">.4</span>f&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 选择最佳模型</span></span><br><span class="line">best_model_name = <span class="built_in">max</span>(results, key=<span class="keyword">lambda</span> x: results[x][<span class="string">&#x27;r2&#x27;</span>])</span><br><span class="line">best_model = results[best_model_name][<span class="string">&#x27;model&#x27;</span>]</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;\n最佳模型: <span class="subst">&#123;best_model_name&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 第四步：模型保存与部署</span></span><br><span class="line">joblib.dump(best_model, <span class="string">&#x27;house_price_model.pkl&#x27;</span>)</span><br><span class="line">joblib.dump(scaler, <span class="string">&#x27;scaler.pkl&#x27;</span>)</span><br><span class="line">joblib.dump(le, <span class="string">&#x27;label_encoder.pkl&#x27;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 第五步：预测 API（FastAPI）</span></span><br><span class="line"><span class="keyword">from</span> fastapi <span class="keyword">import</span> FastAPI</span><br><span class="line"><span class="keyword">from</span> pydantic <span class="keyword">import</span> BaseModel</span><br><span class="line"><span class="keyword">import</span> joblib</span><br><span class="line"></span><br><span class="line">app = FastAPI(title=<span class="string">&quot;房价预测 API&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 加载模型</span></span><br><span class="line">model = joblib.load(<span class="string">&#x27;house_price_model.pkl&#x27;</span>)</span><br><span class="line">scaler = joblib.load(<span class="string">&#x27;scaler.pkl&#x27;</span>)</span><br><span class="line">label_encoder = joblib.load(<span class="string">&#x27;label_encoder.pkl&#x27;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">HouseFeatures</span>(<span class="title class_ inherited__">BaseModel</span>):</span><br><span class="line">    area: <span class="built_in">float</span></span><br><span class="line">    bedrooms: <span class="built_in">int</span></span><br><span class="line">    bathrooms: <span class="built_in">int</span></span><br><span class="line">    age: <span class="built_in">int</span></span><br><span class="line">    location: <span class="built_in">str</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@app.post(<span class="params"><span class="string">&quot;/predict&quot;</span></span>)</span></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">predict_price</span>(<span class="params">features: HouseFeatures</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;预测房价。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="comment"># 特征工程</span></span><br><span class="line">    location_encoded = label_encoder.transform([features.location])[<span class="number">0</span>]</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 构建特征向量</span></span><br><span class="line">    input_data = np.array([[</span><br><span class="line">        features.area,</span><br><span class="line">        features.bedrooms,</span><br><span class="line">        features.bathrooms,</span><br><span class="line">        features.age,</span><br><span class="line">        location_encoded</span><br><span class="line">    ]])</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 预测</span></span><br><span class="line">    input_scaled = scaler.transform(input_data)</span><br><span class="line">    predicted_price = model.predict(input_scaled)[<span class="number">0</span>]</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> &#123;</span><br><span class="line">        <span class="string">&quot;predicted_price&quot;</span>: <span class="built_in">round</span>(predicted_price, <span class="number">2</span>),</span><br><span class="line">        <span class="string">&quot;confidence&quot;</span>: <span class="string">&quot;medium&quot;</span>,</span><br><span class="line">        <span class="string">&quot;model&quot;</span>: best_model_name</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 运行：uvicorn house_price_api:app --reload</span></span><br></pre></td></tr></table></figure><hr><h3 id="10-2-大语言模型（LLM）应用开发"><a href="#10-2-大语言模型（LLM）应用开发" class="headerlink" title="10.2 大语言模型（LLM）应用开发"></a>10.2 大语言模型（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><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><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> openai</span><br><span class="line"><span class="keyword">from</span> typing <span class="keyword">import</span> <span class="type">List</span>, <span class="type">Dict</span></span><br><span class="line"><span class="keyword">import</span> json</span><br><span class="line"><span class="keyword">from</span> dataclasses <span class="keyword">import</span> dataclass</span><br><span class="line"><span class="keyword">from</span> enum <span class="keyword">import</span> Enum</span><br><span class="line"></span><br><span class="line"><span class="comment"># 配置 OpenAI API</span></span><br><span class="line">openai.api_key = <span class="string">&quot;your-api-key&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Intent</span>(<span class="title class_ inherited__">Enum</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;用户意图。&quot;&quot;&quot;</span></span><br><span class="line">    PRODUCT_INQUIRY = <span class="string">&quot;产品咨询&quot;</span></span><br><span class="line">    ORDER_STATUS = <span class="string">&quot;订单查询&quot;</span></span><br><span class="line">    REFUND_REQUEST = <span class="string">&quot;退款申请&quot;</span></span><br><span class="line">    TECH_SUPPORT = <span class="string">&quot;技术支持&quot;</span></span><br><span class="line">    GENERAL_QUESTION = <span class="string">&quot;一般问题&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@dataclass</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Message</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;对话消息。&quot;&quot;&quot;</span></span><br><span class="line">    role: <span class="built_in">str</span>  <span class="comment"># &#x27;user&#x27; / &#x27;assistant&#x27; / &#x27;system&#x27;</span></span><br><span class="line">    content: <span class="built_in">str</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">CustomerServiceBot</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;智能客服机器人。&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, model: <span class="built_in">str</span> = <span class="string">&quot;gpt-4&quot;</span></span>):</span><br><span class="line">        <span class="variable language_">self</span>.model = model</span><br><span class="line">        <span class="variable language_">self</span>.conversation_history: <span class="type">List</span>[<span class="type">Dict</span>] = []</span><br><span class="line">        <span class="variable language_">self</span>.system_prompt = <span class="string">&quot;&quot;&quot;你是一个专业的客服助手,负责回答用户关于产品、订单和技术支持的问题。</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">规则：</span></span><br><span class="line"><span class="string">1. 友好、专业地回答问题</span></span><br><span class="line"><span class="string">2. 如果涉及订单号、退款等敏感操作,请提醒用户提供相关信息</span></span><br><span class="line"><span class="string">3. 无法回答的问题,请建议联系人工客服</span></span><br><span class="line"><span class="string">4. 保持回答简洁明了&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">        <span class="variable language_">self</span>.conversation_history.append(&#123;</span><br><span class="line">            <span class="string">&quot;role&quot;</span>: <span class="string">&quot;system&quot;</span>,</span><br><span class="line">            <span class="string">&quot;content&quot;</span>: <span class="variable language_">self</span>.system_prompt</span><br><span class="line">        &#125;)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 知识库（实际应用中应从数据库加载）</span></span><br><span class="line">        <span class="variable language_">self</span>.knowledge_base = &#123;</span><br><span class="line">            <span class="string">&quot;退货政策&quot;</span>: <span class="string">&quot;购买后7天内可无理由退货,需保持商品完好。&quot;</span>,</span><br><span class="line">            <span class="string">&quot;发货时间&quot;</span>: <span class="string">&quot;订单支付成功后48小时内发货。&quot;</span>,</span><br><span class="line">            <span class="string">&quot;运费&quot;</span>: <span class="string">&quot;订单满99元包邮,否则运费10元。&quot;</span></span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">classify_intent</span>(<span class="params">self, user_message: <span class="built_in">str</span></span>) -&gt; Intent:</span><br><span class="line">        <span class="string">&quot;&quot;&quot;意图分类（使用 LLM）。&quot;&quot;&quot;</span></span><br><span class="line">        prompt = <span class="string">f&quot;&quot;&quot;分类以下用户消息的意图:</span></span><br><span class="line"><span class="string">消息: &quot;<span class="subst">&#123;user_message&#125;</span>&quot;</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">可能的意图:</span></span><br><span class="line"><span class="string">- PRODUCT_INQUIRY: 产品咨询（价格、规格等）</span></span><br><span class="line"><span class="string">- ORDER_STATUS: 订单查询（物流、状态）</span></span><br><span class="line"><span class="string">- REFUND_REQUEST: 退款申请</span></span><br><span class="line"><span class="string">- TECH_SUPPORT: 技术支持</span></span><br><span class="line"><span class="string">- GENERAL_QUESTION: 一般问题</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">只返回意图代码,不要其他内容。&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">        response = openai.ChatCompletion.create(</span><br><span class="line">            model=<span class="variable language_">self</span>.model,</span><br><span class="line">            messages=[&#123;<span class="string">&quot;role&quot;</span>: <span class="string">&quot;user&quot;</span>, <span class="string">&quot;content&quot;</span>: prompt&#125;],</span><br><span class="line">            temperature=<span class="number">0</span>,</span><br><span class="line">            max_tokens=<span class="number">20</span></span><br><span class="line">        )</span><br><span class="line"></span><br><span class="line">        intent_str = response.choices[<span class="number">0</span>].message.content.strip()</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 映射到枚举</span></span><br><span class="line">        intent_mapping = &#123;</span><br><span class="line">            <span class="string">&quot;PRODUCT_INQUIRY&quot;</span>: Intent.PRODUCT_INQUIRY,</span><br><span class="line">            <span class="string">&quot;ORDER_STATUS&quot;</span>: Intent.ORDER_STATUS,</span><br><span class="line">            <span class="string">&quot;REFUND_REQUEST&quot;</span>: Intent.REFUND_REQUEST,</span><br><span class="line">            <span class="string">&quot;TECH_SUPPORT&quot;</span>: Intent.TECH_SUPPORT,</span><br><span class="line">            <span class="string">&quot;GENERAL_QUESTION&quot;</span>: Intent.GENERAL_QUESTION</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> intent_mapping.get(intent_str, Intent.GENERAL_QUESTION)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">search_knowledge</span>(<span class="params">self, query: <span class="built_in">str</span></span>) -&gt; <span class="built_in">str</span>:</span><br><span class="line">        <span class="string">&quot;&quot;&quot;搜索知识库（简化版）。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">for</span> key, value <span class="keyword">in</span> <span class="variable language_">self</span>.knowledge_base.items():</span><br><span class="line">            <span class="keyword">if</span> key <span class="keyword">in</span> query:</span><br><span class="line">                <span class="keyword">return</span> value</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;未找到相关信息。&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">chat</span>(<span class="params">self, user_message: <span class="built_in">str</span></span>) -&gt; <span class="built_in">str</span>:</span><br><span class="line">        <span class="string">&quot;&quot;&quot;对话主流程。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="comment"># 意图分类</span></span><br><span class="line">        intent = <span class="variable language_">self</span>.classify_intent(user_message)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 添加用户消息</span></span><br><span class="line">        <span class="variable language_">self</span>.conversation_history.append(&#123;</span><br><span class="line">            <span class="string">&quot;role&quot;</span>: <span class="string">&quot;user&quot;</span>,</span><br><span class="line">            <span class="string">&quot;content&quot;</span>: user_message</span><br><span class="line">        &#125;)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 如果是知识库问题,优先返回知识库答案</span></span><br><span class="line">        knowledge_answer = <span class="variable language_">self</span>.search_knowledge(user_message)</span><br><span class="line">        <span class="keyword">if</span> knowledge_answer != <span class="string">&quot;未找到相关信息。&quot;</span>:</span><br><span class="line">            <span class="keyword">return</span> knowledge_answer</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 调用 LLM</span></span><br><span class="line">        response = openai.ChatCompletion.create(</span><br><span class="line">            model=<span class="variable language_">self</span>.model,</span><br><span class="line">            messages=<span class="variable language_">self</span>.conversation_history,</span><br><span class="line">            temperature=<span class="number">0.7</span>,</span><br><span class="line">            max_tokens=<span class="number">500</span></span><br><span class="line">        )</span><br><span class="line"></span><br><span class="line">        assistant_message = response.choices[<span class="number">0</span>].message.content</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 添加助手回复</span></span><br><span class="line">        <span class="variable language_">self</span>.conversation_history.append(&#123;</span><br><span class="line">            <span class="string">&quot;role&quot;</span>: <span class="string">&quot;assistant&quot;</span>,</span><br><span class="line">            <span class="string">&quot;content&quot;</span>: assistant_message</span><br><span class="line">        &#125;)</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> assistant_message</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">reset</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;重置对话。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="variable language_">self</span>.conversation_history = [</span><br><span class="line">            &#123;<span class="string">&quot;role&quot;</span>: <span class="string">&quot;system&quot;</span>, <span class="string">&quot;content&quot;</span>: <span class="variable language_">self</span>.system_prompt&#125;</span><br><span class="line">        ]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用示例</span></span><br><span class="line">bot = CustomerServiceBot()</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(bot.chat(<span class="string">&quot;你好,我想了解一下退货政策&quot;</span>))</span><br><span class="line"><span class="comment"># 输出: 购买后7天内可无理由退货,需保持商品完好。</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(bot.chat(<span class="string">&quot;那运费是多少呢？&quot;</span>))</span><br><span class="line"><span class="comment"># 输出: 订单满99元包邮,否则运费10元。</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(bot.chat(<span class="string">&quot;我的订单什么时候发货？&quot;</span>))</span><br><span class="line"><span class="comment"># 输出: 订单支付成功后48小时内发货。</span></span><br></pre></td></tr></table></figure><p><strong>LangChain 实现（更结构化）</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><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><span class="line"><span class="keyword">from</span> langchain.chat_models <span class="keyword">import</span> ChatOpenAI</span><br><span class="line"><span class="keyword">from</span> langchain.prompts <span class="keyword">import</span> ChatPromptTemplate</span><br><span class="line"><span class="keyword">from</span> langchain.chains <span class="keyword">import</span> LLMChain, ConversationalRetrievalChain</span><br><span class="line"><span class="keyword">from</span> langchain.memory <span class="keyword">import</span> ConversationBufferMemory</span><br><span class="line"><span class="keyword">from</span> langchain.vectorstores <span class="keyword">import</span> Chroma</span><br><span class="line"><span class="keyword">from</span> langchain.embeddings <span class="keyword">import</span> OpenAIEmbeddings</span><br><span class="line"><span class="keyword">from</span> langchain.document_loaders <span class="keyword">import</span> TextLoader</span><br><span class="line"><span class="keyword">from</span> langchain.text_splitter <span class="keyword">import</span> RecursiveCharacterTextSplitter</span><br><span class="line"></span><br><span class="line"><span class="comment"># 初始化 LLM</span></span><br><span class="line">llm = ChatOpenAI(model_name=<span class="string">&quot;gpt-4&quot;</span>, temperature=<span class="number">0.7</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 加载知识库</span></span><br><span class="line">loader = TextLoader(<span class="string">&quot;customer_service_manual.txt&quot;</span>)</span><br><span class="line">documents = loader.load()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 文档分割</span></span><br><span class="line">text_splitter = RecursiveCharacterTextSplitter(</span><br><span class="line">    chunk_size=<span class="number">1000</span>,</span><br><span class="line">    chunk_overlap=<span class="number">200</span></span><br><span class="line">)</span><br><span class="line">splits = text_splitter.split_documents(documents)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 构建向量数据库</span></span><br><span class="line">vectorstore = Chroma.from_documents(</span><br><span class="line">    documents=splits,</span><br><span class="line">    embedding=OpenAIEmbeddings()</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建对话链</span></span><br><span class="line">memory = ConversationBufferMemory(</span><br><span class="line">    memory_key=<span class="string">&quot;chat_history&quot;</span>,</span><br><span class="line">    return_messages=<span class="literal">True</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line">qa_chain = ConversationalRetrievalChain.from_llm(</span><br><span class="line">    llm=llm,</span><br><span class="line">    retriever=vectorstore.as_retriever(),</span><br><span class="line">    memory=memory,</span><br><span class="line">    return_source_documents=<span class="literal">True</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 对话</span></span><br><span class="line">response = qa_chain(&#123;<span class="string">&quot;question&quot;</span>: <span class="string">&quot;退货政策是什么？&quot;</span>&#125;)</span><br><span class="line"><span class="built_in">print</span>(response[<span class="string">&#x27;answer&#x27;</span>])</span><br><span class="line"></span><br><span class="line">response = qa_chain(&#123;<span class="string">&quot;question&quot;</span>: <span class="string">&quot;发货需要多久？&quot;</span>&#125;)</span><br><span class="line"><span class="built_in">print</span>(response[<span class="string">&#x27;answer&#x27;</span>])</span><br></pre></td></tr></table></figure><hr><h3 id="10-3-数据可视化与仪表盘"><a href="#10-3-数据可视化与仪表盘" class="headerlink" title="10.3 数据可视化与仪表盘"></a>10.3 数据可视化与仪表盘</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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> pandas <span class="keyword">as</span> pd</span><br><span class="line"><span class="keyword">import</span> plotly.express <span class="keyword">as</span> px</span><br><span class="line"><span class="keyword">import</span> plotly.graph_objects <span class="keyword">as</span> go</span><br><span class="line"><span class="keyword">from</span> plotly.subplots <span class="keyword">import</span> make_subplots</span><br><span class="line"><span class="keyword">import</span> dash</span><br><span class="line"><span class="keyword">from</span> dash <span class="keyword">import</span> dcc, html</span><br><span class="line"><span class="keyword">from</span> dash.dependencies <span class="keyword">import</span> Input, Output</span><br><span class="line"></span><br><span class="line"><span class="comment"># 数据准备（模拟）</span></span><br><span class="line">df = pd.DataFrame(&#123;</span><br><span class="line">    <span class="string">&#x27;date&#x27;</span>: pd.date_range(<span class="string">&#x27;2025-01-01&#x27;</span>, periods=<span class="number">90</span>, freq=<span class="string">&#x27;D&#x27;</span>),</span><br><span class="line">    <span class="string">&#x27;sales&#x27;</span>: np.random.randint(<span class="number">1000</span>, <span class="number">5000</span>, <span class="number">90</span>),</span><br><span class="line">    <span class="string">&#x27;category&#x27;</span>: np.random.choice([<span class="string">&#x27;Electronics&#x27;</span>, <span class="string">&#x27;Clothing&#x27;</span>, <span class="string">&#x27;Books&#x27;</span>], <span class="number">90</span>),</span><br><span class="line">    <span class="string">&#x27;region&#x27;</span>: np.random.choice([<span class="string">&#x27;North&#x27;</span>, <span class="string">&#x27;South&#x27;</span>, <span class="string">&#x27;East&#x27;</span>, <span class="string">&#x27;West&#x27;</span>], <span class="number">90</span>)</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line">df[<span class="string">&#x27;month&#x27;</span>] = df[<span class="string">&#x27;date&#x27;</span>].dt.to_period(<span class="string">&#x27;M&#x27;</span>).astype(<span class="built_in">str</span>)</span><br><span class="line">df[<span class="string">&#x27;week&#x27;</span>] = df[<span class="string">&#x27;date&#x27;</span>].dt.isocalendar().week</span><br><span class="line"></span><br><span class="line"><span class="comment"># Dash 应用</span></span><br><span class="line">app = dash.Dash(__name__)</span><br><span class="line"></span><br><span class="line">app.layout = html.Div([</span><br><span class="line">    html.H1(<span class="string">&quot;销售数据仪表盘&quot;</span>, style=&#123;<span class="string">&#x27;textAlign&#x27;</span>: <span class="string">&#x27;center&#x27;</span>&#125;),</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 控制面板</span></span><br><span class="line">    html.Div([</span><br><span class="line">        dcc.Dropdown(</span><br><span class="line">            <span class="built_in">id</span>=<span class="string">&#x27;category-dropdown&#x27;</span>,</span><br><span class="line">            options=[&#123;<span class="string">&#x27;label&#x27;</span>: cat, <span class="string">&#x27;value&#x27;</span>: cat&#125; <span class="keyword">for</span> cat <span class="keyword">in</span> df[<span class="string">&#x27;category&#x27;</span>].unique()],</span><br><span class="line">            value=<span class="string">&#x27;Electronics&#x27;</span>,</span><br><span class="line">            clearable=<span class="literal">False</span></span><br><span class="line">        ),</span><br><span class="line">        dcc.DatePickerRange(</span><br><span class="line">            <span class="built_in">id</span>=<span class="string">&#x27;date-range&#x27;</span>,</span><br><span class="line">            min_date_allowed=df[<span class="string">&#x27;date&#x27;</span>].<span class="built_in">min</span>(),</span><br><span class="line">            max_date_allowed=df[<span class="string">&#x27;date&#x27;</span>].<span class="built_in">max</span>(),</span><br><span class="line">            start_date=df[<span class="string">&#x27;date&#x27;</span>].<span class="built_in">min</span>(),</span><br><span class="line">            end_date=df[<span class="string">&#x27;date&#x27;</span>].<span class="built_in">max</span>()</span><br><span class="line">        )</span><br><span class="line">    ], style=&#123;<span class="string">&#x27;display&#x27;</span>: <span class="string">&#x27;flex&#x27;</span>, <span class="string">&#x27;gap&#x27;</span>: <span class="string">&#x27;20px&#x27;</span>, <span class="string">&#x27;padding&#x27;</span>: <span class="string">&#x27;20px&#x27;</span>&#125;),</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 图表区域</span></span><br><span class="line">    html.Div([</span><br><span class="line">        dcc.Graph(<span class="built_in">id</span>=<span class="string">&#x27;sales-trend&#x27;</span>),</span><br><span class="line">        dcc.Graph(<span class="built_in">id</span>=<span class="string">&#x27;sales-by-region&#x27;</span>),</span><br><span class="line">        dcc.Graph(<span class="built_in">id</span>=<span class="string">&#x27;sales-by-category&#x27;</span>)</span><br><span class="line">    ], style=&#123;<span class="string">&#x27;display&#x27;</span>: <span class="string">&#x27;grid&#x27;</span>, <span class="string">&#x27;gridTemplateColumns&#x27;</span>: <span class="string">&#x27;repeat(2, 1fr)&#x27;</span>, <span class="string">&#x27;gap&#x27;</span>: <span class="string">&#x27;20px&#x27;</span>&#125;)</span><br><span class="line">])</span><br><span class="line"></span><br><span class="line"><span class="meta">@app.callback(<span class="params"></span></span></span><br><span class="line"><span class="params"><span class="meta">    [Output(<span class="params"><span class="string">&#x27;sales-trend&#x27;</span>, <span class="string">&#x27;figure&#x27;</span></span>),</span></span></span><br><span class="line"><span class="params"><span class="meta">     Output(<span class="params"><span class="string">&#x27;sales-by-region&#x27;</span>, <span class="string">&#x27;figure&#x27;</span></span>),</span></span></span><br><span class="line"><span class="params"><span class="meta">     Output(<span class="params"><span class="string">&#x27;sales-by-category&#x27;</span>, <span class="string">&#x27;figure&#x27;</span></span>)],</span></span></span><br><span class="line"><span class="params"><span class="meta">    [Input(<span class="params"><span class="string">&#x27;category-dropdown&#x27;</span>, <span class="string">&#x27;value&#x27;</span></span>),</span></span></span><br><span class="line"><span class="params"><span class="meta">     Input(<span class="params"><span class="string">&#x27;date-range&#x27;</span>, <span class="string">&#x27;start_date&#x27;</span></span>),</span></span></span><br><span class="line"><span class="params"><span class="meta">     Input(<span class="params"><span class="string">&#x27;date-range&#x27;</span>, <span class="string">&#x27;end_date&#x27;</span></span>)]</span></span></span><br><span class="line"><span class="params"><span class="meta"></span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">update_charts</span>(<span class="params">category, start_date, end_date</span>):</span><br><span class="line">    <span class="comment"># 过滤数据</span></span><br><span class="line">    filtered_df = df[</span><br><span class="line">        (df[<span class="string">&#x27;category&#x27;</span>] == category) &amp;</span><br><span class="line">        (df[<span class="string">&#x27;date&#x27;</span>] &gt;= start_date) &amp;</span><br><span class="line">        (df[<span class="string">&#x27;date&#x27;</span>] &lt;= end_date)</span><br><span class="line">    ]</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 图表1: 销售趋势</span></span><br><span class="line">    trend_fig = px.line(</span><br><span class="line">        filtered_df.groupby(<span class="string">&#x27;date&#x27;</span>)[<span class="string">&#x27;sales&#x27;</span>].<span class="built_in">sum</span>().reset_index(),</span><br><span class="line">        x=<span class="string">&#x27;date&#x27;</span>,</span><br><span class="line">        y=<span class="string">&#x27;sales&#x27;</span>,</span><br><span class="line">        title=<span class="string">&#x27;销售趋势&#x27;</span></span><br><span class="line">    )</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 图表2: 区域分布</span></span><br><span class="line">    region_fig = px.bar(</span><br><span class="line">        filtered_df.groupby(<span class="string">&#x27;region&#x27;</span>)[<span class="string">&#x27;sales&#x27;</span>].<span class="built_in">sum</span>().reset_index(),</span><br><span class="line">        x=<span class="string">&#x27;region&#x27;</span>,</span><br><span class="line">        y=<span class="string">&#x27;sales&#x27;</span>,</span><br><span class="line">        title=<span class="string">&#x27;区域销售分布&#x27;</span>,</span><br><span class="line">        color=<span class="string">&#x27;region&#x27;</span></span><br><span class="line">    )</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 图表3: 类别占比</span></span><br><span class="line">    category_fig = px.pie(</span><br><span class="line">        filtered_df,</span><br><span class="line">        values=<span class="string">&#x27;sales&#x27;</span>,</span><br><span class="line">        names=<span class="string">&#x27;category&#x27;</span>,</span><br><span class="line">        title=<span class="string">&#x27;类别销售占比&#x27;</span></span><br><span class="line">    )</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> trend_fig, region_fig, category_fig</span><br><span class="line"></span><br><span class="line"><span class="comment"># 运行：python app.py</span></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&#x27;__main__&#x27;</span>:</span><br><span class="line">    app.run_server(debug=<span class="literal">True</span>)</span><br></pre></td></tr></table></figure><hr><h2 id="11-常见面试题汇总"><a href="#11-常见面试题汇总" class="headerlink" title="11. 常见面试题汇总"></a>11. 常见面试题汇总</h2><h3 id="11-1-Web-开发相关"><a href="#11-1-Web-开发相关" class="headerlink" title="11.1 Web 开发相关"></a>11.1 Web 开发相关</h3><p><strong>Q1: FastAPI 与 Flask&#x2F;Django 的主要区别？</strong></p><p><strong>答案要点</strong>：</p><table><thead><tr><th>特性</th><th>FastAPI</th><th>Flask</th><th>Django</th></tr></thead><tbody><tr><td>异步支持</td><td>原生支持</td><td>需要扩展</td><td>Django 3.0+ 支持</td></tr><tr><td>性能</td><td>高（异步）</td><td>中等</td><td>中等</td></tr><tr><td>自动文档</td><td>Swagger&#x2F;ReDoc 自动生成</td><td>需要手动</td><td>需要第三方库</td></tr><tr><td>学习曲线</td><td>中等</td><td>低</td><td>高</td></tr><tr><td>类型系统</td><td>Pydantic 强类型</td><td>无</td><td>弱类型</td></tr><tr><td>适用场景</td><td>高性能 API</td><td>小型项目</td><td>全功能网站</td></tr></tbody></table><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><span class="line"><span class="comment"># FastAPI 示例（自动生成文档）</span></span><br><span class="line"><span class="keyword">from</span> fastapi <span class="keyword">import</span> FastAPI</span><br><span class="line"><span class="keyword">from</span> pydantic <span class="keyword">import</span> BaseModel</span><br><span class="line"></span><br><span class="line">app = FastAPI()</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Item</span>(<span class="title class_ inherited__">BaseModel</span>):</span><br><span class="line">    name: <span class="built_in">str</span></span><br><span class="line">    price: <span class="built_in">float</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@app.post(<span class="params"><span class="string">&quot;/items/&quot;</span></span>)</span></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">create_item</span>(<span class="params">item: Item</span>):</span><br><span class="line">    <span class="keyword">return</span> item</span><br><span class="line"></span><br><span class="line"><span class="comment"># 访问 /docs 自动生成 Swagger UI</span></span><br></pre></td></tr></table></figure><hr><p><strong>Q2: 什么是 ORM？为什么使用 ORM？</strong></p><p><strong>答案要点</strong>：</p><ul><li>ORM（对象关系映射）：将数据库表映射为 Python 类</li><li>优势：<ol><li>不用写 SQL，提高开发效率</li><li>数据库无关性（切换数据库无需改代码）</li><li>自动处理关系、事务</li><li>代码更易维护和测试</li></ol></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><span class="line"><span class="comment"># SQLAlchemy ORM 示例</span></span><br><span class="line"><span class="keyword">from</span> sqlalchemy <span class="keyword">import</span> Column, Integer, String, ForeignKey</span><br><span class="line"><span class="keyword">from</span> sqlalchemy.ext.declarative <span class="keyword">import</span> declarative_base</span><br><span class="line"><span class="keyword">from</span> sqlalchemy.orm <span class="keyword">import</span> relationship</span><br><span class="line"></span><br><span class="line">Base = declarative_base()</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">User</span>(<span class="title class_ inherited__">Base</span>):</span><br><span class="line">    __tablename__ = <span class="string">&#x27;users&#x27;</span></span><br><span class="line"></span><br><span class="line">    <span class="built_in">id</span> = Column(Integer, primary_key=<span class="literal">True</span>)</span><br><span class="line">    name = Column(String(<span class="number">50</span>))</span><br><span class="line">    email = Column(String(<span class="number">100</span>), unique=<span class="literal">True</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 关系</span></span><br><span class="line">    posts = relationship(<span class="string">&quot;Post&quot;</span>, back_populates=<span class="string">&quot;author&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Post</span>(<span class="title class_ inherited__">Base</span>):</span><br><span class="line">    __tablename__ = <span class="string">&#x27;posts&#x27;</span></span><br><span class="line"></span><br><span class="line">    <span class="built_in">id</span> = Column(Integer, primary_key=<span class="literal">True</span>)</span><br><span class="line">    title = Column(String(<span class="number">100</span>))</span><br><span class="line">    author_id = Column(Integer, ForeignKey(<span class="string">&#x27;users.id&#x27;</span>))</span><br><span class="line"></span><br><span class="line">    author = relationship(<span class="string">&quot;User&quot;</span>, back_populates=<span class="string">&quot;posts&quot;</span>)</span><br></pre></td></tr></table></figure><hr><h3 id="11-2-数据科学相关"><a href="#11-2-数据科学相关" class="headerlink" title="11.2 数据科学相关"></a>11.2 数据科学相关</h3><p><strong>Q3: Pandas 中 <code>groupby</code> 的工作原理？</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><span class="line"><span class="keyword">import</span> pandas <span class="keyword">as</span> pd</span><br><span class="line"></span><br><span class="line">df = pd.DataFrame(&#123;</span><br><span class="line">    <span class="string">&#x27;category&#x27;</span>: [<span class="string">&#x27;A&#x27;</span>, <span class="string">&#x27;B&#x27;</span>, <span class="string">&#x27;A&#x27;</span>, <span class="string">&#x27;B&#x27;</span>, <span class="string">&#x27;A&#x27;</span>],</span><br><span class="line">    <span class="string">&#x27;value&#x27;</span>: [<span class="number">10</span>, <span class="number">20</span>, <span class="number">15</span>, <span class="number">25</span>, <span class="number">30</span>]</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="comment"># groupby 分三步：</span></span><br><span class="line"><span class="comment"># 1. Split: 按 category 分组</span></span><br><span class="line"><span class="comment"># 2. Apply: 对每组应用聚合函数</span></span><br><span class="line"><span class="comment"># 3. Combine: 合并结果</span></span><br><span class="line"></span><br><span class="line">result = df.groupby(<span class="string">&#x27;category&#x27;</span>)[<span class="string">&#x27;value&#x27;</span>].<span class="built_in">sum</span>()</span><br><span class="line"><span class="comment"># 输出:</span></span><br><span class="line"><span class="comment"># category</span></span><br><span class="line"><span class="comment"># A    55</span></span><br><span class="line"><span class="comment"># B    45</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 多级分组</span></span><br><span class="line">result = df.groupby([<span class="string">&#x27;category&#x27;</span>, <span class="string">&#x27;value&#x27;</span>]).size()</span><br></pre></td></tr></table></figure><hr><p><strong>Q4: 如何处理数据中的缺失值？</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><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><span class="line"><span class="keyword">import</span> pandas <span class="keyword">as</span> pd</span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"></span><br><span class="line">df = pd.DataFrame(&#123;</span><br><span class="line">    <span class="string">&#x27;A&#x27;</span>: [<span class="number">1</span>, <span class="number">2</span>, np.nan, <span class="number">4</span>],</span><br><span class="line">    <span class="string">&#x27;B&#x27;</span>: [<span class="number">5</span>, np.nan, np.nan, <span class="number">8</span>],</span><br><span class="line">    <span class="string">&#x27;C&#x27;</span>: [<span class="string">&#x27;a&#x27;</span>, <span class="string">&#x27;b&#x27;</span>, <span class="string">&#x27;c&#x27;</span>, np.nan]</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 方法1: 删除缺失值</span></span><br><span class="line">df_drop = df.dropna()  <span class="comment"># 删除含缺失值的行</span></span><br><span class="line">df_drop_cols = df.dropna(axis=<span class="number">1</span>)  <span class="comment"># 删除含缺失值的列</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 方法2: 填充缺失值</span></span><br><span class="line">df_fill_zero = df.fillna(<span class="number">0</span>)  <span class="comment"># 用 0 填充</span></span><br><span class="line">df_fill_mean = df[<span class="string">&#x27;A&#x27;</span>].fillna(df[<span class="string">&#x27;A&#x27;</span>].mean())  <span class="comment"># 用均值填充</span></span><br><span class="line">df_fill_median = df[<span class="string">&#x27;B&#x27;</span>].fillna(df[<span class="string">&#x27;B&#x27;</span>].median())  <span class="comment"># 用中位数填充</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 方法3: 插值</span></span><br><span class="line">df_interpolate = df.interpolate()  <span class="comment"># 线性插值</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 方法4: 前向/后向填充</span></span><br><span class="line">df_ffill = df.fillna(method=<span class="string">&#x27;ffill&#x27;</span>)  <span class="comment"># 前向填充</span></span><br><span class="line">df_bfill = df.fillna(method=<span class="string">&#x27;bfill&#x27;</span>)  <span class="comment"># 后向填充</span></span><br></pre></td></tr></table></figure><hr><h3 id="11-3-AI-ML-相关"><a href="#11-3-AI-ML-相关" class="headerlink" title="11.3 AI&#x2F;ML 相关"></a>11.3 AI&#x2F;ML 相关</h3><p><strong>Q5: 解释机器学习中的过拟合与欠拟合。</strong></p><p><strong>答案要点</strong>：</p><table><thead><tr><th>问题</th><th>定义</th><th>原因</th><th>解决方法</th></tr></thead><tbody><tr><td><strong>过拟合</strong></td><td>模型在训练集表现好,测试集差</td><td>模型太复杂、数据量少、训练时间长</td><td>正则化、Dropout、增加数据、早停</td></tr><tr><td><strong>欠拟合</strong></td><td>训练集和测试集都表现差</td><td>模型太简单、特征不足</td><td>增加特征、使用更复杂模型、减少正则化</td></tr></tbody></table><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><span class="line"><span class="keyword">from</span> sklearn.model_selection <span class="keyword">import</span> learning_curve</span><br><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"></span><br><span class="line"><span class="comment"># 学习曲线诊断</span></span><br><span class="line">train_sizes, train_scores, test_scores = learning_curve(</span><br><span class="line">    estimator=RandomForestClassifier(),</span><br><span class="line">    X=X_train,</span><br><span class="line">    y=y_train,</span><br><span class="line">    train_sizes=np.linspace(<span class="number">0.1</span>, <span class="number">1.0</span>, <span class="number">10</span>),</span><br><span class="line">    cv=<span class="number">5</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line">train_mean = np.mean(train_scores, axis=<span class="number">1</span>)</span><br><span class="line">test_mean = np.mean(test_scores, axis=<span class="number">1</span>)</span><br><span class="line"></span><br><span class="line">plt.plot(train_sizes, train_mean, label=<span class="string">&#x27;Training Score&#x27;</span>)</span><br><span class="line">plt.plot(train_sizes, test_mean, label=<span class="string">&#x27;Cross-validation Score&#x27;</span>)</span><br><span class="line">plt.xlabel(<span class="string">&#x27;Training Size&#x27;</span>)</span><br><span class="line">plt.ylabel(<span class="string">&#x27;Score&#x27;</span>)</span><br><span class="line">plt.legend()</span><br><span class="line">plt.show()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 过拟合: Training Score 高, Cross-validation Score 低</span></span><br><span class="line"><span class="comment"># 欠拟合: 两者都低</span></span><br></pre></td></tr></table></figure><hr><p><strong>Q6: 什么是交叉验证？为什么使用？</strong></p><p><strong>答案要点</strong>：</p><ul><li>目的：更准确评估模型性能，避免过拟合</li><li>原理：将数据分成 K 份，轮流用 K-1 份训练，1 份验证，重复 K 次</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> sklearn.model_selection <span class="keyword">import</span> cross_val_score, KFold</span><br><span class="line"><span class="keyword">from</span> sklearn.ensemble <span class="keyword">import</span> RandomForestClassifier</span><br><span class="line"></span><br><span class="line"><span class="comment"># K 折交叉验证</span></span><br><span class="line">kfold = KFold(n_splits=<span class="number">5</span>, shuffle=<span class="literal">True</span>, random_state=<span class="number">42</span>)</span><br><span class="line">model = RandomForestClassifier()</span><br><span class="line"></span><br><span class="line">scores = cross_val_score(model, X, y, cv=kfold, scoring=<span class="string">&#x27;accuracy&#x27;</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;准确率: <span class="subst">&#123;scores.mean():<span class="number">.3</span>f&#125;</span> (+/- <span class="subst">&#123;scores.std():<span class="number">.3</span>f&#125;</span>)&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 分层 K 折（保持类别比例）</span></span><br><span class="line"><span class="keyword">from</span> sklearn.model_selection <span class="keyword">import</span> StratifiedKFold</span><br><span class="line"></span><br><span class="line">skfold = StratifiedKFold(n_splits=<span class="number">5</span>, shuffle=<span class="literal">True</span>, random_state=<span class="number">42</span>)</span><br><span class="line">scores = cross_val_score(model, X, y, cv=skfold)</span><br></pre></td></tr></table></figure><hr><h3 id="11-4-爬虫相关"><a href="#11-4-爬虫相关" class="headerlink" title="11.4 爬虫相关"></a>11.4 爬虫相关</h3><p><strong>Q7: 如何处理动态加载的网页？</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><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><span class="line"><span class="comment"># 方法1: Selenium</span></span><br><span class="line"><span class="keyword">from</span> selenium <span class="keyword">import</span> webdriver</span><br><span class="line"><span class="keyword">from</span> selenium.webdriver.common.by <span class="keyword">import</span> By</span><br><span class="line"><span class="keyword">from</span> selenium.webdriver.support.ui <span class="keyword">import</span> WebDriverWait</span><br><span class="line"><span class="keyword">from</span> selenium.webdriver.support <span class="keyword">import</span> expected_conditions <span class="keyword">as</span> EC</span><br><span class="line"></span><br><span class="line">driver = webdriver.Chrome()</span><br><span class="line">driver.get(<span class="string">&quot;https://example.com&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 等待元素加载</span></span><br><span class="line">element = WebDriverWait(driver, <span class="number">10</span>).until(</span><br><span class="line">    EC.presence_of_element_located((By.CLASS_NAME, <span class="string">&quot;dynamic-content&quot;</span>))</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line">content = element.text</span><br><span class="line">driver.quit()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 方法2: Playwright（推荐）</span></span><br><span class="line"><span class="keyword">from</span> playwright.sync_api <span class="keyword">import</span> sync_playwright</span><br><span class="line"></span><br><span class="line"><span class="keyword">with</span> sync_playwright() <span class="keyword">as</span> p:</span><br><span class="line">    browser = p.chromium.launch(headless=<span class="literal">True</span>)</span><br><span class="line">    page = browser.new_page()</span><br><span class="line">    page.goto(<span class="string">&quot;https://example.com&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 等待动态内容</span></span><br><span class="line">    page.wait_for_selector(<span class="string">&quot;.dynamic-content&quot;</span>)</span><br><span class="line">    content = page.text_content(<span class="string">&quot;.dynamic-content&quot;</span>)</span><br><span class="line"></span><br><span class="line">    browser.close()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 方法3: 分析 API 请求（最快）</span></span><br><span class="line"><span class="keyword">import</span> requests</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用浏览器开发者工具找到真实的 API 端点</span></span><br><span class="line">api_url = <span class="string">&quot;https://example.com/api/data&quot;</span></span><br><span class="line">response = requests.get(api_url)</span><br><span class="line">data = response.json()</span><br></pre></td></tr></table></figure><hr><h3 id="11-5-DevOps-相关"><a href="#11-5-DevOps-相关" class="headerlink" title="11.5 DevOps 相关"></a>11.5 DevOps 相关</h3><p><strong>Q8: 解释 Docker 的核心概念。</strong></p><p><strong>答案要点</strong>：</p><ul><li><strong>镜像（Image）</strong>：只读模板，包含运行应用所需的一切（代码、依赖、配置）</li><li><strong>容器（Container）</strong>：镜像的运行实例，相互隔离</li><li><strong>Dockerfile</strong>：构建镜像的脚本</li><li><strong>仓库（Registry）</strong>：存储和分发镜像（如 Docker Hub）</li></ul><figure class="highlight dockerfile"><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><span class="line"><span class="comment"># Dockerfile 示例</span></span><br><span class="line"><span class="keyword">FROM</span> python:<span class="number">3.11</span>-slim</span><br><span class="line"></span><br><span class="line"><span class="keyword">WORKDIR</span><span class="language-bash"> /app</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">COPY</span><span class="language-bash"> requirements.txt .</span></span><br><span class="line"><span class="keyword">RUN</span><span class="language-bash"> pip install --no-cache-dir -r requirements.txt</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">COPY</span><span class="language-bash"> . .</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">CMD</span><span class="language-bash"> [<span class="string">&quot;python&quot;</span>, <span class="string">&quot;app.py&quot;</span>]</span></span><br></pre></td></tr></table></figure><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><span class="line"><span class="comment"># 常用命令</span></span><br><span class="line">docker build -t myapp:1.0 .    <span class="comment"># 构建镜像</span></span><br><span class="line">docker run -p 8000:8000 myapp  <span class="comment"># 运行容器</span></span><br><span class="line">docker ps                       <span class="comment"># 查看运行中的容器</span></span><br><span class="line">docker logs &lt;container_id&gt;      <span class="comment"># 查看日志</span></span><br><span class="line">docker <span class="built_in">exec</span> -it &lt;container_id&gt; bash  <span class="comment"># 进入容器</span></span><br></pre></td></tr></table></figure><hr><h2 id="12-实战项目：多领域综合项目"><a href="#12-实战项目：多领域综合项目" class="headerlink" title="12. 实战项目：多领域综合项目"></a>12. 实战项目：多领域综合项目</h2><h3 id="12-1-项目概述"><a href="#12-1-项目概述" class="headerlink" title="12.1 项目概述"></a>12.1 项目概述</h3><p><strong>目标</strong>：构建一个完整的 AI 驱动的股票分析系统，涵盖数据采集、分析、预测和可视化。</p><p><strong>技术栈</strong>：</p><ul><li>数据采集：yfinance, BeautifulSoup</li><li>数据分析：Pandas, NumPy</li><li>机器学习：Scikit-learn, PyTorch</li><li>可视化：Plotly, Dash</li><li>Web 界面：FastAPI</li><li>部署：Docker</li></ul><h3 id="12-2-项目架构"><a href="#12-2-项目架构" class="headerlink" title="12.2 项目架构"></a>12.2 项目架构</h3><figure class="highlight plaintext"><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><span class="line">stock_analyzer/</span><br><span class="line">├── src/</span><br><span class="line">│   ├── data/</span><br><span class="line">│   │   ├── collector.py      # 数据采集</span><br><span class="line">│   │   └── preprocessor.py   # 数据预处理</span><br><span class="line">│   ├── models/</span><br><span class="line">│   │   ├── predictor.py      # 预测模型</span><br><span class="line">│   │   └── trainer.py        # 模型训练</span><br><span class="line">│   ├── api/</span><br><span class="line">│   │   └── main.py           # FastAPI 接口</span><br><span class="line">│   └── dashboard/</span><br><span class="line">│       └── app.py            # Dash 仪表盘</span><br><span class="line">├── tests/</span><br><span class="line">├── requirements.txt</span><br><span class="line">├── Dockerfile</span><br><span class="line">└── docker-compose.yml</span><br></pre></td></tr></table></figure><h3 id="12-3-核心代码片段"><a href="#12-3-核心代码片段" class="headerlink" title="12.3 核心代码片段"></a>12.3 核心代码片段</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><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># src/data/collector.py</span></span><br><span class="line"><span class="keyword">import</span> yfinance <span class="keyword">as</span> yf</span><br><span class="line"><span class="keyword">import</span> pandas <span class="keyword">as</span> pd</span><br><span class="line"><span class="keyword">from</span> typing <span class="keyword">import</span> <span class="type">List</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">StockDataCollector</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;股票数据采集器。&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, symbols: <span class="type">List</span>[<span class="built_in">str</span>]</span>):</span><br><span class="line">        <span class="variable language_">self</span>.symbols = symbols</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">fetch_historical_data</span>(<span class="params">self, period: <span class="built_in">str</span> = <span class="string">&quot;1y&quot;</span></span>) -&gt; pd.DataFrame:</span><br><span class="line">        <span class="string">&quot;&quot;&quot;获取历史数据。&quot;&quot;&quot;</span></span><br><span class="line">        data = yf.download(<span class="variable language_">self</span>.symbols, period=period, group_by=<span class="string">&#x27;ticker&#x27;</span>)</span><br><span class="line"></span><br><span class="line">        all_data = []</span><br><span class="line">        <span class="keyword">for</span> symbol <span class="keyword">in</span> <span class="variable language_">self</span>.symbols:</span><br><span class="line">            df = data[symbol].copy()</span><br><span class="line">            df[<span class="string">&#x27;symbol&#x27;</span>] = symbol</span><br><span class="line">            df[<span class="string">&#x27;date&#x27;</span>] = df.index</span><br><span class="line">            all_data.append(df)</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> pd.concat(all_data, ignore_index=<span class="literal">True</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">fetch_realtime</span>(<span class="params">self, symbol: <span class="built_in">str</span></span>) -&gt; <span class="built_in">dict</span>:</span><br><span class="line">        <span class="string">&quot;&quot;&quot;获取实时数据。&quot;&quot;&quot;</span></span><br><span class="line">        stock = yf.Ticker(symbol)</span><br><span class="line">        info = stock.info</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> &#123;</span><br><span class="line">            <span class="string">&#x27;symbol&#x27;</span>: symbol,</span><br><span class="line">            <span class="string">&#x27;price&#x27;</span>: info.get(<span class="string">&#x27;currentPrice&#x27;</span>),</span><br><span class="line">            <span class="string">&#x27;change&#x27;</span>: info.get(<span class="string">&#x27;regularMarketChange&#x27;</span>),</span><br><span class="line">            <span class="string">&#x27;change_percent&#x27;</span>: info.get(<span class="string">&#x27;regularMarketChangePercent&#x27;</span>),</span><br><span class="line">            <span class="string">&#x27;volume&#x27;</span>: info.get(<span class="string">&#x27;regularMarketVolume&#x27;</span>),</span><br><span class="line">            <span class="string">&#x27;market_cap&#x27;</span>: info.get(<span class="string">&#x27;marketCap&#x27;</span>)</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用</span></span><br><span class="line">collector = StockDataCollector([<span class="string">&#x27;AAPL&#x27;</span>, <span class="string">&#x27;GOOGL&#x27;</span>, <span class="string">&#x27;MSFT&#x27;</span>])</span><br><span class="line">df = collector.fetch_historical_data(period=<span class="string">&quot;2y&quot;</span>)</span><br></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><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># src/models/predictor.py</span></span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"><span class="keyword">import</span> pandas <span class="keyword">as</span> pd</span><br><span class="line"><span class="keyword">from</span> sklearn.preprocessing <span class="keyword">import</span> MinMaxScaler</span><br><span class="line"><span class="keyword">from</span> tensorflow.keras.models <span class="keyword">import</span> Sequential</span><br><span class="line"><span class="keyword">from</span> tensorflow.keras.layers <span class="keyword">import</span> LSTM, Dense, Dropout</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">StockPricePredictor</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;股票价格预测器（LSTM）。&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, sequence_length: <span class="built_in">int</span> = <span class="number">60</span></span>):</span><br><span class="line">        <span class="variable language_">self</span>.sequence_length = sequence_length</span><br><span class="line">        <span class="variable language_">self</span>.scaler = MinMaxScaler(feature_range=(<span class="number">0</span>, <span class="number">1</span>))</span><br><span class="line">        <span class="variable language_">self</span>.model = <span class="literal">None</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">prepare_data</span>(<span class="params">self, prices: pd.Series</span>) -&gt; <span class="built_in">tuple</span>:</span><br><span class="line">        <span class="string">&quot;&quot;&quot;准备训练数据。&quot;&quot;&quot;</span></span><br><span class="line">        scaled_data = <span class="variable language_">self</span>.scaler.fit_transform(prices.values.reshape(-<span class="number">1</span>, <span class="number">1</span>))</span><br><span class="line"></span><br><span class="line">        X, y = [], []</span><br><span class="line">        <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="variable language_">self</span>.sequence_length, <span class="built_in">len</span>(scaled_data)):</span><br><span class="line">            X.append(scaled_data[i-<span class="variable language_">self</span>.sequence_length:i])</span><br><span class="line">            y.append(scaled_data[i])</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> np.array(X), np.array(y)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">build_model</span>(<span class="params">self, input_shape: <span class="built_in">tuple</span></span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;构建 LSTM 模型。&quot;&quot;&quot;</span></span><br><span class="line">        model = Sequential([</span><br><span class="line">            LSTM(<span class="number">50</span>, return_sequences=<span class="literal">True</span>, input_shape=input_shape),</span><br><span class="line">            Dropout(<span class="number">0.2</span>),</span><br><span class="line">            LSTM(<span class="number">50</span>, return_sequences=<span class="literal">False</span>),</span><br><span class="line">            Dropout(<span class="number">0.2</span>),</span><br><span class="line">            Dense(<span class="number">25</span>),</span><br><span class="line">            Dense(<span class="number">1</span>)</span><br><span class="line">        ])</span><br><span class="line"></span><br><span class="line">        model.<span class="built_in">compile</span>(optimizer=<span class="string">&#x27;adam&#x27;</span>, loss=<span class="string">&#x27;mean_squared_error&#x27;</span>)</span><br><span class="line">        <span class="variable language_">self</span>.model = model</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">train</span>(<span class="params">self, X_train, y_train, epochs: <span class="built_in">int</span> = <span class="number">50</span></span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;训练模型。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="variable language_">self</span>.model.fit(X_train, y_train, epochs=epochs, batch_size=<span class="number">32</span>, verbose=<span class="number">1</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">predict</span>(<span class="params">self, recent_prices: pd.Series</span>) -&gt; <span class="built_in">float</span>:</span><br><span class="line">        <span class="string">&quot;&quot;&quot;预测下一天价格。&quot;&quot;&quot;</span></span><br><span class="line">        scaled = <span class="variable language_">self</span>.scaler.transform(recent_prices.values.reshape(-<span class="number">1</span>, <span class="number">1</span>))</span><br><span class="line">        X = scaled[-<span class="variable language_">self</span>.sequence_length:].reshape(<span class="number">1</span>, <span class="variable language_">self</span>.sequence_length, <span class="number">1</span>)</span><br><span class="line">        prediction = <span class="variable language_">self</span>.model.predict(X)</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>.scaler.inverse_transform(prediction)[<span class="number">0</span>][<span class="number">0</span>]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用</span></span><br><span class="line">predictor = StockPricePredictor(sequence_length=<span class="number">60</span>)</span><br><span class="line">X, y = predictor.prepare_data(df[<span class="string">&#x27;Close&#x27;</span>])</span><br><span class="line">predictor.build_model((X.shape[<span class="number">1</span>], <span class="number">1</span>))</span><br><span class="line">predictor.train(X, y, epochs=<span class="number">10</span>)</span><br><span class="line">next_price = predictor.predict(df[<span class="string">&#x27;Close&#x27;</span>][-<span class="number">100</span>:])</span><br></pre></td></tr></table></figure><hr><blockquote><p><strong>工程师寄语</strong>：Python 的强大在于它的”通用性”。但成为优秀的工程师，需要在某个领域深入扎根。选择一个你感兴趣的方向，从”能运行”到”能生产”，从”会写代码”到”会设计系统”。领域知识 + Python 技能 &#x3D; 不可替代性。</p></blockquote><blockquote><p><strong>最终建议</strong>：</p><ol><li>不要贪多，选择一个方向深入</li><li>做项目！项目经验比理论更重要</li><li>持续学习，技术在不断演进</li><li>加入社区，分享知识，帮助他人</li><li>保持好奇心，享受编程的乐趣</li></ol></blockquote>]]>
    </content>
    <id>https://itdxb.cn/2026/07/24/python/document/%E3%80%90Python%E3%80%91%E4%B8%BB%E6%B5%81%E5%BA%94%E7%94%A8%E9%A2%86%E5%9F%9F%EF%BC%88%E7%AC%AC%E5%8D%81%E9%98%B6%E6%AE%B5%EF%BC%89/</id>
    <link href="https://itdxb.cn/2026/07/24/python/document/%E3%80%90Python%E3%80%91%E4%B8%BB%E6%B5%81%E5%BA%94%E7%94%A8%E9%A2%86%E5%9F%9F%EF%BC%88%E7%AC%AC%E5%8D%81%E9%98%B6%E6%AE%B5%EF%BC%89/"/>
    <published>2026-07-24T02:00:00.000Z</published>
    <summary>Python 主流应用领域全景，涵盖 Web 开发、数据科学、爬虫、AI/ML、自动化运维等方向，帮助你建立领域地图并选择深入方向。</summary>
    <title>【Python】主流应用领域（第十阶段）</title>
    <updated>2026-07-24T06:26:39.590Z</updated>
  </entry>
  <entry>
    <author>
      <name>Super Bing</name>
    </author>
    <category term="Python" scheme="https://itdxb.cn/categories/Python/"/>
    <category term="教程" scheme="https://itdxb.cn/tags/%E6%95%99%E7%A8%8B/"/>
    <category term="Python" scheme="https://itdxb.cn/tags/Python/"/>
    <category term="工程化" scheme="https://itdxb.cn/tags/%E5%B7%A5%E7%A8%8B%E5%8C%96/"/>
    <category term="测试" scheme="https://itdxb.cn/tags/%E6%B5%8B%E8%AF%95/"/>
    <category term="CI/CD" scheme="https://itdxb.cn/tags/CI-CD/"/>
    <content>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><h1 id="九、工程化与代码质量"><a href="#九、工程化与代码质量" class="headerlink" title="九、工程化与代码质量"></a>九、工程化与代码质量</h1><blockquote><p><strong>阶段定位</strong>：写代码容易，写好代码难。工程化是将个人编码习惯升级为团队协作规范的过程。本阶段从编码规范出发，覆盖测试策略、性能优化、CI&#x2F;CD 和项目发布，帮你建立”可维护、可协作、可交付”的工程思维。</p></blockquote><figure class="highlight plaintext"><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><span class="line">┌──────────────────────────────────────────────────────────────────┐</span><br><span class="line">│                  Python 工程化知识图谱                            │</span><br><span class="line">├──────────────────────────────────────────────────────────────────┤</span><br><span class="line">│                                                                  │</span><br><span class="line">│  编码规范                                                        │</span><br><span class="line">│    ├── PEP 8 风格指南                                           │</span><br><span class="line">│    ├── 命名规范（模块/类/函数/常量）                              │</span><br><span class="line">│    └── 文档字符串（Google/Numpy 风格）                           │</span><br><span class="line">│                                                                  │</span><br><span class="line">│  代码质量工具                                                    │</span><br><span class="line">│    ├── black（格式化）                                          │</span><br><span class="line">│    ├── isort（导入排序）                                        │</span><br><span class="line">│    ├── flake8 / pylint（代码检查）                              │</span><br><span class="line">│    ├── mypy（类型检查）                                         │</span><br><span class="line">│    └── pre-commit（Git 钩子）                                   │</span><br><span class="line">│                                                                  │</span><br><span class="line">│  测试策略                                                        │</span><br><span class="line">│    ├── 单元测试（pytest）                                       │</span><br><span class="line">│    ├── 集成测试                                                  │</span><br><span class="line">│    ├── Mock 与依赖隔离                                          │</span><br><span class="line">│    └── 测试覆盖率（pytest-cov）                                 │</span><br><span class="line">│                                                                  │</span><br><span class="line">│  性能优化                                                        │</span><br><span class="line">│    ├── cProfile（性能分析）                                     │</span><br><span class="line">│    ├── 优化技巧（推导式/生成器/内置函数）                         │</span><br><span class="line">│    └── Numba / Cython（JIT 编译）                               │</span><br><span class="line">│                                                                  │</span><br><span class="line">│  CI/CD                                                           │</span><br><span class="line">│    ├── GitHub Actions                                           │</span><br><span class="line">│    ├── tox（多环境测试）                                        │</span><br><span class="line">│    └── 自动化流水线                                              │</span><br><span class="line">│                                                                  │</span><br><span class="line">│  打包发布                                                        │</span><br><span class="line">│    ├── pyproject.toml                                           │</span><br><span class="line">│    ├── 构建与发布（build / twine）                              │</span><br><span class="line">│    └── Docker 容器化                                             │</span><br><span class="line">│                                                                  │</span><br><span class="line">└──────────────────────────────────────────────────────────────────┘</span><br></pre></td></tr></table></figure><hr><h2 id="1-编码规范与文档"><a href="#1-编码规范与文档" class="headerlink" title="1. 编码规范与文档"></a>1. 编码规范与文档</h2><h3 id="1-1-PEP-8-核心规则"><a href="#1-1-PEP-8-核心规则" class="headerlink" title="1.1 PEP 8 核心规则"></a>1.1 PEP 8 核心规则</h3><p>PEP 8 是 Python 的官方编码风格指南，遵循它不是束缚，而是让代码”像 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><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><span class="line"><span class="comment"># 命名规范</span></span><br><span class="line">module_name.py           <span class="comment"># 模块：小写 + 下划线</span></span><br><span class="line">package_name             <span class="comment"># 包：小写，简短</span></span><br><span class="line">ClassName                <span class="comment"># 类：大驼峰</span></span><br><span class="line">function_name            <span class="comment"># 函数/变量：小写 + 下划线</span></span><br><span class="line">CONSTANT_NAME            <span class="comment"># 常量：大写 + 下划线</span></span><br><span class="line">_private                 <span class="comment"># 内部使用：单下划线前缀</span></span><br><span class="line">__mangled                <span class="comment"># 避免子类冲突：双下划线前缀</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 空白规范</span></span><br><span class="line"><span class="comment"># 运算符两侧空格</span></span><br><span class="line">x = <span class="number">1</span> + <span class="number">2</span></span><br><span class="line"><span class="comment"># 函数参数默认值不加空格</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">func</span>(<span class="params">a, b=<span class="number">1</span></span>):</span><br><span class="line">    <span class="keyword">pass</span></span><br><span class="line"><span class="comment"># 切片内部不加空格</span></span><br><span class="line">items[<span class="number">1</span>:<span class="number">3</span>]</span><br><span class="line"><span class="comment"># 逗号后空格</span></span><br><span class="line">items = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 行长：79 字符（传统）或 88 字符（Black 默认）</span></span><br><span class="line"><span class="comment"># 续行：括号内隐式续行优先于反斜杠</span></span><br><span class="line">result = some_function(</span><br><span class="line">    arg1, arg2, arg3,</span><br><span class="line">    arg4, arg5,</span><br><span class="line">)</span><br><span class="line"><span class="comment"># 而非</span></span><br><span class="line"><span class="comment"># result = some_function(arg1, arg2, arg3, \</span></span><br><span class="line"><span class="comment">#                        arg4, arg5)</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 导入规范：标准库 &gt; 第三方 &gt; 本地，每组空一行</span></span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"><span class="keyword">import</span> sys</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> requests</span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> myproject <span class="keyword">import</span> utils</span><br><span class="line"><span class="keyword">from</span> myproject.core <span class="keyword">import</span> models</span><br></pre></td></tr></table></figure><p><strong>PEP 8 命名规范速查</strong>：</p><table><thead><tr><th>类型</th><th>命名风格</th><th>示例</th></tr></thead><tbody><tr><td>模块</td><td>小写 + 下划线</td><td><code>my_module.py</code></td></tr><tr><td>包</td><td>小写，简短</td><td><code>mypackage</code></td></tr><tr><td>类</td><td>大驼峰</td><td><code>MyClass</code></td></tr><tr><td>函数</td><td>小写 + 下划线</td><td><code>my_function</code></td></tr><tr><td>变量</td><td>小写 + 下划线</td><td><code>my_variable</code></td></tr><tr><td>常量</td><td>大写 + 下划线</td><td><code>MY_CONSTANT</code></td></tr><tr><td>私有属性</td><td>单下划线前缀</td><td><code>_private_attr</code></td></tr><tr><td>名称改写</td><td>双下划线前缀</td><td><code>__mangled</code></td></tr></tbody></table><h3 id="1-2-代码质量工具链"><a href="#1-2-代码质量工具链" class="headerlink" title="1.2 代码质量工具链"></a>1.2 代码质量工具链</h3><p>现代 Python 项目使用自动化工具保证代码质量：</p><table><thead><tr><th>工具</th><th>用途</th><th>命令</th></tr></thead><tbody><tr><td><code>black</code></td><td>代码格式化（opinionated）</td><td><code>black src/</code></td></tr><tr><td><code>isort</code></td><td>导入排序</td><td><code>isort src/</code></td></tr><tr><td><code>flake8</code></td><td>风格检查</td><td><code>flake8 src/</code></td></tr><tr><td><code>pylint</code></td><td>深度代码分析</td><td><code>pylint src/</code></td></tr><tr><td><code>mypy</code></td><td>静态类型检查</td><td><code>mypy src/</code></td></tr><tr><td><code>bandit</code></td><td>安全检查</td><td><code>bandit -r src/</code></td></tr><tr><td><code>pydocstyle</code></td><td>文档字符串规范</td><td><code>pydocstyle src/</code></td></tr></tbody></table><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><span class="line"><span class="comment"># pyproject.toml 配置示例</span></span><br><span class="line">[tool.black]</span><br><span class="line">line-length = <span class="number">88</span></span><br><span class="line">target-version = [<span class="string">&#x27;py39&#x27;</span>]</span><br><span class="line"></span><br><span class="line">[tool.isort]</span><br><span class="line">profile = <span class="string">&quot;black&quot;</span></span><br><span class="line">line_length = <span class="number">88</span></span><br><span class="line"></span><br><span class="line">[tool.mypy]</span><br><span class="line">python_version = <span class="string">&quot;3.9&quot;</span></span><br><span class="line">warn_return_any = true</span><br><span class="line">warn_unused_configs = true</span><br><span class="line">disallow_untyped_defs = true</span><br><span class="line"></span><br><span class="line">[tool.flake8]</span><br><span class="line"><span class="built_in">max</span>-line-length = <span class="number">88</span></span><br><span class="line">extend-ignore = [<span class="string">&quot;E203&quot;</span>, <span class="string">&quot;W503&quot;</span>]</span><br></pre></td></tr></table></figure><p><strong>工具链工作流程</strong>：</p><figure class="highlight plaintext"><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><span class="line">代码提交</span><br><span class="line">    │</span><br><span class="line">    ▼</span><br><span class="line">pre-commit 钩子</span><br><span class="line">    │</span><br><span class="line">    ├── black（格式化）─────────────┐</span><br><span class="line">    │                              │</span><br><span class="line">    ├── isort（导入排序）───────────┤</span><br><span class="line">    │                              │</span><br><span class="line">    ├── flake8（风格检查）──────────┤──→ 通过 ✓</span><br><span class="line">    │                              │</span><br><span class="line">    └── mypy（类型检查）────────────┘</span><br><span class="line">                                   │</span><br><span class="line">                                   ▼</span><br><span class="line">                              提交成功</span><br></pre></td></tr></table></figure><h3 id="1-3-pre-commit-钩子"><a href="#1-3-pre-commit-钩子" class="headerlink" title="1.3 pre-commit 钩子"></a>1.3 pre-commit 钩子</h3><p>pre-commit 在每次 git commit 前自动运行代码检查，确保代码质量。</p><figure class="highlight yaml"><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><span class="line"><span class="comment"># .pre-commit-config.yaml</span></span><br><span class="line"><span class="attr">repos:</span></span><br><span class="line">  <span class="bullet">-</span> <span class="attr">repo:</span> <span class="string">https://github.com/pre-commit/pre-commit-hooks</span></span><br><span class="line">    <span class="attr">rev:</span> <span class="string">v4.5.0</span></span><br><span class="line">    <span class="attr">hooks:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">id:</span> <span class="string">trailing-whitespace</span>    <span class="comment"># 删除行尾空白</span></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">id:</span> <span class="string">end-of-file-fixer</span>      <span class="comment"># 文件末尾换行</span></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">id:</span> <span class="string">check-yaml</span>             <span class="comment"># YAML 语法检查</span></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">id:</span> <span class="string">check-added-large-files</span> <span class="comment"># 大文件检查</span></span><br><span class="line"></span><br><span class="line">  <span class="bullet">-</span> <span class="attr">repo:</span> <span class="string">https://github.com/psf/black</span></span><br><span class="line">    <span class="attr">rev:</span> <span class="number">23.12</span><span class="number">.1</span></span><br><span class="line">    <span class="attr">hooks:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">id:</span> <span class="string">black</span></span><br><span class="line"></span><br><span class="line">  <span class="bullet">-</span> <span class="attr">repo:</span> <span class="string">https://github.com/PyCQA/isort</span></span><br><span class="line">    <span class="attr">rev:</span> <span class="number">5.13</span><span class="number">.2</span></span><br><span class="line">    <span class="attr">hooks:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">id:</span> <span class="string">isort</span></span><br><span class="line"></span><br><span class="line">  <span class="bullet">-</span> <span class="attr">repo:</span> <span class="string">https://github.com/PyCQA/flake8</span></span><br><span class="line">    <span class="attr">rev:</span> <span class="number">7.0</span><span class="number">.0</span></span><br><span class="line">    <span class="attr">hooks:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">id:</span> <span class="string">flake8</span></span><br><span class="line"></span><br><span class="line">  <span class="bullet">-</span> <span class="attr">repo:</span> <span class="string">https://github.com/pre-commit/mirrors-mypy</span></span><br><span class="line">    <span class="attr">rev:</span> <span class="string">v1.8.0</span></span><br><span class="line">    <span class="attr">hooks:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">id:</span> <span class="string">mypy</span></span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 安装 pre-commit</span></span><br><span class="line">pip install pre-commit</span><br><span class="line">pre-commit install    <span class="comment"># 安装到 git hooks</span></span><br><span class="line">pre-commit run --all-files   <span class="comment"># 手动运行所有检查</span></span><br></pre></td></tr></table></figure><h3 id="1-4-文档规范"><a href="#1-4-文档规范" class="headerlink" title="1.4 文档规范"></a>1.4 文档规范</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></pre></td><td class="code"><pre><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">模块级文档字符串。</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">描述模块的职责、主要类和函数的概要。</span></span><br><span class="line"><span class="string">可以包含使用示例。</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">Example:</span></span><br><span class="line"><span class="string">    &gt;&gt;&gt; from myproject.utils import calculate</span></span><br><span class="line"><span class="string">    &gt;&gt;&gt; calculate(1, 2)</span></span><br><span class="line"><span class="string">    3</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Calculator</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;计算器类。</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">    提供基础的数学运算功能。</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">    Attributes:</span></span><br><span class="line"><span class="string">        history: 操作历史记录。</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">    Example:</span></span><br><span class="line"><span class="string">        &gt;&gt;&gt; calc = Calculator()</span></span><br><span class="line"><span class="string">        &gt;&gt;&gt; calc.add(1, 2)</span></span><br><span class="line"><span class="string">        3</span></span><br><span class="line"><span class="string">    &quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">add</span>(<span class="params">self, a: <span class="built_in">int</span>, b: <span class="built_in">int</span></span>) -&gt; <span class="built_in">int</span>:</span><br><span class="line">        <span class="string">&quot;&quot;&quot;返回两数之和。</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">        Args:</span></span><br><span class="line"><span class="string">            a: 第一个加数。</span></span><br><span class="line"><span class="string">            b: 第二个加数。</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">        Returns:</span></span><br><span class="line"><span class="string">            两数之和。</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">        Raises:</span></span><br><span class="line"><span class="string">            TypeError: 如果参数不是数字。</span></span><br><span class="line"><span class="string">        &quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> <span class="built_in">isinstance</span>(a, (<span class="built_in">int</span>, <span class="built_in">float</span>)) <span class="keyword">or</span> <span class="keyword">not</span> <span class="built_in">isinstance</span>(b, (<span class="built_in">int</span>, <span class="built_in">float</span>)):</span><br><span class="line">            <span class="keyword">raise</span> TypeError(<span class="string">&quot;参数必须是数字&quot;</span>)</span><br><span class="line">        <span class="keyword">return</span> a + b</span><br></pre></td></tr></table></figure><p><strong>文档生成工具</strong>：</p><table><thead><tr><th>工具</th><th>特点</th><th>适用场景</th></tr></thead><tbody><tr><td><code>Sphinx</code></td><td>功能强大，支持多种输出格式</td><td>大型项目文档</td></tr><tr><td><code>MkDocs</code></td><td>基于 Markdown，配置简单</td><td>项目文档站点</td></tr><tr><td><code>pdoc</code></td><td>零配置，自动生成 API 文档</td><td>快速文档生成</td></tr></tbody></table><hr><h2 id="2-测试策略"><a href="#2-测试策略" class="headerlink" title="2. 测试策略"></a>2. 测试策略</h2><h3 id="2-1-测试金字塔"><a href="#2-1-测试金字塔" class="headerlink" title="2.1 测试金字塔"></a>2.1 测试金字塔</h3><figure class="highlight plaintext"><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><span class="line">        /\</span><br><span class="line">       /  \      E2E 测试（少而精）</span><br><span class="line">      /----\     - 模拟真实用户操作</span><br><span class="line">     /      \    - 执行慢，维护成本高</span><br><span class="line">    /--------\</span><br><span class="line">   /          \  集成测试（中等数量）</span><br><span class="line">  /------------\ - 测试模块间交互</span><br><span class="line"> /              \ - 需要外部依赖</span><br><span class="line">/----------------\</span><br><span class="line">                  单元测试（大量）</span><br><span class="line">                  - 测试最小单元</span><br><span class="line">                  - 快速、独立、可重复</span><br></pre></td></tr></table></figure><p><strong>比例建议</strong>：单元测试 70% &#x2F; 集成测试 20% &#x2F; E2E 测试 10%</p><p><strong>测试类型对比</strong>：</p><table><thead><tr><th>类型</th><th>速度</th><th>维护成本</th><th>覆盖范围</th><th>数量建议</th></tr></thead><tbody><tr><td>单元测试</td><td>快</td><td>低</td><td>单个函数&#x2F;方法</td><td>大量</td></tr><tr><td>集成测试</td><td>中等</td><td>中等</td><td>模块间交互</td><td>中等</td></tr><tr><td>E2E 测试</td><td>慢</td><td>高</td><td>完整业务流程</td><td>少量</td></tr></tbody></table><h3 id="2-2-pytest-框架"><a href="#2-2-pytest-框架" class="headerlink" title="2.2 pytest 框架"></a>2.2 pytest 框架</h3><p>pytest 是 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><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># test_calculator.py</span></span><br><span class="line"><span class="keyword">import</span> pytest</span><br><span class="line"><span class="keyword">from</span> myproject.calculator <span class="keyword">import</span> Calculator</span><br><span class="line"></span><br><span class="line"><span class="comment"># fixture：共享测试资源</span></span><br><span class="line"><span class="meta">@pytest.fixture</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">calc</span>():</span><br><span class="line">    <span class="string">&quot;&quot;&quot;每个测试方法前创建 Calculator 实例。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">return</span> Calculator()</span><br><span class="line"></span><br><span class="line"><span class="meta">@pytest.fixture(<span class="params">scope=<span class="string">&quot;module&quot;</span></span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">database</span>():</span><br><span class="line">    <span class="string">&quot;&quot;&quot;模块级只创建一次。&quot;&quot;&quot;</span></span><br><span class="line">    db = create_test_database()</span><br><span class="line">    <span class="keyword">yield</span> db</span><br><span class="line">    db.cleanup()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 参数化测试</span></span><br><span class="line"><span class="meta">@pytest.mark.parametrize(<span class="params"><span class="string">&quot;a,b,expected&quot;</span>, [</span></span></span><br><span class="line"><span class="params"><span class="meta">    (<span class="params"><span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span></span>),</span></span></span><br><span class="line"><span class="params"><span class="meta">    (<span class="params">-<span class="number">1</span>, <span class="number">1</span>, <span class="number">0</span></span>),</span></span></span><br><span class="line"><span class="params"><span class="meta">    (<span class="params"><span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span></span>),</span></span></span><br><span class="line"><span class="params"><span class="meta">    (<span class="params"><span class="number">100</span>, <span class="number">200</span>, <span class="number">300</span></span>),</span></span></span><br><span class="line"><span class="params"><span class="meta">]</span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">test_add</span>(<span class="params">calc, a, b, expected</span>):</span><br><span class="line">    <span class="keyword">assert</span> calc.add(a, b) == expected</span><br><span class="line"></span><br><span class="line"><span class="comment"># 测试异常</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">test_add_invalid_type</span>(<span class="params">calc</span>):</span><br><span class="line">    <span class="keyword">with</span> pytest.raises(TypeError, <span class="keyword">match</span>=<span class="string">&quot;必须是数字&quot;</span>):</span><br><span class="line">        calc.add(<span class="string">&quot;1&quot;</span>, <span class="number">2</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 跳过和标记</span></span><br><span class="line"><span class="meta">@pytest.mark.skip(<span class="params">reason=<span class="string">&quot;功能尚未实现&quot;</span></span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">test_future_feature</span>():</span><br><span class="line">    <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@pytest.mark.skipif(<span class="params">sys.version_info &lt; (<span class="params"><span class="number">3</span>, <span class="number">10</span></span>), reason=<span class="string">&quot;需要 Python 3.10+&quot;</span></span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">test_match_case</span>():</span><br><span class="line">    <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@pytest.mark.slow    </span><span class="comment"># 自定义标记</span></span><br><span class="line"><span class="meta">@pytest.mark.integration</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">test_database_integration</span>():</span><br><span class="line">    <span class="keyword">pass</span></span><br></pre></td></tr></table></figure><p><strong>pytest 常用命令</strong>：</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><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">pytest -v                          <span class="comment"># 详细输出</span></span><br><span class="line">pytest -x                          <span class="comment"># 第一次失败就停止</span></span><br><span class="line">pytest -k <span class="string">&quot;test_add&quot;</span>               <span class="comment"># 按名称过滤</span></span><br><span class="line">pytest -m <span class="string">&quot;not slow&quot;</span>               <span class="comment"># 排除慢测试</span></span><br><span class="line">pytest --cov=myproject             <span class="comment"># 覆盖率</span></span><br><span class="line">pytest --cov=myproject --cov-report=html</span><br><span class="line">pytest -n 4                        <span class="comment"># 并行执行（需要 pytest-xdist）</span></span><br></pre></td></tr></table></figure><p><strong>fixture 作用域</strong>：</p><table><thead><tr><th>作用域</th><th>说明</th><th>适用场景</th></tr></thead><tbody><tr><td><code>function</code></td><td>每个测试函数调用一次（默认）</td><td>独立测试数据</td></tr><tr><td><code>class</code></td><td>每个测试类调用一次</td><td>类级别共享资源</td></tr><tr><td><code>module</code></td><td>每个模块调用一次</td><td>数据库连接、配置</td></tr><tr><td><code>package</code></td><td>每个包调用一次</td><td>包级别资源</td></tr><tr><td><code>session</code></td><td>整个测试会话调用一次</td><td>全局资源</td></tr></tbody></table><h3 id="2-3-Mock-与依赖隔离"><a href="#2-3-Mock-与依赖隔离" class="headerlink" title="2.3 Mock 与依赖隔离"></a>2.3 Mock 与依赖隔离</h3><p>Mock 用于隔离外部依赖，让测试更快、更稳定。</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> unittest.mock <span class="keyword">import</span> Mock, patch, MagicMock, call</span><br><span class="line"></span><br><span class="line"><span class="comment"># patch：替换对象</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">test_fetch_user</span>():</span><br><span class="line">    <span class="keyword">with</span> patch(<span class="string">&quot;myproject.api.requests.get&quot;</span>) <span class="keyword">as</span> mock_get:</span><br><span class="line">        mock_get.return_value.json.return_value = &#123;<span class="string">&quot;id&quot;</span>: <span class="number">1</span>, <span class="string">&quot;name&quot;</span>: <span class="string">&quot;Alice&quot;</span>&#125;</span><br><span class="line">        mock_get.return_value.status_code = <span class="number">200</span></span><br><span class="line"></span><br><span class="line">        user = fetch_user(<span class="number">1</span>)</span><br><span class="line">        <span class="keyword">assert</span> user[<span class="string">&quot;name&quot;</span>] == <span class="string">&quot;Alice&quot;</span></span><br><span class="line">        mock_get.assert_called_once_with(<span class="string">&quot;https://api.example.com/users/1&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># patch 作为装饰器</span></span><br><span class="line"><span class="meta">@patch(<span class="params"><span class="string">&quot;myproject.api.requests.get&quot;</span></span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">test_fetch_user_decorator</span>(<span class="params">mock_get</span>):</span><br><span class="line">    mock_get.return_value.json.return_value = &#123;<span class="string">&quot;id&quot;</span>: <span class="number">1</span>&#125;</span><br><span class="line">    user = fetch_user(<span class="number">1</span>)</span><br><span class="line">    <span class="keyword">assert</span> user[<span class="string">&quot;id&quot;</span>] == <span class="number">1</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Mock 对象</span></span><br><span class="line">mock = Mock()</span><br><span class="line">mock.some_method.return_value = <span class="number">42</span></span><br><span class="line">mock.other.side_effect = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]   <span class="comment"># 每次调用返回不同值</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(mock.some_method())   <span class="comment"># 42</span></span><br><span class="line"><span class="built_in">print</span>(mock.other())         <span class="comment"># 1</span></span><br><span class="line"><span class="built_in">print</span>(mock.other())         <span class="comment"># 2</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 验证调用</span></span><br><span class="line">mock.func(<span class="number">1</span>, <span class="number">2</span>, key=<span class="string">&quot;value&quot;</span>)</span><br><span class="line">mock.func.assert_called_with(<span class="number">1</span>, <span class="number">2</span>, key=<span class="string">&quot;value&quot;</span>)</span><br><span class="line">mock.func.assert_called_once()</span><br><span class="line"><span class="built_in">print</span>(mock.func.call_args)   <span class="comment"># call(1, 2, key=&#x27;value&#x27;)</span></span><br><span class="line"><span class="built_in">print</span>(mock.func.call_count)  <span class="comment"># 1</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># MagicMock：支持魔术方法</span></span><br><span class="line">mock_dict = MagicMock()</span><br><span class="line">mock_dict.__getitem__.return_value = <span class="string">&quot;value&quot;</span></span><br><span class="line"><span class="built_in">print</span>(mock_dict[<span class="string">&quot;key&quot;</span>])   <span class="comment"># value</span></span><br></pre></td></tr></table></figure><p><strong>Mock 使用场景</strong>：</p><figure class="highlight plaintext"><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><span class="line">┌─────────────────────────────────────────────────────────────┐</span><br><span class="line">│                     测试代码                                 │</span><br><span class="line">├─────────────────────────────────────────────────────────────┤</span><br><span class="line">│                                                             │</span><br><span class="line">│   def test_fetch_user():                                    │</span><br><span class="line">│       with patch(&quot;requests.get&quot;) as mock:                   │</span><br><span class="line">│           # Mock 替代真实的 HTTP 请求                        │</span><br><span class="line">│           mock.return_value.json.return_value = &#123;...&#125;       │</span><br><span class="line">│           result = fetch_user(1)                            │</span><br><span class="line">│           assert result[&quot;name&quot;] == &quot;Alice&quot;                  │</span><br><span class="line">│                                                             │</span><br><span class="line">└─────────────────────────────────────────────────────────────┘</span><br><span class="line">                          │</span><br><span class="line">                          │ 替代</span><br><span class="line">                          ▼</span><br><span class="line">┌─────────────────────────────────────────────────────────────┐</span><br><span class="line">│                   外部依赖（被 Mock）                         │</span><br><span class="line">├─────────────────────────────────────────────────────────────┤</span><br><span class="line">│                                                             │</span><br><span class="line">│   - HTTP 请求（requests）                                   │</span><br><span class="line">│   - 数据库查询                                              │</span><br><span class="line">│   - 文件系统                                                │</span><br><span class="line">│   - 第三方 API                                              │</span><br><span class="line">│                                                             │</span><br><span class="line">└─────────────────────────────────────────────────────────────┘</span><br></pre></td></tr></table></figure><h3 id="2-4-测试覆盖率"><a href="#2-4-测试覆盖率" class="headerlink" title="2.4 测试覆盖率"></a>2.4 测试覆盖率</h3><p>测试覆盖率衡量测试代码覆盖了多少生产代码。</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><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 安装</span></span><br><span class="line">pip install pytest-cov</span><br><span class="line"></span><br><span class="line"><span class="comment"># 运行并生成报告</span></span><br><span class="line">pytest --cov=myproject --cov-report=term-missing</span><br><span class="line">pytest --cov=myproject --cov-report=html</span><br><span class="line">pytest --cov=myproject --cov-report=xml   <span class="comment"># 用于 CI 集成</span></span><br></pre></td></tr></table></figure><figure class="highlight ini"><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><span class="line"><span class="comment"># .coveragerc 配置</span></span><br><span class="line"><span class="section">[run]</span></span><br><span class="line"><span class="attr">source</span> = src/myproject</span><br><span class="line"><span class="attr">omit</span> = */tests/*, */migrations/*</span><br><span class="line"></span><br><span class="line"><span class="section">[report]</span></span><br><span class="line"><span class="attr">fail_under</span> = <span class="number">80</span>   <span class="comment"># 覆盖率低于 80% 报错</span></span><br><span class="line"><span class="attr">exclude_lines</span> =</span><br><span class="line">    pragma: no cover</span><br><span class="line">    if TYPE_CHECKING:</span><br><span class="line">    raise NotImplementedError</span><br></pre></td></tr></table></figure><p><strong>覆盖率目标</strong>：</p><table><thead><tr><th>项目类型</th><th>目标覆盖率</th></tr></thead><tbody><tr><td>个人项目</td><td>60-70%</td></tr><tr><td>团队项目</td><td>70-80%</td></tr><tr><td>开源库</td><td>80-90%</td></tr><tr><td>金融&#x2F;医疗</td><td>90%+</td></tr></tbody></table><hr><h2 id="3-性能优化与-Profiling"><a href="#3-性能优化与-Profiling" class="headerlink" title="3. 性能优化与 Profiling"></a>3. 性能优化与 Profiling</h2><h3 id="3-1-性能分析工具"><a href="#3-1-性能分析工具" class="headerlink" title="3.1 性能分析工具"></a>3.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></pre></td><td class="code"><pre><span class="line"><span class="comment"># cProfile：函数级分析</span></span><br><span class="line"><span class="keyword">import</span> cProfile</span><br><span class="line"><span class="keyword">import</span> pstats</span><br><span class="line"></span><br><span class="line">profiler = cProfile.Profile()</span><br><span class="line">profiler.enable()</span><br><span class="line"><span class="comment"># 执行需要分析的代码</span></span><br><span class="line">result = heavy_computation()</span><br><span class="line">profiler.disable()</span><br><span class="line"></span><br><span class="line">stats = pstats.Stats(profiler)</span><br><span class="line">stats.sort_stats(<span class="string">&quot;cumulative&quot;</span>)   <span class="comment"># 按累积时间排序</span></span><br><span class="line">stats.print_stats(<span class="number">20</span>)            <span class="comment"># 打印前 20</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 保存到文件</span></span><br><span class="line">stats.dump_stats(<span class="string">&quot;profile.stats&quot;</span>)</span><br><span class="line"><span class="comment"># 可视化：snakeviz profile.stats</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 命令行方式</span></span><br><span class="line"><span class="comment"># python -m cProfile -s cumulative script.py</span></span><br></pre></td></tr></table></figure><p><strong>cProfile 输出示例</strong>：</p><figure class="highlight plaintext"><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><span class="line">       1000004 function calls in 0.357 seconds</span><br><span class="line"></span><br><span class="line"> Ordered by: cumulative time</span><br><span class="line"></span><br><span class="line"> ncalls  tottime  percall  cumtime  percall filename:lineno(function)</span><br><span class="line">      1    0.000    0.000    0.357    0.357 script.py:1(&lt;module&gt;)</span><br><span class="line">      1    0.200    0.200    0.350    0.350 script.py:5(heavy_computation)</span><br><span class="line">1000000    0.150    0.000    0.150    0.000 &#123;built-in method math.sqrt&#125;</span><br><span class="line">      1    0.000    0.000    0.000    0.000 &#123;method &#x27;disable&#x27; of &#x27;_lsprof.Profiler&#x27;&#125;</span><br></pre></td></tr></table></figure><h3 id="3-2-常见性能优化技巧"><a href="#3-2-常见性能优化技巧" class="headerlink" title="3.2 常见性能优化技巧"></a>3.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><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 1. 列表推导式替代循环</span></span><br><span class="line"><span class="comment"># 慢</span></span><br><span class="line">squares = []</span><br><span class="line"><span class="keyword">for</span> x <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">1000</span>):</span><br><span class="line">    squares.append(x ** <span class="number">2</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 快</span></span><br><span class="line">squares = [x ** <span class="number">2</span> <span class="keyword">for</span> x <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">1000</span>)]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 2. 生成器表达式处理大数据</span></span><br><span class="line"><span class="comment"># 节省内存</span></span><br><span class="line">total = <span class="built_in">sum</span>(x ** <span class="number">2</span> <span class="keyword">for</span> x <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">1000000</span>))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 3. 局部变量比全局变量快</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">process_local</span>(<span class="params">data</span>):</span><br><span class="line">    append = result.append   <span class="comment"># 绑定到局部变量</span></span><br><span class="line">    <span class="keyword">for</span> item <span class="keyword">in</span> data:</span><br><span class="line">        append(item * <span class="number">2</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 4. 使用内置函数</span></span><br><span class="line"><span class="comment"># 慢</span></span><br><span class="line">result = []</span><br><span class="line"><span class="keyword">for</span> x <span class="keyword">in</span> items:</span><br><span class="line">    <span class="keyword">if</span> x &gt; <span class="number">0</span>:</span><br><span class="line">        result.append(x)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 快</span></span><br><span class="line">result = <span class="built_in">list</span>(<span class="built_in">filter</span>(<span class="literal">None</span>, items))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 5. 字符串拼接用 join</span></span><br><span class="line"><span class="comment"># 慢</span></span><br><span class="line">result = <span class="string">&quot;&quot;</span></span><br><span class="line"><span class="keyword">for</span> s <span class="keyword">in</span> strings:</span><br><span class="line">    result += s</span><br><span class="line"></span><br><span class="line"><span class="comment"># 快</span></span><br><span class="line">result = <span class="string">&quot;&quot;</span>.join(strings)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 6. 字典查找替代列表/元组</span></span><br><span class="line"><span class="comment"># O(n) → O(1)</span></span><br><span class="line"><span class="keyword">if</span> item <span class="keyword">in</span> large_list:     <span class="comment"># 慢：O(n)</span></span><br><span class="line"><span class="keyword">if</span> item <span class="keyword">in</span> large_set:      <span class="comment"># 快：O(1)</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 7. 使用 __slots__ 减少内存</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Point</span>:</span><br><span class="line">    __slots__ = [<span class="string">&quot;x&quot;</span>, <span class="string">&quot;y&quot;</span>]   <span class="comment"># 不用 __dict__，节省内存</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, x, y</span>):</span><br><span class="line">        <span class="variable language_">self</span>.x = x</span><br><span class="line">        <span class="variable language_">self</span>.y = y</span><br><span class="line"></span><br><span class="line"><span class="comment"># 8. 批量操作减少 I/O</span></span><br><span class="line"><span class="comment"># 慢：1000 次数据库查询</span></span><br><span class="line"><span class="keyword">for</span> user_id <span class="keyword">in</span> user_ids:</span><br><span class="line">    db.query(<span class="string">f&quot;SELECT * FROM users WHERE id = <span class="subst">&#123;user_id&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 快：1 次批量查询</span></span><br><span class="line">placeholders = <span class="string">&quot;,&quot;</span>.join([<span class="string">&quot;?&quot;</span>] * <span class="built_in">len</span>(user_ids))</span><br><span class="line">db.query(<span class="string">f&quot;SELECT * FROM users WHERE id IN (<span class="subst">&#123;placeholders&#125;</span>)&quot;</span>, user_ids)</span><br></pre></td></tr></table></figure><p><strong>性能优化速查表</strong>：</p><table><thead><tr><th>优化点</th><th>慢代码</th><th>快代码</th><th>提升</th></tr></thead><tbody><tr><td>循环</td><td>for + append</td><td>列表推导式</td><td>~20%</td></tr><tr><td>大数据</td><td>列表</td><td>生成器</td><td>内存节省 90%</td></tr><tr><td>字符串拼接</td><td>+&#x3D;</td><td>join()</td><td>~10x</td></tr><tr><td>查找</td><td>list</td><td>set&#x2F;dict</td><td>O(n)→O(1)</td></tr><tr><td>函数调用</td><td>全局函数</td><td>局部变量绑定</td><td>~15%</td></tr><tr><td>内存</td><td>普通 class</td><td><code>__slots__</code></td><td>节省 30-50%</td></tr></tbody></table><h3 id="3-3-Numba-JIT-编译"><a href="#3-3-Numba-JIT-编译" class="headerlink" title="3.3 Numba JIT 编译"></a>3.3 Numba JIT 编译</h3><p>对于数值计算密集型任务，Numba 可以实现接近 C 的性能。</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><span class="line"><span class="comment"># Numba：JIT 编译（适合数值计算）</span></span><br><span class="line"><span class="comment"># pip install numba</span></span><br><span class="line"><span class="keyword">from</span> numba <span class="keyword">import</span> jit</span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"></span><br><span class="line"><span class="meta">@jit(<span class="params">nopython=<span class="literal">True</span></span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">fast_sum</span>(<span class="params">arr</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;使用 Numba 加速的求和函数。&quot;&quot;&quot;</span></span><br><span class="line">    total = <span class="number">0.0</span></span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(arr.shape[<span class="number">0</span>]):</span><br><span class="line">        total += arr[i]</span><br><span class="line">    <span class="keyword">return</span> total</span><br><span class="line"></span><br><span class="line">arr = np.random.randn(<span class="number">1000000</span>)</span><br><span class="line"><span class="comment"># fast_sum(arr)  比纯 Python 快 100x+</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 对比</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">slow_sum</span>(<span class="params">arr</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;纯 Python 实现。&quot;&quot;&quot;</span></span><br><span class="line">    total = <span class="number">0.0</span></span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(arr.shape[<span class="number">0</span>]):</span><br><span class="line">        total += arr[i]</span><br><span class="line">    <span class="keyword">return</span> total</span><br><span class="line"></span><br><span class="line"><span class="comment"># 性能对比：</span></span><br><span class="line"><span class="comment"># slow_sum: 0.15s</span></span><br><span class="line"><span class="comment"># fast_sum: 0.001s（快 150x）</span></span><br></pre></td></tr></table></figure><hr><h2 id="4-CI-CD-与自动化"><a href="#4-CI-CD-与自动化" class="headerlink" title="4. CI&#x2F;CD 与自动化"></a>4. CI&#x2F;CD 与自动化</h2><h3 id="4-1-GitHub-Actions-示例"><a href="#4-1-GitHub-Actions-示例" class="headerlink" title="4.1 GitHub Actions 示例"></a>4.1 GitHub Actions 示例</h3><p>GitHub Actions 是 GitHub 提供的 CI&#x2F;CD 服务，与代码仓库深度集成。</p><figure class="highlight yaml"><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># .github/workflows/ci.yml</span></span><br><span class="line"><span class="attr">name:</span> <span class="string">CI</span></span><br><span class="line"></span><br><span class="line"><span class="attr">on:</span></span><br><span class="line">  <span class="attr">push:</span></span><br><span class="line">    <span class="attr">branches:</span> [<span class="string">main</span>, <span class="string">develop</span>]</span><br><span class="line">  <span class="attr">pull_request:</span></span><br><span class="line">    <span class="attr">branches:</span> [<span class="string">main</span>]</span><br><span class="line"></span><br><span class="line"><span class="attr">jobs:</span></span><br><span class="line">  <span class="attr">test:</span></span><br><span class="line">    <span class="attr">runs-on:</span> <span class="string">ubuntu-latest</span></span><br><span class="line">    <span class="attr">strategy:</span></span><br><span class="line">      <span class="attr">matrix:</span></span><br><span class="line">        <span class="attr">python-version:</span> [<span class="string">&quot;3.9&quot;</span>, <span class="string">&quot;3.10&quot;</span>, <span class="string">&quot;3.11&quot;</span>, <span class="string">&quot;3.12&quot;</span>]</span><br><span class="line"></span><br><span class="line">    <span class="attr">steps:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">uses:</span> <span class="string">actions/checkout@v4</span></span><br><span class="line"></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Set</span> <span class="string">up</span> <span class="string">Python</span></span><br><span class="line">        <span class="attr">uses:</span> <span class="string">actions/setup-python@v5</span></span><br><span class="line">        <span class="attr">with:</span></span><br><span class="line">          <span class="attr">python-version:</span> <span class="string">$&#123;&#123;</span> <span class="string">matrix.python-version</span> <span class="string">&#125;&#125;</span></span><br><span class="line"></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Install</span> <span class="string">dependencies</span></span><br><span class="line">        <span class="attr">run:</span> <span class="string">|</span></span><br><span class="line"><span class="string">          python -m pip install --upgrade pip</span></span><br><span class="line"><span class="string">          pip install -e &quot;.[dev]&quot;</span></span><br><span class="line"><span class="string"></span></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Lint</span> <span class="string">with</span> <span class="string">ruff</span></span><br><span class="line">        <span class="attr">run:</span> <span class="string">ruff</span> <span class="string">check</span> <span class="string">.</span></span><br><span class="line"></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Format</span> <span class="string">check</span> <span class="string">with</span> <span class="string">black</span></span><br><span class="line">        <span class="attr">run:</span> <span class="string">black</span> <span class="string">--check</span> <span class="string">.</span></span><br><span class="line"></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Type</span> <span class="string">check</span> <span class="string">with</span> <span class="string">mypy</span></span><br><span class="line">        <span class="attr">run:</span> <span class="string">mypy</span> <span class="string">src/</span></span><br><span class="line"></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Test</span> <span class="string">with</span> <span class="string">pytest</span></span><br><span class="line">        <span class="attr">run:</span> <span class="string">pytest</span> <span class="string">--cov=src</span> <span class="string">--cov-report=xml</span></span><br><span class="line"></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Upload</span> <span class="string">coverage</span></span><br><span class="line">        <span class="attr">uses:</span> <span class="string">codecov/codecov-action@v3</span></span><br><span class="line">        <span class="attr">with:</span></span><br><span class="line">          <span class="attr">file:</span> <span class="string">./coverage.xml</span></span><br></pre></td></tr></table></figure><p><strong>CI&#x2F;CD 流水线</strong>：</p><figure class="highlight plaintext"><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><span class="line">代码推送</span><br><span class="line">    │</span><br><span class="line">    ▼</span><br><span class="line">┌─────────────────────────────────────────────────────────────┐</span><br><span class="line">│                    GitHub Actions                            │</span><br><span class="line">├─────────────────────────────────────────────────────────────┤</span><br><span class="line">│                                                             │</span><br><span class="line">│  ┌─────────┐   ┌─────────┐   ┌─────────┐   ┌─────────┐     │</span><br><span class="line">│  │ 安装依赖 │──→│ 代码检查 │──→│ 类型检查 │──→│ 运行测试 │     │</span><br><span class="line">│  └─────────┘   └─────────┘   └─────────┘   └─────────┘     │</span><br><span class="line">│       │             │             │             │           │</span><br><span class="line">│       └─────────────┴─────────────┴─────────────┘           │</span><br><span class="line">│                               │                             │</span><br><span class="line">│                               ▼                             │</span><br><span class="line">│                        ┌───────────┐                        │</span><br><span class="line">│                        │ 上传覆盖率 │                        │</span><br><span class="line">│                        └───────────┘                        │</span><br><span class="line">│                                                             │</span><br><span class="line">└─────────────────────────────────────────────────────────────┘</span><br><span class="line">                    │</span><br><span class="line">                    ▼</span><br><span class="line">              ┌───────────┐</span><br><span class="line">              │ 自动部署   │</span><br><span class="line">              └───────────┘</span><br></pre></td></tr></table></figure><h3 id="4-2-tox-多环境测试"><a href="#4-2-tox-多环境测试" class="headerlink" title="4.2 tox 多环境测试"></a>4.2 tox 多环境测试</h3><p>tox 可以在多个 Python 版本下运行测试，确保代码兼容性。</p><figure class="highlight ini"><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><span class="line"><span class="comment"># tox.ini</span></span><br><span class="line"><span class="section">[tox]</span></span><br><span class="line"><span class="attr">envlist</span> = py39, py310, py311, py312, lint, type</span><br><span class="line"></span><br><span class="line"><span class="section">[testenv]</span></span><br><span class="line"><span class="attr">deps</span> = pytest</span><br><span class="line">       pytest-cov</span><br><span class="line"><span class="attr">commands</span> = pytest &#123;posargs&#125;</span><br><span class="line"></span><br><span class="line"><span class="section">[testenv:lint]</span></span><br><span class="line"><span class="attr">deps</span> = flake8</span><br><span class="line">       black</span><br><span class="line"><span class="attr">commands</span> =</span><br><span class="line">    black --check .</span><br><span class="line">    flake8 src/</span><br><span class="line"></span><br><span class="line"><span class="section">[testenv:type]</span></span><br><span class="line"><span class="attr">deps</span> = mypy</span><br><span class="line"><span class="attr">commands</span> = mypy src/</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line">tox          <span class="comment"># 运行所有环境</span></span><br><span class="line">tox -e py311 <span class="comment"># 只运行 Python 3.11</span></span><br><span class="line">tox -e lint  <span class="comment"># 只运行代码检查</span></span><br></pre></td></tr></table></figure><hr><h2 id="5-打包与发布"><a href="#5-打包与发布" class="headerlink" title="5. 打包与发布"></a>5. 打包与发布</h2><h3 id="5-1-现代打包流程"><a href="#5-1-现代打包流程" class="headerlink" title="5.1 现代打包流程"></a>5.1 现代打包流程</h3><p>Python 现在使用 <code>pyproject.toml</code> 作为项目配置文件。</p><figure class="highlight toml"><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># pyproject.toml（完整版）</span></span><br><span class="line"><span class="section">[build-system]</span></span><br><span class="line"><span class="attr">requires</span> = [<span class="string">&quot;setuptools&gt;=61.0&quot;</span>, <span class="string">&quot;wheel&quot;</span>]</span><br><span class="line"><span class="attr">build-backend</span> = <span class="string">&quot;setuptools.build_meta&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="section">[project]</span></span><br><span class="line"><span class="attr">name</span> = <span class="string">&quot;myproject&quot;</span></span><br><span class="line"><span class="attr">version</span> = <span class="string">&quot;1.0.0&quot;</span></span><br><span class="line"><span class="attr">description</span> = <span class="string">&quot;项目描述&quot;</span></span><br><span class="line"><span class="attr">readme</span> = <span class="string">&quot;README.md&quot;</span></span><br><span class="line"><span class="attr">license</span> = &#123;text = <span class="string">&quot;MIT&quot;</span>&#125;</span><br><span class="line"><span class="attr">requires-python</span> = <span class="string">&quot;&gt;=3.9&quot;</span></span><br><span class="line"><span class="attr">classifiers</span> = [</span><br><span class="line">    <span class="string">&quot;Development Status :: 4 - Beta&quot;</span>,</span><br><span class="line">    <span class="string">&quot;Intended Audience :: Developers&quot;</span>,</span><br><span class="line">    <span class="string">&quot;License :: OSI Approved :: MIT License&quot;</span>,</span><br><span class="line">    <span class="string">&quot;Programming Language :: Python :: 3&quot;</span>,</span><br><span class="line">    <span class="string">&quot;Programming Language :: Python :: 3.9&quot;</span>,</span><br><span class="line">    <span class="string">&quot;Programming Language :: Python :: 3.10&quot;</span>,</span><br><span class="line">    <span class="string">&quot;Programming Language :: Python :: 3.11&quot;</span>,</span><br><span class="line">]</span><br><span class="line"><span class="attr">dependencies</span> = [</span><br><span class="line">    <span class="string">&quot;requests&gt;=2.28.0&quot;</span>,</span><br><span class="line">    <span class="string">&quot;pydantic&gt;=2.0&quot;</span>,</span><br><span class="line">]</span><br><span class="line"></span><br><span class="line"><span class="section">[project.optional-dependencies]</span></span><br><span class="line"><span class="attr">dev</span> = [</span><br><span class="line">    <span class="string">&quot;pytest&gt;=7.0&quot;</span>,</span><br><span class="line">    <span class="string">&quot;pytest-cov&quot;</span>,</span><br><span class="line">    <span class="string">&quot;black&quot;</span>,</span><br><span class="line">    <span class="string">&quot;flake8&quot;</span>,</span><br><span class="line">    <span class="string">&quot;mypy&quot;</span>,</span><br><span class="line">    <span class="string">&quot;pre-commit&quot;</span>,</span><br><span class="line">]</span><br><span class="line"></span><br><span class="line"><span class="section">[project.scripts]</span></span><br><span class="line"><span class="attr">myproject</span> = <span class="string">&quot;myproject.cli:main&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="section">[project.urls]</span></span><br><span class="line"><span class="attr">Homepage</span> = <span class="string">&quot;https://github.com/username/myproject&quot;</span></span><br><span class="line"><span class="attr">Repository</span> = <span class="string">&quot;https://github.com/username/myproject&quot;</span></span><br><span class="line"><span class="attr">Documentation</span> = <span class="string">&quot;https://myproject.readthedocs.io&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="section">[tool.setuptools.packages.find]</span></span><br><span class="line"><span class="attr">where</span> = [<span class="string">&quot;src&quot;</span>]</span><br></pre></td></tr></table></figure><p><strong>项目结构</strong>：</p><figure class="highlight plaintext"><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><span class="line">myproject/</span><br><span class="line">├── src/</span><br><span class="line">│   └── myproject/</span><br><span class="line">│       ├── __init__.py</span><br><span class="line">│       └── core.py</span><br><span class="line">├── tests/</span><br><span class="line">│   ├── __init__.py</span><br><span class="line">│   └── test_core.py</span><br><span class="line">├── pyproject.toml</span><br><span class="line">├── README.md</span><br><span class="line">└── LICENSE</span><br></pre></td></tr></table></figure><p><strong>构建与发布命令</strong>：</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><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><span class="line"><span class="comment"># 安装构建工具</span></span><br><span class="line">pip install build twine</span><br><span class="line"></span><br><span class="line"><span class="comment"># 构建</span></span><br><span class="line">python -m build</span><br><span class="line"></span><br><span class="line"><span class="comment"># 发布到 PyPI</span></span><br><span class="line">python -m twine upload dist/*</span><br><span class="line"></span><br><span class="line"><span class="comment"># 发布到 TestPyPI（测试）</span></span><br><span class="line">python -m twine upload --repository testpypi dist/*</span><br><span class="line"></span><br><span class="line"><span class="comment"># 可编辑安装（开发模式）</span></span><br><span class="line">pip install -e <span class="string">&quot;.[dev]&quot;</span></span><br></pre></td></tr></table></figure><h3 id="5-2-Docker-化"><a href="#5-2-Docker-化" class="headerlink" title="5.2 Docker 化"></a>5.2 Docker 化</h3><p>Docker 确保应用在任何环境下一致运行。</p><figure class="highlight dockerfile"><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><span class="line"><span class="comment"># Dockerfile</span></span><br><span class="line"><span class="keyword">FROM</span> python:<span class="number">3.11</span>-slim</span><br><span class="line"></span><br><span class="line"><span class="keyword">WORKDIR</span><span class="language-bash"> /app</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 先复制依赖，利用缓存层</span></span><br><span class="line"><span class="keyword">COPY</span><span class="language-bash"> requirements.txt .</span></span><br><span class="line"><span class="keyword">RUN</span><span class="language-bash"> pip install --no-cache-dir -r requirements.txt</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 再复制代码</span></span><br><span class="line"><span class="keyword">COPY</span><span class="language-bash"> src/ ./src/</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">ENV</span> PYTHONPATH=/app/src</span><br><span class="line"><span class="keyword">EXPOSE</span> <span class="number">8000</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">CMD</span><span class="language-bash"> [<span class="string">&quot;python&quot;</span>, <span class="string">&quot;-m&quot;</span>, <span class="string">&quot;myproject&quot;</span>]</span></span><br></pre></td></tr></table></figure><figure class="highlight yaml"><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><span class="line"><span class="comment"># docker-compose.yml</span></span><br><span class="line"><span class="attr">version:</span> <span class="string">&quot;3.8&quot;</span></span><br><span class="line"><span class="attr">services:</span></span><br><span class="line">  <span class="attr">app:</span></span><br><span class="line">    <span class="attr">build:</span> <span class="string">.</span></span><br><span class="line">    <span class="attr">ports:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">&quot;8000:8000&quot;</span></span><br><span class="line">    <span class="attr">environment:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">DATABASE_URL=postgresql://user:pass@db:5432/mydb</span></span><br><span class="line">    <span class="attr">depends_on:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">db</span></span><br><span class="line"></span><br><span class="line">  <span class="attr">db:</span></span><br><span class="line">    <span class="attr">image:</span> <span class="string">postgres:15</span></span><br><span class="line">    <span class="attr">environment:</span></span><br><span class="line">      <span class="attr">POSTGRES_USER:</span> <span class="string">user</span></span><br><span class="line">      <span class="attr">POSTGRES_PASSWORD:</span> <span class="string">pass</span></span><br><span class="line">      <span class="attr">POSTGRES_DB:</span> <span class="string">mydb</span></span><br><span class="line">    <span class="attr">volumes:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">postgres_data:/var/lib/postgresql/data</span></span><br><span class="line"></span><br><span class="line"><span class="attr">volumes:</span></span><br><span class="line">  <span class="attr">postgres_data:</span></span><br></pre></td></tr></table></figure><p><strong>Docker 常用命令</strong>：</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><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><span class="line"><span class="comment"># 构建镜像</span></span><br><span class="line">docker build -t myproject:1.0 .</span><br><span class="line"></span><br><span class="line"><span class="comment"># 运行容器</span></span><br><span class="line">docker run -p 8000:8000 myproject:1.0</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用 docker-compose</span></span><br><span class="line">docker-compose up -d        <span class="comment"># 启动</span></span><br><span class="line">docker-compose logs -f      <span class="comment"># 查看日志</span></span><br><span class="line">docker-compose down         <span class="comment"># 停止</span></span><br></pre></td></tr></table></figure><hr><h2 id="附录：第九阶段自检清单"><a href="#附录：第九阶段自检清单" class="headerlink" title="附录：第九阶段自检清单"></a>附录：第九阶段自检清单</h2><p>在继续学习第十阶段之前，请确保你能：</p><ul><li><input disabled="" type="checkbox"> 遵循 PEP 8 编码规范，使用 black + isort + flake8 格式化代码</li><li><input disabled="" type="checkbox"> 配置 pre-commit 钩子，在提交前自动检查代码质量</li><li><input disabled="" type="checkbox"> 使用 pytest 编写单元测试，掌握 fixture、参数化和标记</li><li><input disabled="" type="checkbox"> 使用 unittest.mock 隔离外部依赖</li><li><input disabled="" type="checkbox"> 配置测试覆盖率，目标达到 80% 以上</li><li><input disabled="" type="checkbox"> 使用 cProfile 分析性能瓶颈</li><li><input disabled="" type="checkbox"> 应用至少 5 种 Python 性能优化技巧</li><li><input disabled="" type="checkbox"> 编写 GitHub Actions 工作流实现 CI&#x2F;CD</li><li><input disabled="" type="checkbox"> 使用 pyproject.toml 打包并发布 Python 项目</li><li><input disabled="" type="checkbox"> 编写 Dockerfile 和 docker-compose.yml 容器化应用</li></ul><hr><h2 id="6-工作实战场景"><a href="#6-工作实战场景" class="headerlink" title="6. 工作实战场景"></a>6. 工作实战场景</h2><h3 id="6-1-场景一：为遗留项目添加测试"><a href="#6-1-场景一：为遗留项目添加测试" class="headerlink" title="6.1 场景一：为遗留项目添加测试"></a>6.1 场景一：为遗留项目添加测试</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><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><span class="line"><span class="comment"># 第一步：识别核心业务逻辑（通常在 utils.py, core.py）</span></span><br><span class="line"><span class="comment"># src/myproject/utils.py</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">calculate_discount</span>(<span class="params">price, discount_rate, max_discount=<span class="number">100</span></span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;计算折扣价格。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">if</span> price &lt;= <span class="number">0</span>:</span><br><span class="line">        <span class="keyword">raise</span> ValueError(<span class="string">&quot;价格必须大于 0&quot;</span>)</span><br><span class="line">    <span class="keyword">if</span> discount_rate &lt; <span class="number">0</span> <span class="keyword">or</span> discount_rate &gt; <span class="number">1</span>:</span><br><span class="line">        <span class="keyword">raise</span> ValueError(<span class="string">&quot;折扣率必须在 0-1 之间&quot;</span>)</span><br><span class="line"></span><br><span class="line">    discount = price * discount_rate</span><br><span class="line">    actual_discount = <span class="built_in">min</span>(discount, max_discount)</span><br><span class="line">    <span class="keyword">return</span> price - actual_discount</span><br><span class="line"></span><br><span class="line"><span class="comment"># tests/test_utils.py（先写基本测试）</span></span><br><span class="line"><span class="keyword">import</span> pytest</span><br><span class="line"><span class="keyword">from</span> myproject.utils <span class="keyword">import</span> calculate_discount</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">test_calculate_discount_normal</span>():</span><br><span class="line">    <span class="keyword">assert</span> calculate_discount(<span class="number">100</span>, <span class="number">0.1</span>) == <span class="number">90</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">test_calculate_discount_max_cap</span>():</span><br><span class="line">    <span class="keyword">assert</span> calculate_discount(<span class="number">1000</span>, <span class="number">0.5</span>, max_discount=<span class="number">200</span>) == <span class="number">800</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">test_calculate_discount_invalid_price</span>():</span><br><span class="line">    <span class="keyword">with</span> pytest.raises(ValueError, <span class="keyword">match</span>=<span class="string">&quot;价格必须大于 0&quot;</span>):</span><br><span class="line">        calculate_discount(<span class="number">0</span>, <span class="number">0.1</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 第二步：使用 pytest-cov 找出未覆盖的代码</span></span><br><span class="line"><span class="comment"># pytest --cov=src/myproject --cov-report=html</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 第三步：逐步增加覆盖率</span></span><br><span class="line"><span class="comment"># - 从最常用的函数开始</span></span><br><span class="line"><span class="comment"># - 优先测试关键业务逻辑</span></span><br><span class="line"><span class="comment"># - 遗留代码可以先黑盒测试（集成测试），再逐步重构</span></span><br></pre></td></tr></table></figure><p><strong>步骤总结</strong>：</p><ol><li><strong>优先级排序</strong>：核心业务 &gt; 常用功能 &gt; 边缘功能</li><li><strong>集成测试先行</strong>：对于难以单元测试的遗留代码，先写集成测试</li><li><strong>重构时添加单元测试</strong>：每次修改遗留代码时，先写测试</li><li><strong>设定合理目标</strong>：遗留项目 60-70% 覆盖率即可，新代码 80%+</li></ol><hr><h3 id="6-2-场景二：CI-CD-流水线搭建"><a href="#6-2-场景二：CI-CD-流水线搭建" class="headerlink" title="6.2 场景二：CI&#x2F;CD 流水线搭建"></a>6.2 场景二：CI&#x2F;CD 流水线搭建</h3><p><strong>目标</strong>：为团队项目搭建完整的 CI&#x2F;CD 流水线。</p><figure class="highlight yaml"><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><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># .github/workflows/ci-cd.yml</span></span><br><span class="line"><span class="attr">name:</span> <span class="string">CI/CD</span> <span class="string">Pipeline</span></span><br><span class="line"></span><br><span class="line"><span class="attr">on:</span></span><br><span class="line">  <span class="attr">push:</span></span><br><span class="line">    <span class="attr">branches:</span> [<span class="string">main</span>, <span class="string">develop</span>]</span><br><span class="line">  <span class="attr">pull_request:</span></span><br><span class="line">    <span class="attr">branches:</span> [<span class="string">main</span>]</span><br><span class="line"></span><br><span class="line"><span class="attr">jobs:</span></span><br><span class="line">  <span class="comment"># Job 1: 代码质量检查</span></span><br><span class="line">  <span class="attr">lint:</span></span><br><span class="line">    <span class="attr">runs-on:</span> <span class="string">ubuntu-latest</span></span><br><span class="line">    <span class="attr">steps:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">uses:</span> <span class="string">actions/checkout@v4</span></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">uses:</span> <span class="string">actions/setup-python@v5</span></span><br><span class="line">        <span class="attr">with:</span></span><br><span class="line">          <span class="attr">python-version:</span> <span class="string">&quot;3.11&quot;</span></span><br><span class="line"></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Install</span> <span class="string">linters</span></span><br><span class="line">        <span class="attr">run:</span> <span class="string">pip</span> <span class="string">install</span> <span class="string">black</span> <span class="string">isort</span> <span class="string">flake8</span> <span class="string">mypy</span> <span class="string">ruff</span></span><br><span class="line"></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Run</span> <span class="string">Black</span></span><br><span class="line">        <span class="attr">run:</span> <span class="string">black</span> <span class="string">--check</span> <span class="string">.</span></span><br><span class="line"></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Run</span> <span class="string">isort</span></span><br><span class="line">        <span class="attr">run:</span> <span class="string">isort</span> <span class="string">--check-only</span> <span class="string">--diff</span> <span class="string">.</span></span><br><span class="line"></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Run</span> <span class="string">flake8</span></span><br><span class="line">        <span class="attr">run:</span> <span class="string">flake8</span> <span class="string">src/</span> <span class="string">--max-line-length=88</span></span><br><span class="line"></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Run</span> <span class="string">mypy</span></span><br><span class="line">        <span class="attr">run:</span> <span class="string">mypy</span> <span class="string">src/</span> <span class="string">--ignore-missing-imports</span></span><br><span class="line"></span><br><span class="line">  <span class="comment"># Job 2: 单元测试</span></span><br><span class="line">  <span class="attr">test:</span></span><br><span class="line">    <span class="attr">runs-on:</span> <span class="string">ubuntu-latest</span></span><br><span class="line">    <span class="attr">needs:</span> <span class="string">lint</span></span><br><span class="line">    <span class="attr">strategy:</span></span><br><span class="line">      <span class="attr">matrix:</span></span><br><span class="line">        <span class="attr">python-version:</span> [<span class="string">&quot;3.9&quot;</span>, <span class="string">&quot;3.10&quot;</span>, <span class="string">&quot;3.11&quot;</span>, <span class="string">&quot;3.12&quot;</span>]</span><br><span class="line">        <span class="attr">os:</span> [<span class="string">ubuntu-latest</span>, <span class="string">windows-latest</span>, <span class="string">macos-latest</span>]</span><br><span class="line">      <span class="attr">fail-fast:</span> <span class="literal">false</span></span><br><span class="line"></span><br><span class="line">    <span class="attr">steps:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">uses:</span> <span class="string">actions/checkout@v4</span></span><br><span class="line"></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Set</span> <span class="string">up</span> <span class="string">Python</span> <span class="string">$&#123;&#123;</span> <span class="string">matrix.python-version</span> <span class="string">&#125;&#125;</span></span><br><span class="line">        <span class="attr">uses:</span> <span class="string">actions/setup-python@v5</span></span><br><span class="line">        <span class="attr">with:</span></span><br><span class="line">          <span class="attr">python-version:</span> <span class="string">$&#123;&#123;</span> <span class="string">matrix.python-version</span> <span class="string">&#125;&#125;</span></span><br><span class="line"></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Install</span> <span class="string">dependencies</span></span><br><span class="line">        <span class="attr">run:</span> <span class="string">|</span></span><br><span class="line"><span class="string">          python -m pip install --upgrade pip</span></span><br><span class="line"><span class="string">          pip install -e &quot;.[dev]&quot;</span></span><br><span class="line"><span class="string"></span></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Run</span> <span class="string">tests</span></span><br><span class="line">        <span class="attr">run:</span> <span class="string">pytest</span> <span class="string">--cov=src</span> <span class="string">--cov-report=xml</span> <span class="string">--cov-report=html</span></span><br><span class="line"></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Upload</span> <span class="string">coverage</span> <span class="string">to</span> <span class="string">Codecov</span></span><br><span class="line">        <span class="attr">uses:</span> <span class="string">codecov/codecov-action@v3</span></span><br><span class="line">        <span class="attr">with:</span></span><br><span class="line">          <span class="attr">file:</span> <span class="string">./coverage.xml</span></span><br><span class="line">          <span class="attr">flags:</span> <span class="string">unittests</span></span><br><span class="line">          <span class="attr">fail_ci_if_error:</span> <span class="literal">true</span></span><br><span class="line"></span><br><span class="line">  <span class="comment"># Job 3: 安全检查</span></span><br><span class="line">  <span class="attr">security:</span></span><br><span class="line">    <span class="attr">runs-on:</span> <span class="string">ubuntu-latest</span></span><br><span class="line">    <span class="attr">needs:</span> <span class="string">lint</span></span><br><span class="line">    <span class="attr">steps:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">uses:</span> <span class="string">actions/checkout@v4</span></span><br><span class="line"></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Run</span> <span class="string">Bandit</span> <span class="string">security</span> <span class="string">check</span></span><br><span class="line">        <span class="attr">uses:</span> <span class="string">tj-actions/bandit@v5</span></span><br><span class="line">        <span class="attr">with:</span></span><br><span class="line">          <span class="attr">targets:</span> <span class="string">src/</span></span><br><span class="line"></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Run</span> <span class="string">Safety</span> <span class="string">check</span> <span class="string">for</span> <span class="string">dependencies</span></span><br><span class="line">        <span class="attr">run:</span> <span class="string">|</span></span><br><span class="line"><span class="string">          pip install safety</span></span><br><span class="line"><span class="string">          safety check --json</span></span><br><span class="line"><span class="string"></span></span><br><span class="line">  <span class="comment"># Job 4: 构建和发布</span></span><br><span class="line">  <span class="attr">build:</span></span><br><span class="line">    <span class="attr">runs-on:</span> <span class="string">ubuntu-latest</span></span><br><span class="line">    <span class="attr">needs:</span> [<span class="string">test</span>, <span class="string">security</span>]</span><br><span class="line">    <span class="attr">if:</span> <span class="string">github.ref</span> <span class="string">==</span> <span class="string">&#x27;refs/heads/main&#x27;</span></span><br><span class="line"></span><br><span class="line">    <span class="attr">steps:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">uses:</span> <span class="string">actions/checkout@v4</span></span><br><span class="line"></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Set</span> <span class="string">up</span> <span class="string">Python</span></span><br><span class="line">        <span class="attr">uses:</span> <span class="string">actions/setup-python@v5</span></span><br><span class="line">        <span class="attr">with:</span></span><br><span class="line">          <span class="attr">python-version:</span> <span class="string">&quot;3.11&quot;</span></span><br><span class="line"></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Install</span> <span class="string">build</span> <span class="string">tools</span></span><br><span class="line">        <span class="attr">run:</span> <span class="string">pip</span> <span class="string">install</span> <span class="string">build</span> <span class="string">twine</span></span><br><span class="line"></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Build</span> <span class="string">package</span></span><br><span class="line">        <span class="attr">run:</span> <span class="string">python</span> <span class="string">-m</span> <span class="string">build</span></span><br><span class="line"></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Publish</span> <span class="string">to</span> <span class="string">PyPI</span></span><br><span class="line">        <span class="attr">uses:</span> <span class="string">pypa/gh-action-pypi-publish@release/v1</span></span><br><span class="line">        <span class="attr">with:</span></span><br><span class="line">          <span class="attr">password:</span> <span class="string">$&#123;&#123;</span> <span class="string">secrets.PYPI_API_TOKEN</span> <span class="string">&#125;&#125;</span></span><br><span class="line"></span><br><span class="line">  <span class="comment"># Job 5: Docker 构建</span></span><br><span class="line">  <span class="attr">docker:</span></span><br><span class="line">    <span class="attr">runs-on:</span> <span class="string">ubuntu-latest</span></span><br><span class="line">    <span class="attr">needs:</span> [<span class="string">test</span>, <span class="string">security</span>]</span><br><span class="line">    <span class="attr">if:</span> <span class="string">github.ref</span> <span class="string">==</span> <span class="string">&#x27;refs/heads/main&#x27;</span></span><br><span class="line"></span><br><span class="line">    <span class="attr">steps:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">uses:</span> <span class="string">actions/checkout@v4</span></span><br><span class="line"></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Set</span> <span class="string">up</span> <span class="string">Docker</span> <span class="string">Buildx</span></span><br><span class="line">        <span class="attr">uses:</span> <span class="string">docker/setup-buildx-action@v3</span></span><br><span class="line"></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Login</span> <span class="string">to</span> <span class="string">Docker</span> <span class="string">Hub</span></span><br><span class="line">        <span class="attr">uses:</span> <span class="string">docker/login-action@v3</span></span><br><span class="line">        <span class="attr">with:</span></span><br><span class="line">          <span class="attr">username:</span> <span class="string">$&#123;&#123;</span> <span class="string">secrets.DOCKERHUB_USERNAME</span> <span class="string">&#125;&#125;</span></span><br><span class="line">          <span class="attr">password:</span> <span class="string">$&#123;&#123;</span> <span class="string">secrets.DOCKERHUB_TOKEN</span> <span class="string">&#125;&#125;</span></span><br><span class="line"></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Build</span> <span class="string">and</span> <span class="string">push</span></span><br><span class="line">        <span class="attr">uses:</span> <span class="string">docker/build-push-action@v5</span></span><br><span class="line">        <span class="attr">with:</span></span><br><span class="line">          <span class="attr">context:</span> <span class="string">.</span></span><br><span class="line">          <span class="attr">push:</span> <span class="literal">true</span></span><br><span class="line">          <span class="attr">tags:</span> <span class="string">|</span></span><br><span class="line"><span class="string">            myproject:latest</span></span><br><span class="line"><span class="string">            myproject:$&#123;&#123; github.sha &#125;&#125;</span></span><br></pre></td></tr></table></figure><p><strong>流水线架构图</strong>：</p><figure class="highlight plaintext"><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><span class="line">┌─────────────────────────────────────────────────────────────┐</span><br><span class="line">│                    CI/CD 流水线                              │</span><br><span class="line">├─────────────────────────────────────────────────────────────┤</span><br><span class="line">│                                                             │</span><br><span class="line">│  代码推送                                                    │</span><br><span class="line">│    │                                                        │</span><br><span class="line">│    ▼                                                        │</span><br><span class="line">│  ┌──────────────┐                                          │</span><br><span class="line">│  │  lint 检查   │  black + isort + flake8 + mypy           │</span><br><span class="line">│  └──────────────┘                                          │</span><br><span class="line">│    │                                                        │</span><br><span class="line">│    ├──────────────────────┐                                │</span><br><span class="line">│    ▼                      ▼                                │</span><br><span class="line">│  ┌──────────────┐   ┌──────────────┐                      │</span><br><span class="line">│  │  test 测试   │   │ security 检查 │                      │</span><br><span class="line">│  │ (多版本多系统) │   │  bandit+safety │                     │</span><br><span class="line">│  └──────────────┘   └──────────────┘                      │</span><br><span class="line">│    │                      │                                │</span><br><span class="line">│    └──────────────────────┘                                │</span><br><span class="line">│              │                                              │</span><br><span class="line">│              ▼                                              │</span><br><span class="line">│      ┌─────────────┐                                       │</span><br><span class="line">│      │  构建发布   │  PyPI + Docker Hub                    │</span><br><span class="line">│      └─────────────┘                                       │</span><br><span class="line">│                                                             │</span><br><span class="line">│  耗时估计：                                                  │</span><br><span class="line">│    lint: ~1min                                              │</span><br><span class="line">│    test: ~5min (并行)                                       │</span><br><span class="line">│    build: ~2min                                             │</span><br><span class="line">│    总计: ~8-10min                                           │</span><br><span class="line">│                                                             │</span><br><span class="line">└─────────────────────────────────────────────────────────────┘</span><br></pre></td></tr></table></figure><hr><h3 id="6-3-场景三：性能优化实战"><a href="#6-3-场景三：性能优化实战" class="headerlink" title="6.3 场景三：性能优化实战"></a>6.3 场景三：性能优化实战</h3><p><strong>问题</strong>：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><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 第一步：使用 cProfile 定位瓶颈</span></span><br><span class="line"><span class="keyword">import</span> cProfile</span><br><span class="line"><span class="keyword">import</span> pstats</span><br><span class="line"><span class="keyword">from</span> io <span class="keyword">import</span> StringIO</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">profile_api</span>():</span><br><span class="line">    profiler = cProfile.Profile()</span><br><span class="line">    profiler.enable()</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 执行慢接口</span></span><br><span class="line">    result = slow_api_endpoint()</span><br><span class="line"></span><br><span class="line">    profiler.disable()</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 分析结果</span></span><br><span class="line">    s = StringIO()</span><br><span class="line">    stats = pstats.Stats(profiler, stream=s).sort_stats(<span class="string">&#x27;cumulative&#x27;</span>)</span><br><span class="line">    stats.print_stats(<span class="number">20</span>)</span><br><span class="line">    <span class="built_in">print</span>(s.getvalue())</span><br><span class="line"></span><br><span class="line"><span class="comment"># 第二步：根据分析结果优化</span></span><br><span class="line"><span class="comment"># 假设瓶颈在数据库查询和 JSON 序列化</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 优化前：</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">get_users_slow</span>():</span><br><span class="line">    users = db.query(<span class="string">&quot;SELECT * FROM users&quot;</span>)  <span class="comment"># 查询所有字段</span></span><br><span class="line">    result = []</span><br><span class="line">    <span class="keyword">for</span> user <span class="keyword">in</span> users:</span><br><span class="line">        result.append(&#123;</span><br><span class="line">            <span class="string">&quot;id&quot;</span>: user.<span class="built_in">id</span>,</span><br><span class="line">            <span class="string">&quot;name&quot;</span>: user.name,</span><br><span class="line">            <span class="string">&quot;email&quot;</span>: user.email,</span><br><span class="line">            <span class="comment"># ... 计算其他字段</span></span><br><span class="line">        &#125;)</span><br><span class="line">    <span class="keyword">return</span> json.dumps(result)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 优化后：</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">get_users_fast</span>():</span><br><span class="line">    <span class="comment"># 1. 只查询需要的字段</span></span><br><span class="line">    users = db.query(<span class="string">&quot;SELECT id, name, email FROM users&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 2. 使用列表推导式 + 直接返回（避免中间变量）</span></span><br><span class="line">    result = [</span><br><span class="line">        &#123;<span class="string">&quot;id&quot;</span>: u.<span class="built_in">id</span>, <span class="string">&quot;name&quot;</span>: u.name, <span class="string">&quot;email&quot;</span>: u.email&#125;</span><br><span class="line">        <span class="keyword">for</span> u <span class="keyword">in</span> users</span><br><span class="line">    ]</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 3. 使用 orjson（比标准 json 快 5-10x）</span></span><br><span class="line">    <span class="keyword">import</span> orjson</span><br><span class="line">    <span class="keyword">return</span> orjson.dumps(result)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 第三步：使用 Numba 优化计算密集型任务</span></span><br><span class="line"><span class="keyword">from</span> numba <span class="keyword">import</span> jit</span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"></span><br><span class="line"><span class="meta">@jit(<span class="params">nopython=<span class="literal">True</span></span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">calculate_score_batch</span>(<span class="params">user_scores: np.ndarray</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;批量计算用户评分（比纯 Python 快 50x）。&quot;&quot;&quot;</span></span><br><span class="line">    result = np.zeros_like(user_scores)</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="built_in">len</span>(user_scores)):</span><br><span class="line">        <span class="comment"># 复杂计算逻辑</span></span><br><span class="line">        result[i] = np.log(user_scores[i] + <span class="number">1</span>) * <span class="number">100</span></span><br><span class="line">    <span class="keyword">return</span> result</span><br><span class="line"></span><br><span class="line"><span class="comment"># 第四步：引入缓存</span></span><br><span class="line"><span class="keyword">from</span> functools <span class="keyword">import</span> lru_cache</span><br><span class="line"><span class="keyword">import</span> redis</span><br><span class="line"></span><br><span class="line"><span class="comment"># 内存缓存（小数据量）</span></span><br><span class="line"><span class="meta">@lru_cache(<span class="params">maxsize=<span class="number">1000</span></span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">get_user_config</span>(<span class="params">user_id: <span class="built_in">int</span></span>):</span><br><span class="line">    <span class="keyword">return</span> db.query(<span class="string">&quot;SELECT config FROM user_settings WHERE user_id = ?&quot;</span>, user_id)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Redis 缓存（大数据量，多进程共享）</span></span><br><span class="line">redis_client = redis.Redis(host=<span class="string">&#x27;localhost&#x27;</span>, port=<span class="number">6379</span>, db=<span class="number">0</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">get_user_stats_cached</span>(<span class="params">user_id: <span class="built_in">int</span></span>):</span><br><span class="line">    cache_key = <span class="string">f&quot;user_stats:<span class="subst">&#123;user_id&#125;</span>&quot;</span></span><br><span class="line">    cached = redis_client.get(cache_key)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> cached:</span><br><span class="line">        <span class="keyword">return</span> json.loads(cached)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 未命中，从数据库查询</span></span><br><span class="line">    stats = db.query(<span class="string">&quot;SELECT * FROM user_stats WHERE user_id = ?&quot;</span>, user_id)</span><br><span class="line">    redis_client.setex(cache_key, <span class="number">300</span>, json.dumps(stats))  <span class="comment"># 缓存 5 分钟</span></span><br><span class="line">    <span class="keyword">return</span> stats</span><br></pre></td></tr></table></figure><p><strong>性能优化检查清单</strong>：</p><figure class="highlight plaintext"><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><span class="line">□ 数据库查询</span><br><span class="line">  □ 是否只查询必要的字段？</span><br><span class="line">  □ 是否使用了索引？</span><br><span class="line">  □ 是否避免了 N+1 查询？</span><br><span class="line">  □ 是否使用了批量操作？</span><br><span class="line"></span><br><span class="line">□ 数据处理</span><br><span class="line">  □ 是否使用了列表推导式代替循环？</span><br><span class="line">  □ 是否使用了生成器处理大数据？</span><br><span class="line">  □ 是否使用了内置函数（map/filter/sorted）？</span><br><span class="line"></span><br><span class="line">□ I/O 操作</span><br><span class="line">  □ 是否使用了缓存（lru_cache / Redis）？</span><br><span class="line">  □ 是否减少了文件读写次数？</span><br><span class="line">  □ 是否使用了异步 I/O（aiohttp/asyncio）？</span><br><span class="line"></span><br><span class="line">□ 序列化</span><br><span class="line">  □ 是否使用了高速 JSON 库（orjson/ujson）？</span><br><span class="line">  □ 是否避免了大对象的深拷贝？</span><br><span class="line"></span><br><span class="line">□ 计算密集型</span><br><span class="line">  □ 是否可以考虑 Numba/Cython 加速？</span><br><span class="line">  □ 是否可以使用多进程（multiprocessing）？</span><br></pre></td></tr></table></figure><hr><h2 id="7-常见面试题汇总"><a href="#7-常见面试题汇总" class="headerlink" title="7. 常见面试题汇总"></a>7. 常见面试题汇总</h2><h3 id="7-1-测试相关"><a href="#7-1-测试相关" class="headerlink" title="7.1 测试相关"></a>7.1 测试相关</h3><p><strong>Q1: 什么是测试金字塔？如何平衡不同类型的测试？</strong></p><p><strong>答案要点</strong>：</p><ul><li>测试金字塔：单元测试（底）&gt; 集成测试（中）&gt; E2E 测试（顶）</li><li>比例建议：70% &#x2F; 20% &#x2F; 10%</li><li>单元测试：快速、独立、维护成本低</li><li>集成测试：测试模块间交互，需要外部依赖</li><li>E2E 测试：模拟真实用户操作，执行慢、维护成本高</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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 单元测试示例</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">test_calculate_discount</span>():</span><br><span class="line">    <span class="keyword">assert</span> calculate_discount(<span class="number">100</span>, <span class="number">0.1</span>) == <span class="number">90</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 集成测试示例</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">test_order_flow</span>():</span><br><span class="line">    order = create_order(user_id=<span class="number">1</span>, items=[...])</span><br><span class="line">    payment = process_payment(order.<span class="built_in">id</span>)</span><br><span class="line">    <span class="keyword">assert</span> payment.status == <span class="string">&quot;success&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># E2E 测试示例（Selenium/Playwright）</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">test_user_checkout_flow</span>():</span><br><span class="line">    browser.get(<span class="string">&quot;/products/1&quot;</span>)</span><br><span class="line">    browser.find_element(<span class="string">&quot;#add-to-cart&quot;</span>).click()</span><br><span class="line">    browser.find_element(<span class="string">&quot;#checkout&quot;</span>).click()</span><br><span class="line">    <span class="keyword">assert</span> <span class="string">&quot;订单成功&quot;</span> <span class="keyword">in</span> browser.page_source</span><br></pre></td></tr></table></figure><hr><p><strong>Q2: 如何使用 Mock 测试依赖外部 API 的函数？</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><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><span class="line"><span class="keyword">from</span> unittest.mock <span class="keyword">import</span> patch, Mock</span><br><span class="line"><span class="keyword">import</span> pytest</span><br><span class="line"><span class="keyword">from</span> myproject.api <span class="keyword">import</span> fetch_user_data</span><br><span class="line"></span><br><span class="line"><span class="meta">@patch(<span class="params"><span class="string">&quot;myproject.api.requests.get&quot;</span></span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">test_fetch_user_data</span>(<span class="params">mock_get</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;测试依赖 HTTP 请求的函数。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="comment"># 配置 Mock 返回值</span></span><br><span class="line">    mock_response = Mock()</span><br><span class="line">    mock_response.json.return_value = &#123;<span class="string">&quot;id&quot;</span>: <span class="number">1</span>, <span class="string">&quot;name&quot;</span>: <span class="string">&quot;Alice&quot;</span>&#125;</span><br><span class="line">    mock_response.status_code = <span class="number">200</span></span><br><span class="line">    mock_get.return_value = mock_response</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 执行测试</span></span><br><span class="line">    result = fetch_user_data(<span class="number">1</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 验证结果</span></span><br><span class="line">    <span class="keyword">assert</span> result[<span class="string">&quot;name&quot;</span>] == <span class="string">&quot;Alice&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 验证调用</span></span><br><span class="line">    mock_get.assert_called_once_with(<span class="string">&quot;https://api.example.com/users/1&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用 side_effect 模拟不同返回值</span></span><br><span class="line"><span class="meta">@patch(<span class="params"><span class="string">&quot;myproject.api.requests.get&quot;</span></span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">test_fetch_user_data_retry</span>(<span class="params">mock_get</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;测试重试逻辑。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="comment"># 第一次失败，第二次成功</span></span><br><span class="line">    mock_get.side_effect = [</span><br><span class="line">        ConnectionError(<span class="string">&quot;网络错误&quot;</span>),</span><br><span class="line">        Mock(json=<span class="keyword">lambda</span>: &#123;<span class="string">&quot;id&quot;</span>: <span class="number">1</span>&#125;, status_code=<span class="number">200</span>)</span><br><span class="line">    ]</span><br><span class="line"></span><br><span class="line">    result = fetch_user_data_with_retry(<span class="number">1</span>)</span><br><span class="line">    <span class="keyword">assert</span> result[<span class="string">&quot;id&quot;</span>] == <span class="number">1</span></span><br><span class="line">    <span class="keyword">assert</span> mock_get.call_count == <span class="number">2</span>  <span class="comment"># 调用了 2 次</span></span><br></pre></td></tr></table></figure><hr><p><strong>Q3: 测试覆盖率多少合适？100% 覆盖率是必要的吗？</strong></p><p><strong>答案要点</strong>：</p><ul><li>个人项目：60-70%</li><li>团队项目：70-80%</li><li>开源库：80-90%</li><li>金融&#x2F;医疗：90%+</li></ul><p><strong>100% 覆盖率不是目标</strong>：</p><ol><li>有些代码难以测试（如异常处理分支）</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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 不重要的代码可以跳过覆盖率检查</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">debug_log</span>(<span class="params">message</span>):</span><br><span class="line">    <span class="keyword">if</span> os.getenv(<span class="string">&quot;DEBUG&quot;</span>):</span><br><span class="line">        <span class="built_in">print</span>(message)  <span class="comment"># pragma: no cover</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 类型检查代码不需要测试</span></span><br><span class="line"><span class="keyword">if</span> TYPE_CHECKING:  <span class="comment"># pragma: no cover</span></span><br><span class="line">    <span class="keyword">from</span> typing <span class="keyword">import</span> <span class="type">List</span></span><br></pre></td></tr></table></figure><hr><h3 id="7-2-CI-CD-相关"><a href="#7-2-CI-CD-相关" class="headerlink" title="7.2 CI&#x2F;CD 相关"></a>7.2 CI&#x2F;CD 相关</h3><p><strong>Q4: 解释 CI&#x2F;CD 的核心概念和流程。</strong></p><p><strong>CI（持续集成）</strong>：</p><ul><li>开发者频繁提交代码到主分支</li><li>每次提交自动运行测试、代码检查</li><li>快速发现问题，避免集成地狱</li></ul><p><strong>CD（持续交付&#x2F;部署）</strong>：</p><ul><li>持续交付：代码随时可以部署到生产环境</li><li>持续部署：自动部署到生产环境（无需人工干预）</li></ul><p><strong>典型流程</strong>：</p><figure class="highlight plaintext"><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><span class="line">开发者提交代码</span><br><span class="line">    ↓</span><br><span class="line">Git Push</span><br><span class="line">    ↓</span><br><span class="line">CI 服务器触发（GitHub Actions/Jenkins）</span><br><span class="line">    ↓</span><br><span class="line">代码检查（black/flake8/mypy）</span><br><span class="line">    ↓</span><br><span class="line">运行测试（pytest）</span><br><span class="line">    ↓</span><br><span class="line">构建产物（Docker/Wheel）</span><br><span class="line">    ↓</span><br><span class="line">部署到测试环境</span><br><span class="line">    ↓</span><br><span class="line">人工审批（可选）</span><br><span class="line">    ↓</span><br><span class="line">部署到生产环境</span><br></pre></td></tr></table></figure><hr><p><strong>Q5: 如何确保 CI&#x2F;CD 流水线的安全性？</strong></p><p><strong>答案要点</strong>：</p><ol><li><p><strong>密钥管理</strong>：</p><ul><li>不要在代码中硬编码密钥</li><li>使用 CI 平台的 Secrets 功能</li><li>定期轮换密钥</li></ul></li><li><p><strong>权限控制</strong>：</p><ul><li>限制谁能修改 CI 配置</li><li>分支保护：关键分支必须通过 CI 才能 merge</li></ul></li><li><p><strong>依赖安全</strong>：</p><ul><li>锁定依赖版本（requirements.lock）</li><li>使用 Dependabot 自动更新依赖</li><li>运行安全扫描（Safety&#x2F;Bandit）</li></ul></li></ol><figure class="highlight yaml"><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><span class="line"><span class="comment"># GitHub Actions Secrets 示例</span></span><br><span class="line"><span class="attr">env:</span></span><br><span class="line">  <span class="attr">DATABASE_URL:</span> <span class="string">$&#123;&#123;</span> <span class="string">secrets.DATABASE_URL</span> <span class="string">&#125;&#125;</span></span><br><span class="line">  <span class="attr">API_KEY:</span> <span class="string">$&#123;&#123;</span> <span class="string">secrets.API_KEY</span> <span class="string">&#125;&#125;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 依赖安全检查</span></span><br><span class="line"><span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Run</span> <span class="string">Safety</span></span><br><span class="line">  <span class="attr">run:</span> <span class="string">|</span></span><br><span class="line"><span class="string">    pip install safety</span></span><br><span class="line"><span class="string">    safety check --json</span></span><br></pre></td></tr></table></figure><hr><h3 id="7-3-性能优化相关"><a href="#7-3-性能优化相关" class="headerlink" title="7.3 性能优化相关"></a>7.3 性能优化相关</h3><p><strong>Q6: 如何定位 Python 程序的性能瓶颈？</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><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><span class="line"><span class="comment"># 方法1：cProfile</span></span><br><span class="line"><span class="keyword">import</span> cProfile</span><br><span class="line"><span class="keyword">import</span> pstats</span><br><span class="line"></span><br><span class="line">profiler = cProfile.Profile()</span><br><span class="line">profiler.enable()</span><br><span class="line"><span class="comment"># 执行需要分析的代码</span></span><br><span class="line">result = slow_function()</span><br><span class="line">profiler.disable()</span><br><span class="line"></span><br><span class="line">stats = pstats.Stats(profiler)</span><br><span class="line">stats.sort_stats(<span class="string">&#x27;cumulative&#x27;</span>)</span><br><span class="line">stats.print_stats(<span class="number">20</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 方法2：line_profiler（逐行分析）</span></span><br><span class="line"><span class="keyword">from</span> line_profiler <span class="keyword">import</span> LineProfiler</span><br><span class="line"></span><br><span class="line">lp = LineProfiler()</span><br><span class="line">lp.add_function(slow_function)</span><br><span class="line">lp_wrapper = lp(slow_function)</span><br><span class="line"></span><br><span class="line">result = lp_wrapper()</span><br><span class="line">lp.print_stats()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 方法3：memory_profiler（内存分析）</span></span><br><span class="line"><span class="keyword">from</span> memory_profiler <span class="keyword">import</span> profile</span><br><span class="line"></span><br><span class="line"><span class="meta">@profile</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">memory_intensive_function</span>():</span><br><span class="line">    large_list = [i <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">1000000</span>)]</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">sum</span>(large_list)</span><br></pre></td></tr></table></figure><hr><p><strong>Q7: 列举 5 种常见的 Python 性能优化技巧。</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><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><span class="line"><span class="comment"># 1. 列表推导式代替循环</span></span><br><span class="line"><span class="comment"># 慢：</span></span><br><span class="line">squares = []</span><br><span class="line"><span class="keyword">for</span> x <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">1000</span>):</span><br><span class="line">    squares.append(x ** <span class="number">2</span>)</span><br><span class="line"><span class="comment"># 快：</span></span><br><span class="line">squares = [x ** <span class="number">2</span> <span class="keyword">for</span> x <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">1000</span>)]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 2. 使用生成器节省内存</span></span><br><span class="line"><span class="comment"># 慢（占用大量内存）：</span></span><br><span class="line">data = [process(item) <span class="keyword">for</span> item <span class="keyword">in</span> huge_list]</span><br><span class="line"><span class="comment"># 快（惰性计算）：</span></span><br><span class="line">data = (process(item) <span class="keyword">for</span> item <span class="keyword">in</span> huge_list)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 3. 字符串拼接用 join</span></span><br><span class="line"><span class="comment"># 慢：</span></span><br><span class="line">result = <span class="string">&quot;&quot;</span></span><br><span class="line"><span class="keyword">for</span> s <span class="keyword">in</span> strings:</span><br><span class="line">    result += s</span><br><span class="line"><span class="comment"># 快：</span></span><br><span class="line">result = <span class="string">&quot;&quot;</span>.join(strings)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 4. 使用 set/dict 代替 list 进行查找</span></span><br><span class="line"><span class="comment"># 慢（O(n)）：</span></span><br><span class="line"><span class="keyword">if</span> item <span class="keyword">in</span> large_list:</span><br><span class="line">    <span class="keyword">pass</span></span><br><span class="line"><span class="comment"># 快（O(1)）：</span></span><br><span class="line"><span class="keyword">if</span> item <span class="keyword">in</span> large_set:</span><br><span class="line">    <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 5. 局部变量比全局变量快</span></span><br><span class="line"><span class="keyword">import</span> math</span><br><span class="line"><span class="keyword">def</span> <span class="title function_">calculate_fast</span>():</span><br><span class="line">    sqrt = math.sqrt  <span class="comment"># 绑定到局部变量</span></span><br><span class="line">    <span class="keyword">return</span> [sqrt(x) <span class="keyword">for</span> x <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">1000</span>)]</span><br></pre></td></tr></table></figure><hr><h3 id="7-4-打包发布相关"><a href="#7-4-打包发布相关" class="headerlink" title="7.4 打包发布相关"></a>7.4 打包发布相关</h3><p><strong>Q8: 解释 pyproject.toml 的作用和结构。</strong></p><p><strong>答案要点</strong>：</p><ul><li>现代 Python 项目的配置文件（替代 setup.py）</li><li>声明式配置，更易维护</li><li>包含构建系统、项目元数据、依赖、工具配置</li></ul><figure class="highlight toml"><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><span class="line"><span class="section">[build-system]</span></span><br><span class="line"><span class="attr">requires</span> = [<span class="string">&quot;setuptools&gt;=61.0&quot;</span>]</span><br><span class="line"><span class="attr">build-backend</span> = <span class="string">&quot;setuptools.build_meta&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="section">[project]</span></span><br><span class="line"><span class="attr">name</span> = <span class="string">&quot;myproject&quot;</span></span><br><span class="line"><span class="attr">version</span> = <span class="string">&quot;1.0.0&quot;</span></span><br><span class="line"><span class="attr">description</span> = <span class="string">&quot;项目描述&quot;</span></span><br><span class="line"><span class="attr">requires-python</span> = <span class="string">&quot;&gt;=3.9&quot;</span></span><br><span class="line"><span class="attr">dependencies</span> = [</span><br><span class="line">    <span class="string">&quot;requests&gt;=2.28.0&quot;</span>,</span><br><span class="line">    <span class="string">&quot;pydantic&gt;=2.0&quot;</span>,</span><br><span class="line">]</span><br><span class="line"></span><br><span class="line"><span class="section">[project.optional-dependencies]</span></span><br><span class="line"><span class="attr">dev</span> = [</span><br><span class="line">    <span class="string">&quot;pytest&gt;=7.0&quot;</span>,</span><br><span class="line">    <span class="string">&quot;black&quot;</span>,</span><br><span class="line">    <span class="string">&quot;mypy&quot;</span>,</span><br><span class="line">]</span><br><span class="line"></span><br><span class="line"><span class="section">[project.scripts]</span></span><br><span class="line"><span class="attr">myproject</span> = <span class="string">&quot;myproject.cli:main&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="section">[tool.black]</span></span><br><span class="line"><span class="attr">line-length</span> = <span class="number">88</span></span><br><span class="line"></span><br><span class="line"><span class="section">[tool.mypy]</span></span><br><span class="line"><span class="attr">python_version</span> = <span class="string">&quot;3.11&quot;</span></span><br><span class="line"><span class="attr">strict</span> = <span class="literal">true</span></span><br></pre></td></tr></table></figure><hr><p><strong>Q9: 如何发布 Python 包到 PyPI？</strong></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><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><span class="line"><span class="comment"># 第一步：安装工具</span></span><br><span class="line">pip install build twine</span><br><span class="line"></span><br><span class="line"><span class="comment"># 第二步：构建产物</span></span><br><span class="line">python -m build</span><br><span class="line"><span class="comment"># 生成：</span></span><br><span class="line"><span class="comment">#   dist/myproject-1.0.0.tar.gz（源码包）</span></span><br><span class="line"><span class="comment">#   dist/myproject-1.0.0-py3-none-any.whl（Wheel 包）</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 第三步：检查产物</span></span><br><span class="line">twine check dist/*</span><br><span class="line"></span><br><span class="line"><span class="comment"># 第四步：上传到 TestPyPI（测试）</span></span><br><span class="line">twine upload --repository testpypi dist/*</span><br><span class="line"></span><br><span class="line"><span class="comment"># 第五步：上传到 PyPI（正式）</span></span><br><span class="line">twine upload dist/*</span><br></pre></td></tr></table></figure><hr><h3 id="7-5-Docker-相关"><a href="#7-5-Docker-相关" class="headerlink" title="7.5 Docker 相关"></a>7.5 Docker 相关</h3><p><strong>Q10: 编写一个生产级 Dockerfile 的最佳实践。</strong></p><figure class="highlight dockerfile"><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><span class="line"><span class="comment"># 多阶段构建，减小镜像体积</span></span><br><span class="line"><span class="keyword">FROM</span> python:<span class="number">3.11</span>-slim as builder</span><br><span class="line"></span><br><span class="line"><span class="keyword">WORKDIR</span><span class="language-bash"> /app</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 安装依赖到独立层</span></span><br><span class="line"><span class="keyword">COPY</span><span class="language-bash"> requirements.txt .</span></span><br><span class="line"><span class="keyword">RUN</span><span class="language-bash"> pip install --no-cache-dir --user -r requirements.txt</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 最终镜像</span></span><br><span class="line"><span class="keyword">FROM</span> python:<span class="number">3.11</span>-slim</span><br><span class="line"></span><br><span class="line"><span class="keyword">WORKDIR</span><span class="language-bash"> /app</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 从 builder 复制依赖</span></span><br><span class="line"><span class="keyword">COPY</span><span class="language-bash"> --from=builder /root/.local /root/.local</span></span><br><span class="line"><span class="keyword">ENV</span> PATH=/root/.local/bin:$PATH</span><br><span class="line"></span><br><span class="line"><span class="comment"># 复制应用代码</span></span><br><span class="line"><span class="keyword">COPY</span><span class="language-bash"> src/ ./src/</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 安全：非 root 用户运行</span></span><br><span class="line"><span class="keyword">RUN</span><span class="language-bash"> useradd -m appuser</span></span><br><span class="line"><span class="keyword">USER</span> appuser</span><br><span class="line"></span><br><span class="line"><span class="comment"># 健康检查</span></span><br><span class="line"><span class="keyword">HEALTHCHECK</span><span class="language-bash"> --interval=30s --<span class="built_in">timeout</span>=3s --retries=3 \</span></span><br><span class="line"><span class="language-bash">  CMD curl -f http://localhost:8000/health || <span class="built_in">exit</span> 1</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 环境变量</span></span><br><span class="line"><span class="keyword">ENV</span> PYTHONUNBUFFERED=<span class="number">1</span></span><br><span class="line"><span class="keyword">ENV</span> PYTHONDONTWRITEBYTECODE=<span class="number">1</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">EXPOSE</span> <span class="number">8000</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">CMD</span><span class="language-bash"> [<span class="string">&quot;python&quot;</span>, <span class="string">&quot;-m&quot;</span>, <span class="string">&quot;myproject&quot;</span>]</span></span><br></pre></td></tr></table></figure><p><strong>最佳实践要点</strong>：</p><ol><li>使用 <code>.dockerignore</code> 排除无关文件</li><li>多阶段构建减小镜像体积</li><li>非特权用户运行（安全）</li><li>健康检查（HEALTHCHECK）</li><li>固定基础镜像版本（<code>python:3.11-slim</code> 而非 <code>python:latest</code>）</li><li>利用 Docker 缓存层（依赖先安装，代码后复制）</li></ol><hr><h2 id="8-实战项目：完整的-CI-CD-流水线搭建"><a href="#8-实战项目：完整的-CI-CD-流水线搭建" class="headerlink" title="8. 实战项目：完整的 CI&#x2F;CD 流水线搭建"></a>8. 实战项目：完整的 CI&#x2F;CD 流水线搭建</h2><h3 id="8-1-项目概述"><a href="#8-1-项目概述" class="headerlink" title="8.1 项目概述"></a>8.1 项目概述</h3><p><strong>目标</strong>：为 Flask API 项目搭建完整的 CI&#x2F;CD 流水线，包括代码检查、测试、安全扫描、自动部署。</p><p><strong>技术栈</strong>：</p><ul><li>Flask：Web 框架</li><li>pytest：测试框架</li><li>GitHub Actions：CI&#x2F;CD 平台</li><li>Docker：容器化</li><li>PyPI：包发布</li></ul><h3 id="8-2-项目结构"><a href="#8-2-项目结构" class="headerlink" title="8.2 项目结构"></a>8.2 项目结构</h3><figure class="highlight plaintext"><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><span class="line">myflaskapp/</span><br><span class="line">├── src/</span><br><span class="line">│   └── myflaskapp/</span><br><span class="line">│       ├── __init__.py</span><br><span class="line">│       ├── app.py</span><br><span class="line">│       ├── models.py</span><br><span class="line">│       └── utils.py</span><br><span class="line">├── tests/</span><br><span class="line">│   ├── __init__.py</span><br><span class="line">│   ├── conftest.py</span><br><span class="line">│   ├── test_app.py</span><br><span class="line">│   └── test_utils.py</span><br><span class="line">├── .github/</span><br><span class="line">│   └── workflows/</span><br><span class="line">│       └── ci.yml</span><br><span class="line">├── Dockerfile</span><br><span class="line">├── docker-compose.yml</span><br><span class="line">├── pyproject.toml</span><br><span class="line">├── requirements.txt</span><br><span class="line">├── .pre-commit-config.yaml</span><br><span class="line">├── .dockerignore</span><br><span class="line">└── .gitignore</span><br></pre></td></tr></table></figure><h3 id="8-3-核心文件"><a href="#8-3-核心文件" class="headerlink" title="8.3 核心文件"></a>8.3 核心文件</h3><p><strong>pyproject.toml</strong>：</p><figure class="highlight toml"><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></pre></td><td class="code"><pre><span class="line"><span class="section">[build-system]</span></span><br><span class="line"><span class="attr">requires</span> = [<span class="string">&quot;setuptools&gt;=61.0&quot;</span>]</span><br><span class="line"><span class="attr">build-backend</span> = <span class="string">&quot;setuptools.build_meta&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="section">[project]</span></span><br><span class="line"><span class="attr">name</span> = <span class="string">&quot;myflaskapp&quot;</span></span><br><span class="line"><span class="attr">version</span> = <span class="string">&quot;1.0.0&quot;</span></span><br><span class="line"><span class="attr">description</span> = <span class="string">&quot;Flask API with CI/CD&quot;</span></span><br><span class="line"><span class="attr">requires-python</span> = <span class="string">&quot;&gt;=3.9&quot;</span></span><br><span class="line"><span class="attr">dependencies</span> = [</span><br><span class="line">    <span class="string">&quot;flask&gt;=3.0&quot;</span>,</span><br><span class="line">    <span class="string">&quot;flask-sqlalchemy&gt;=3.0&quot;</span>,</span><br><span class="line">    <span class="string">&quot;pydantic&gt;=2.0&quot;</span>,</span><br><span class="line">]</span><br><span class="line"></span><br><span class="line"><span class="section">[project.optional-dependencies]</span></span><br><span class="line"><span class="attr">dev</span> = [</span><br><span class="line">    <span class="string">&quot;pytest&gt;=7.0&quot;</span>,</span><br><span class="line">    <span class="string">&quot;pytest-cov&quot;</span>,</span><br><span class="line">    <span class="string">&quot;pytest-flask&quot;</span>,</span><br><span class="line">    <span class="string">&quot;black&quot;</span>,</span><br><span class="line">    <span class="string">&quot;isort&quot;</span>,</span><br><span class="line">    <span class="string">&quot;flake8&quot;</span>,</span><br><span class="line">    <span class="string">&quot;mypy&quot;</span>,</span><br><span class="line">    <span class="string">&quot;pre-commit&quot;</span>,</span><br><span class="line">]</span><br><span class="line"></span><br><span class="line"><span class="section">[tool.black]</span></span><br><span class="line"><span class="attr">line-length</span> = <span class="number">88</span></span><br><span class="line"><span class="attr">target-version</span> = [<span class="string">&#x27;py39&#x27;</span>]</span><br><span class="line"></span><br><span class="line"><span class="section">[tool.isort]</span></span><br><span class="line"><span class="attr">profile</span> = <span class="string">&quot;black&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="section">[tool.mypy]</span></span><br><span class="line"><span class="attr">python_version</span> = <span class="string">&quot;3.11&quot;</span></span><br><span class="line"><span class="attr">warn_return_any</span> = <span class="literal">true</span></span><br><span class="line"><span class="attr">disallow_untyped_defs</span> = <span class="literal">true</span></span><br><span class="line"></span><br><span class="line"><span class="section">[tool.pytest.ini_options]</span></span><br><span class="line"><span class="attr">testpaths</span> = [<span class="string">&quot;tests&quot;</span>]</span><br><span class="line"><span class="attr">python_files</span> = <span class="string">&quot;test_*.py&quot;</span></span><br><span class="line"><span class="attr">python_classes</span> = <span class="string">&quot;Test*&quot;</span></span><br><span class="line"><span class="attr">python_functions</span> = <span class="string">&quot;test_*&quot;</span></span><br><span class="line"><span class="attr">addopts</span> = <span class="string">&quot;-v --cov=src --cov-report=term-missing&quot;</span></span><br></pre></td></tr></table></figure><p><strong>Flask 应用代码</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><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># src/myflaskapp/app.py</span></span><br><span class="line"><span class="keyword">from</span> flask <span class="keyword">import</span> Flask, jsonify, request</span><br><span class="line"><span class="keyword">from</span> pydantic <span class="keyword">import</span> BaseModel, Field</span><br><span class="line"><span class="keyword">from</span> typing <span class="keyword">import</span> <span class="type">Optional</span></span><br><span class="line"></span><br><span class="line">app = Flask(__name__)</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">UserCreate</span>(<span class="title class_ inherited__">BaseModel</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;用户创建模型。&quot;&quot;&quot;</span></span><br><span class="line">    name: <span class="built_in">str</span> = Field(..., min_length=<span class="number">1</span>, max_length=<span class="number">50</span>)</span><br><span class="line">    email: <span class="built_in">str</span> = Field(..., pattern=<span class="string">r&quot;^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$&quot;</span>)</span><br><span class="line">    age: <span class="type">Optional</span>[<span class="built_in">int</span>] = Field(<span class="literal">None</span>, ge=<span class="number">0</span>, le=<span class="number">150</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 模拟数据库</span></span><br><span class="line">users_db = &#123;&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@app.route(<span class="params"><span class="string">&quot;/health&quot;</span>, methods=[<span class="string">&quot;GET&quot;</span>]</span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">health</span>():</span><br><span class="line">    <span class="string">&quot;&quot;&quot;健康检查端点。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">return</span> jsonify(&#123;<span class="string">&quot;status&quot;</span>: <span class="string">&quot;ok&quot;</span>&#125;)</span><br><span class="line"></span><br><span class="line"><span class="meta">@app.route(<span class="params"><span class="string">&quot;/users&quot;</span>, methods=[<span class="string">&quot;POST&quot;</span>]</span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">create_user</span>():</span><br><span class="line">    <span class="string">&quot;&quot;&quot;创建用户。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        user_data = UserCreate(**request.json)</span><br><span class="line">    <span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">        <span class="keyword">return</span> jsonify(&#123;<span class="string">&quot;error&quot;</span>: <span class="built_in">str</span>(e)&#125;), <span class="number">400</span></span><br><span class="line"></span><br><span class="line">    user_id = <span class="built_in">len</span>(users_db) + <span class="number">1</span></span><br><span class="line">    users_db[user_id] = user_data.<span class="built_in">dict</span>()</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> jsonify(&#123;</span><br><span class="line">        <span class="string">&quot;id&quot;</span>: user_id,</span><br><span class="line">        <span class="string">&quot;user&quot;</span>: user_data.<span class="built_in">dict</span>()</span><br><span class="line">    &#125;), <span class="number">201</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@app.route(<span class="params"><span class="string">&quot;/users/&lt;int:user_id&gt;&quot;</span>, methods=[<span class="string">&quot;GET&quot;</span>]</span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">get_user</span>(<span class="params">user_id: <span class="built_in">int</span></span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;获取用户。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">if</span> user_id <span class="keyword">not</span> <span class="keyword">in</span> users_db:</span><br><span class="line">        <span class="keyword">return</span> jsonify(&#123;<span class="string">&quot;error&quot;</span>: <span class="string">&quot;User not found&quot;</span>&#125;), <span class="number">404</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> jsonify(&#123;</span><br><span class="line">        <span class="string">&quot;id&quot;</span>: user_id,</span><br><span class="line">        <span class="string">&quot;user&quot;</span>: users_db[user_id]</span><br><span class="line">    &#125;)</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    app.run(debug=<span class="literal">True</span>)</span><br></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><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><span class="line"><span class="comment"># tests/conftest.py</span></span><br><span class="line"><span class="keyword">import</span> pytest</span><br><span class="line"><span class="keyword">from</span> myflaskapp.app <span class="keyword">import</span> app</span><br><span class="line"></span><br><span class="line"><span class="meta">@pytest.fixture</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">client</span>():</span><br><span class="line">    <span class="string">&quot;&quot;&quot;创建测试客户端。&quot;&quot;&quot;</span></span><br><span class="line">    app.config[<span class="string">&quot;TESTING&quot;</span>] = <span class="literal">True</span></span><br><span class="line">    <span class="keyword">with</span> app.test_client() <span class="keyword">as</span> client:</span><br><span class="line">        <span class="keyword">yield</span> client</span><br><span class="line"></span><br><span class="line"><span class="comment"># tests/test_app.py</span></span><br><span class="line"><span class="keyword">import</span> pytest</span><br><span class="line"><span class="keyword">from</span> myflaskapp.app <span class="keyword">import</span> app</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">test_health</span>(<span class="params">client</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;测试健康检查。&quot;&quot;&quot;</span></span><br><span class="line">    response = client.get(<span class="string">&quot;/health&quot;</span>)</span><br><span class="line">    <span class="keyword">assert</span> response.status_code == <span class="number">200</span></span><br><span class="line">    <span class="keyword">assert</span> response.json[<span class="string">&quot;status&quot;</span>] == <span class="string">&quot;ok&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">test_create_user</span>(<span class="params">client</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;测试创建用户。&quot;&quot;&quot;</span></span><br><span class="line">    response = client.post(<span class="string">&quot;/users&quot;</span>, json=&#123;</span><br><span class="line">        <span class="string">&quot;name&quot;</span>: <span class="string">&quot;Alice&quot;</span>,</span><br><span class="line">        <span class="string">&quot;email&quot;</span>: <span class="string">&quot;alice@example.com&quot;</span>,</span><br><span class="line">        <span class="string">&quot;age&quot;</span>: <span class="number">30</span></span><br><span class="line">    &#125;)</span><br><span class="line">    <span class="keyword">assert</span> response.status_code == <span class="number">201</span></span><br><span class="line">    <span class="keyword">assert</span> response.json[<span class="string">&quot;user&quot;</span>][<span class="string">&quot;name&quot;</span>] == <span class="string">&quot;Alice&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">test_create_user_invalid_email</span>(<span class="params">client</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;测试无效邮箱。&quot;&quot;&quot;</span></span><br><span class="line">    response = client.post(<span class="string">&quot;/users&quot;</span>, json=&#123;</span><br><span class="line">        <span class="string">&quot;name&quot;</span>: <span class="string">&quot;Bob&quot;</span>,</span><br><span class="line">        <span class="string">&quot;email&quot;</span>: <span class="string">&quot;invalid-email&quot;</span></span><br><span class="line">    &#125;)</span><br><span class="line">    <span class="keyword">assert</span> response.status_code == <span class="number">400</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">test_get_user_not_found</span>(<span class="params">client</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;测试获取不存在的用户。&quot;&quot;&quot;</span></span><br><span class="line">    response = client.get(<span class="string">&quot;/users/999&quot;</span>)</span><br><span class="line">    <span class="keyword">assert</span> response.status_code == <span class="number">404</span></span><br></pre></td></tr></table></figure><p><strong>GitHub Actions CI&#x2F;CD</strong>：</p><figure class="highlight yaml"><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># .github/workflows/ci.yml</span></span><br><span class="line"><span class="attr">name:</span> <span class="string">CI/CD</span> <span class="string">Pipeline</span></span><br><span class="line"></span><br><span class="line"><span class="attr">on:</span></span><br><span class="line">  <span class="attr">push:</span></span><br><span class="line">    <span class="attr">branches:</span> [<span class="string">main</span>, <span class="string">develop</span>]</span><br><span class="line">  <span class="attr">pull_request:</span></span><br><span class="line">    <span class="attr">branches:</span> [<span class="string">main</span>]</span><br><span class="line"></span><br><span class="line"><span class="attr">jobs:</span></span><br><span class="line">  <span class="attr">lint:</span></span><br><span class="line">    <span class="attr">runs-on:</span> <span class="string">ubuntu-latest</span></span><br><span class="line">    <span class="attr">steps:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">uses:</span> <span class="string">actions/checkout@v4</span></span><br><span class="line"></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">uses:</span> <span class="string">actions/setup-python@v5</span></span><br><span class="line">        <span class="attr">with:</span></span><br><span class="line">          <span class="attr">python-version:</span> <span class="string">&quot;3.11&quot;</span></span><br><span class="line"></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Install</span> <span class="string">linters</span></span><br><span class="line">        <span class="attr">run:</span> <span class="string">pip</span> <span class="string">install</span> <span class="string">black</span> <span class="string">isort</span> <span class="string">flake8</span> <span class="string">mypy</span></span><br><span class="line"></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Run</span> <span class="string">Black</span></span><br><span class="line">        <span class="attr">run:</span> <span class="string">black</span> <span class="string">--check</span> <span class="string">.</span></span><br><span class="line"></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Run</span> <span class="string">isort</span></span><br><span class="line">        <span class="attr">run:</span> <span class="string">isort</span> <span class="string">--check-only</span> <span class="string">--diff</span> <span class="string">.</span></span><br><span class="line"></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Run</span> <span class="string">flake8</span></span><br><span class="line">        <span class="attr">run:</span> <span class="string">flake8</span> <span class="string">src/</span> <span class="string">--max-line-length=88</span></span><br><span class="line"></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Run</span> <span class="string">mypy</span></span><br><span class="line">        <span class="attr">run:</span> <span class="string">mypy</span> <span class="string">src/</span></span><br><span class="line"></span><br><span class="line">  <span class="attr">test:</span></span><br><span class="line">    <span class="attr">runs-on:</span> <span class="string">ubuntu-latest</span></span><br><span class="line">    <span class="attr">needs:</span> <span class="string">lint</span></span><br><span class="line">    <span class="attr">strategy:</span></span><br><span class="line">      <span class="attr">matrix:</span></span><br><span class="line">        <span class="attr">python-version:</span> [<span class="string">&quot;3.9&quot;</span>, <span class="string">&quot;3.10&quot;</span>, <span class="string">&quot;3.11&quot;</span>, <span class="string">&quot;3.12&quot;</span>]</span><br><span class="line"></span><br><span class="line">    <span class="attr">steps:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">uses:</span> <span class="string">actions/checkout@v4</span></span><br><span class="line"></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Set</span> <span class="string">up</span> <span class="string">Python</span> <span class="string">$&#123;&#123;</span> <span class="string">matrix.python-version</span> <span class="string">&#125;&#125;</span></span><br><span class="line">        <span class="attr">uses:</span> <span class="string">actions/setup-python@v5</span></span><br><span class="line">        <span class="attr">with:</span></span><br><span class="line">          <span class="attr">python-version:</span> <span class="string">$&#123;&#123;</span> <span class="string">matrix.python-version</span> <span class="string">&#125;&#125;</span></span><br><span class="line"></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Install</span> <span class="string">dependencies</span></span><br><span class="line">        <span class="attr">run:</span> <span class="string">|</span></span><br><span class="line"><span class="string">          python -m pip install --upgrade pip</span></span><br><span class="line"><span class="string">          pip install -e &quot;.[dev]&quot;</span></span><br><span class="line"><span class="string"></span></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Run</span> <span class="string">tests</span></span><br><span class="line">        <span class="attr">run:</span> <span class="string">pytest</span> <span class="string">--cov=src</span> <span class="string">--cov-report=xml</span></span><br><span class="line"></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Upload</span> <span class="string">coverage</span></span><br><span class="line">        <span class="attr">uses:</span> <span class="string">codecov/codecov-action@v3</span></span><br><span class="line">        <span class="attr">with:</span></span><br><span class="line">          <span class="attr">file:</span> <span class="string">./coverage.xml</span></span><br><span class="line"></span><br><span class="line">  <span class="attr">build:</span></span><br><span class="line">    <span class="attr">runs-on:</span> <span class="string">ubuntu-latest</span></span><br><span class="line">    <span class="attr">needs:</span> [<span class="string">test</span>]</span><br><span class="line">    <span class="attr">if:</span> <span class="string">github.ref</span> <span class="string">==</span> <span class="string">&#x27;refs/heads/main&#x27;</span></span><br><span class="line"></span><br><span class="line">    <span class="attr">steps:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">uses:</span> <span class="string">actions/checkout@v4</span></span><br><span class="line"></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Build</span> <span class="string">Docker</span> <span class="string">image</span></span><br><span class="line">        <span class="attr">run:</span> <span class="string">docker</span> <span class="string">build</span> <span class="string">-t</span> <span class="string">myflaskapp:$&#123;&#123;</span> <span class="string">github.sha</span> <span class="string">&#125;&#125;</span> <span class="string">.</span></span><br><span class="line"></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Push</span> <span class="string">to</span> <span class="string">registry</span></span><br><span class="line">        <span class="attr">run:</span> <span class="string">|</span></span><br><span class="line"><span class="string">          echo $&#123;&#123; secrets.DOCKER_PASSWORD &#125;&#125; | docker login -u $&#123;&#123; secrets.DOCKER_USERNAME &#125;&#125; --password-stdin</span></span><br><span class="line"><span class="string">          docker push myflaskapp:$&#123;&#123; github.sha &#125;&#125;</span></span><br></pre></td></tr></table></figure><p><strong>Dockerfile</strong>：</p><figure class="highlight dockerfile"><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><span class="line"><span class="keyword">FROM</span> python:<span class="number">3.11</span>-slim</span><br><span class="line"></span><br><span class="line"><span class="keyword">WORKDIR</span><span class="language-bash"> /app</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">COPY</span><span class="language-bash"> requirements.txt .</span></span><br><span class="line"><span class="keyword">RUN</span><span class="language-bash"> pip install --no-cache-dir -r requirements.txt</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">COPY</span><span class="language-bash"> src/ ./src/</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">ENV</span> FLASK_APP=myflaskapp.app</span><br><span class="line"><span class="keyword">ENV</span> FLASK_ENV=production</span><br><span class="line"></span><br><span class="line"><span class="keyword">EXPOSE</span> <span class="number">5000</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">HEALTHCHECK</span><span class="language-bash"> --interval=30s --<span class="built_in">timeout</span>=3s \</span></span><br><span class="line"><span class="language-bash">  CMD curl -f http://localhost:5000/health || <span class="built_in">exit</span> 1</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">CMD</span><span class="language-bash"> [<span class="string">&quot;python&quot;</span>, <span class="string">&quot;-m&quot;</span>, <span class="string">&quot;flask&quot;</span>, <span class="string">&quot;run&quot;</span>, <span class="string">&quot;--host=0.0.0.0&quot;</span>]</span></span><br></pre></td></tr></table></figure><h3 id="8-4-项目亮点"><a href="#8-4-项目亮点" class="headerlink" title="8.4 项目亮点"></a>8.4 项目亮点</h3><ol><li><p><strong>完整的 CI&#x2F;CD 流程</strong>：</p><ul><li>代码检查（lint）</li><li>多版本测试（test）</li><li>Docker 构建（build）</li><li>自动部署</li></ul></li><li><p><strong>测试覆盖</strong>：</p><ul><li>单元测试（pytest）</li><li>覆盖率报告（pytest-cov）</li><li>异常处理测试</li></ul></li><li><p><strong>代码质量</strong>：</p><ul><li>pre-commit 钩子</li><li>类型检查（mypy）</li><li>格式化（black）</li></ul></li><li><p><strong>容器化</strong>：</p><ul><li>多阶段构建（可选）</li><li>健康检查</li><li>生产级配置</li></ul></li></ol><hr><blockquote><p><strong>工程师寄语</strong>：代码质量不是”写完后再检查”，而是”写的时候就有意识”。自动化工具（black、pytest、CI）是质量的护城河，但真正的质量来自于工程师对”简洁、清晰、可测试”的追求。记住：<strong>写代码是给人看的，顺便让机器执行</strong>。</p></blockquote><blockquote><p><strong>工程化建议</strong>：</p><ol><li>从项目第一天就配置好自动化工具（pre-commit、CI）</li><li>测试覆盖率是手段，不是目的——关键路径必须有测试</li><li>性能优化前先 profiling，避免过早优化</li><li>CI&#x2F;CD 流水线越早建立，后期越轻松</li></ol></blockquote>]]>
    </content>
    <id>https://itdxb.cn/2026/07/23/python/document/%E3%80%90Python%E3%80%91%E5%B7%A5%E7%A8%8B%E5%8C%96%E4%B8%8E%E4%BB%A3%E7%A0%81%E8%B4%A8%E9%87%8F%EF%BC%88%E7%AC%AC%E4%B9%9D%E9%98%B6%E6%AE%B5%EF%BC%89/</id>
    <link href="https://itdxb.cn/2026/07/23/python/document/%E3%80%90Python%E3%80%91%E5%B7%A5%E7%A8%8B%E5%8C%96%E4%B8%8E%E4%BB%A3%E7%A0%81%E8%B4%A8%E9%87%8F%EF%BC%88%E7%AC%AC%E4%B9%9D%E9%98%B6%E6%AE%B5%EF%BC%89/"/>
    <published>2026-07-23T08:00:00.000Z</published>
    <summary>Python 工程化实践，涵盖编码规范、测试策略、性能优化、CI/CD 和项目发布，建立&quot;可维护、可协作、可交付&quot;的工程思维。</summary>
    <title>【Python】工程化与代码质量（第九阶段）</title>
    <updated>2026-07-24T03:34:28.011Z</updated>
  </entry>
  <entry>
    <author>
      <name>Super Bing</name>
    </author>
    <category term="Python" scheme="https://itdxb.cn/categories/Python/"/>
    <category term="教程" scheme="https://itdxb.cn/tags/%E6%95%99%E7%A8%8B/"/>
    <category term="Python" scheme="https://itdxb.cn/tags/Python/"/>
    <category term="标准库" scheme="https://itdxb.cn/tags/%E6%A0%87%E5%87%86%E5%BA%93/"/>
    <category term="collections" scheme="https://itdxb.cn/tags/collections/"/>
    <category term="itertools" scheme="https://itdxb.cn/tags/itertools/"/>
    <content>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><h1 id="八、标准库核心模块（按功能分类）"><a href="#八、标准库核心模块（按功能分类）" class="headerlink" title="八、标准库核心模块（按功能分类）"></a>八、标准库核心模块（按功能分类）</h1><blockquote><p><strong>阶段定位</strong>：Python 的强大不仅在于语法，更在于丰富的标准库。”batteries included”意味着你不需要为常见任务去安装第三方包。本阶段按功能分类梳理核心标准库，帮你建立”标准库优先”的工程思维。</p></blockquote><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">┌──────────────────────────────────────────────────────────────────┐</span><br><span class="line">│                   Python 标准库知识图谱                           │</span><br><span class="line">├──────────────────────────────────────────────────────────────────┤</span><br><span class="line">│                                                                  │</span><br><span class="line">│  文本处理                                                        │</span><br><span class="line">│    ├── re（正则表达式）                                          │</span><br><span class="line">│    ├── string（字符串常量）                                      │</span><br><span class="line">│    └── textwrap（文本格式化）                                    │</span><br><span class="line">│                                                                  │</span><br><span class="line">│  数据结构                                                        │</span><br><span class="line">│    ├── collections（namedtuple/deque/Counter/defaultdict等）     │</span><br><span class="line">│    ├── heapq（堆队列）                                           │</span><br><span class="line">│    └── bisect（二分查找）                                        │</span><br><span class="line">│                                                                  │</span><br><span class="line">│  函数式工具                                                      │</span><br><span class="line">│    ├── functools（partial/reduce/lru_cache/wraps）              │</span><br><span class="line">│    ├── itertools（无限迭代/组合生成/分组）                        │</span><br><span class="line">│    └── operator（操作符函数化）                                   │</span><br><span class="line">│                                                                  │</span><br><span class="line">│  文件与路径                                                      │</span><br><span class="line">│    ├── os（操作系统接口）                                        │</span><br><span class="line">│    ├── os.path（路径操作）                                       │</span><br><span class="line">│    ├── pathlib（面向对象路径）                                   │</span><br><span class="line">│    └── shutil（高级文件操作）                                     │</span><br><span class="line">│                                                                  │</span><br><span class="line">│  网络与通信                                                      │</span><br><span class="line">│    ├── urllib（HTTP客户端）                                      │</span><br><span class="line">│    ├── socket（网络编程）                                        │</span><br><span class="line">│    └── http（HTTP协议）                                          │</span><br><span class="line">│                                                                  │</span><br><span class="line">│  并发与异步                                                      │</span><br><span class="line">│    ├── threading（线程）                                         │</span><br><span class="line">│    ├── multiprocessing（进程）                                   │</span><br><span class="line">│    ├── concurrent.futures（并发接口）                            │</span><br><span class="line">│    └── asyncio（异步IO）                                         │</span><br><span class="line">│                                                                  │</span><br><span class="line">│  数据持久化                                                      │</span><br><span class="line">│    ├── json（JSON序列化）                                        │</span><br><span class="line">│    ├── pickle（对象序列化）                                      │</span><br><span class="line">│    └── sqlite3（SQLite数据库）                                   │</span><br><span class="line">│                                                                  │</span><br><span class="line">│  时间与日期                                                      │</span><br><span class="line">│    ├── datetime（日期时间处理）                                  │</span><br><span class="line">│    ├── time（时间基础）                                          │</span><br><span class="line">│    └── calendar（日历操作）                                      │</span><br><span class="line">│                                                                  │</span><br><span class="line">│  其他工具                                                        │</span><br><span class="line">│    ├── logging（日志）                                           │</span><br><span class="line">│    ├── argparse（命令行参数）                                     │</span><br><span class="line">│    ├── configparser（配置文件）                                  │</span><br><span class="line">│    └── hashlib（哈希算法）                                       │</span><br><span class="line">│                                                                  │</span><br><span class="line">└──────────────────────────────────────────────────────────────────┘</span><br></pre></td></tr></table></figure><hr><h2 id="1-文本与字符串"><a href="#1-文本与字符串" class="headerlink" title="1. 文本与字符串"></a>1. 文本与字符串</h2><h3 id="1-1-re-正则表达式"><a href="#1-1-re-正则表达式" class="headerlink" title="1.1 re 正则表达式"></a>1.1 <code>re</code> 正则表达式</h3><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><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><span class="line"><span class="keyword">import</span> re</span><br><span class="line"></span><br><span class="line"><span class="comment"># 基础匹配</span></span><br><span class="line">text = <span class="string">&quot;Email: alice@example.com, Phone: 138-1234-5678&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># search：查找第一个匹配</span></span><br><span class="line"><span class="keyword">match</span> = re.search(<span class="string">r&quot;\w+@\w+\.\w+&quot;</span>, text)</span><br><span class="line"><span class="keyword">if</span> <span class="keyword">match</span>:</span><br><span class="line">    <span class="built_in">print</span>(<span class="keyword">match</span>.group())   <span class="comment"># alice@example.com</span></span><br><span class="line">    <span class="built_in">print</span>(<span class="keyword">match</span>.span())    <span class="comment"># (7, 24)</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># findall：查找所有匹配</span></span><br><span class="line">emails = re.findall(<span class="string">r&quot;\w+@\w+\.\w+&quot;</span>, text)</span><br><span class="line">phones = re.findall(<span class="string">r&quot;\d&#123;3&#125;-\d&#123;4&#125;-\d&#123;4&#125;&quot;</span>, text)</span><br><span class="line"></span><br><span class="line"><span class="comment"># finditer：返回迭代器（节省内存）</span></span><br><span class="line"><span class="keyword">for</span> <span class="keyword">match</span> <span class="keyword">in</span> re.finditer(<span class="string">r&quot;\d+&quot;</span>, text):</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;数字: <span class="subst">&#123;<span class="keyword">match</span>.group()&#125;</span>, 位置: <span class="subst">&#123;<span class="keyword">match</span>.span()&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># sub：替换</span></span><br><span class="line">redacted = re.sub(<span class="string">r&quot;\w+@\w+\.\w+&quot;</span>, <span class="string">&quot;[EMAIL]&quot;</span>, text)</span><br><span class="line"></span><br><span class="line"><span class="comment"># split：按模式分割</span></span><br><span class="line">parts = re.split(<span class="string">r&quot;[,;]\s*&quot;</span>, <span class="string">&quot;a, b; c, d&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 编译正则（频繁使用时提升性能）</span></span><br><span class="line">EMAIL_PATTERN = re.<span class="built_in">compile</span>(<span class="string">r&quot;^[\w.-]+@[\w.-]+\.\w+$&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(EMAIL_PATTERN.<span class="keyword">match</span>(<span class="string">&quot;test@example.com&quot;</span>))    <span class="comment"># Match</span></span><br><span class="line"><span class="built_in">print</span>(EMAIL_PATTERN.<span class="keyword">match</span>(<span class="string">&quot;invalid&quot;</span>))             <span class="comment"># None</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 命名捕获组</span></span><br><span class="line">pattern = re.<span class="built_in">compile</span>(<span class="string">r&quot;(?P&lt;year&gt;\d&#123;4&#125;)-(?P&lt;month&gt;\d&#123;2&#125;)-(?P&lt;day&gt;\d&#123;2&#125;)&quot;</span>)</span><br><span class="line"><span class="keyword">match</span> = pattern.<span class="keyword">match</span>(<span class="string">&quot;2024-07-20&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="keyword">match</span>.groupdict())   <span class="comment"># &#123;&#x27;year&#x27;: &#x27;2024&#x27;, &#x27;month&#x27;: &#x27;07&#x27;, &#x27;day&#x27;: &#x27;20&#x27;&#125;</span></span><br></pre></td></tr></table></figure><p><strong>常用正则模式速查</strong>：</p><table><thead><tr><th>模式</th><th>含义</th></tr></thead><tbody><tr><td><code>.</code></td><td>任意字符（除换行）</td></tr><tr><td><code>\d</code></td><td>数字 <code>[0-9]</code></td></tr><tr><td><code>\w</code></td><td>单词字符 <code>[a-zA-Z0-9_]</code></td></tr><tr><td><code>\s</code></td><td>空白字符</td></tr><tr><td><code>*</code></td><td>0 次或多次</td></tr><tr><td><code>+</code></td><td>1 次或多次</td></tr><tr><td><code>?</code></td><td>0 次或 1 次</td></tr><tr><td><code>{n,m}</code></td><td>n 到 m 次</td></tr><tr><td><code>^</code> &#x2F; <code>$</code></td><td>字符串开头&#x2F;结尾</td></tr><tr><td><code>()</code></td><td>捕获组</td></tr><tr><td><code>(?:...)</code></td><td>非捕获组</td></tr><tr><td><code>(?P&lt;name&gt;...)</code></td><td>命名捕获组</td></tr><tr><td><code>\b</code></td><td>单词边界</td></tr></tbody></table><p><strong>正则表达式工作流程</strong>：</p><figure class="highlight plaintext"><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><span class="line">文本输入 → 正则模式 → 匹配引擎 → 匹配结果/None</span><br><span class="line">    │           │           │</span><br><span class="line">    │           └→ 编译正则  │</span><br><span class="line">    │                       │</span><br><span class="line">    └───────────────────────┘</span><br><span class="line">              重复使用</span><br></pre></td></tr></table></figure><p><strong>工程实践建议</strong>：</p><ol><li>复杂正则使用 <code>re.compile()</code> 编译后复用，提升性能</li><li>使用命名捕获组 <code>(?P&lt;name&gt;...)</code> 让代码更易读</li><li>正则表达式调试困难，尽量保持简洁</li><li>对于简单模式，优先使用字符串方法（<code>str.startswith()</code>, <code>str.find()</code> 等）</li></ol><h3 id="1-2-string-与-textwrap"><a href="#1-2-string-与-textwrap" class="headerlink" title="1.2 string 与 textwrap"></a>1.2 <code>string</code> 与 <code>textwrap</code></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><span class="line"><span class="keyword">import</span> string</span><br><span class="line"><span class="keyword">import</span> textwrap</span><br><span class="line"></span><br><span class="line"><span class="comment"># string 常量</span></span><br><span class="line"><span class="built_in">print</span>(string.ascii_letters)   <span class="comment"># abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</span></span><br><span class="line"><span class="built_in">print</span>(string.digits)          <span class="comment"># 0123456789</span></span><br><span class="line"><span class="built_in">print</span>(string.punctuation)     <span class="comment"># !&quot;#$%&amp;&#x27;()*+,-./:;&lt;=&gt;?@[\]^_`&#123;|&#125;~</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># textwrap：文本格式化</span></span><br><span class="line">long_text = <span class="string">&quot;这是一个非常长的段落，需要自动换行和缩进处理。&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 自动换行</span></span><br><span class="line">wrapped = textwrap.fill(long_text, width=<span class="number">20</span>)</span><br><span class="line"><span class="built_in">print</span>(wrapped)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 添加缩进</span></span><br><span class="line">indented = textwrap.indent(long_text, <span class="string">&quot;    &quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(indented)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 去除缩进</span></span><br><span class="line">dedented = textwrap.dedent(<span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">    第一行</span></span><br><span class="line"><span class="string">    第二行</span></span><br><span class="line"><span class="string">    第三行</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 截断文本</span></span><br><span class="line">truncated = textwrap.shorten(<span class="string">&quot;这是一段很长的文本需要截断显示。&quot;</span>, width=<span class="number">20</span>)</span><br></pre></td></tr></table></figure><p><strong>应用场景</strong>：</p><ul><li><code>string.digits</code> + <code>string.ascii_letters</code>：生成验证码</li><li><code>textwrap.fill()</code>：格式化日志输出</li><li><code>textwrap.dedent()</code>：去除多行字符串的公共缩进</li></ul><hr><h2 id="2-数据结构增强"><a href="#2-数据结构增强" class="headerlink" title="2. 数据结构增强"></a>2. 数据结构增强</h2><h3 id="2-1-collections"><a href="#2-1-collections" class="headerlink" title="2.1 collections"></a>2.1 <code>collections</code></h3><p><code>collections</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> collections <span class="keyword">import</span> namedtuple, deque, Counter, OrderedDict, defaultdict, ChainMap</span><br><span class="line"></span><br><span class="line"><span class="comment"># namedtuple：轻量级不可变对象</span></span><br><span class="line">Point = namedtuple(<span class="string">&quot;Point&quot;</span>, [<span class="string">&quot;x&quot;</span>, <span class="string">&quot;y&quot;</span>])</span><br><span class="line">p = Point(<span class="number">3</span>, <span class="number">4</span>)</span><br><span class="line"><span class="built_in">print</span>(p.x, p.y)           <span class="comment"># 3 4</span></span><br><span class="line"><span class="built_in">print</span>(p._asdict())        <span class="comment"># &#123;&#x27;x&#x27;: 3, &#x27;y&#x27;: 4&#125;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># deque：双端队列（两端操作都是 O(1)）</span></span><br><span class="line">dq = deque([<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>])</span><br><span class="line">dq.append(<span class="number">4</span>)              <span class="comment"># 右端添加</span></span><br><span class="line">dq.appendleft(<span class="number">0</span>)          <span class="comment"># 左端添加</span></span><br><span class="line">dq.pop()                  <span class="comment"># 右端弹出</span></span><br><span class="line">dq.popleft()              <span class="comment"># 左端弹出</span></span><br><span class="line">dq.rotate(<span class="number">1</span>)              <span class="comment"># 旋转</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Counter：计数器</span></span><br><span class="line">c = Counter([<span class="string">&quot;a&quot;</span>, <span class="string">&quot;b&quot;</span>, <span class="string">&quot;a&quot;</span>, <span class="string">&quot;c&quot;</span>, <span class="string">&quot;a&quot;</span>, <span class="string">&quot;b&quot;</span>])</span><br><span class="line"><span class="built_in">print</span>(c)                  <span class="comment"># Counter(&#123;&#x27;a&#x27;: 3, &#x27;b&#x27;: 2, &#x27;c&#x27;: 1&#125;)</span></span><br><span class="line"><span class="built_in">print</span>(c.most_common(<span class="number">2</span>))   <span class="comment"># [(&#x27;a&#x27;, 3), (&#x27;b&#x27;, 2)]</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 统计字符频率</span></span><br><span class="line">char_count = Counter(<span class="string">&quot;mississippi&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(char_count.most_common(<span class="number">3</span>))</span><br><span class="line"></span><br><span class="line"><span class="comment"># defaultdict：带默认值的字典</span></span><br><span class="line">word_groups = defaultdict(<span class="built_in">list</span>)</span><br><span class="line"><span class="keyword">for</span> word <span class="keyword">in</span> [<span class="string">&quot;apple&quot;</span>, <span class="string">&quot;apricot&quot;</span>, <span class="string">&quot;banana&quot;</span>, <span class="string">&quot;blueberry&quot;</span>]:</span><br><span class="line">    word_groups[word[<span class="number">0</span>]].append(word)</span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">dict</span>(word_groups))   <span class="comment"># &#123;&#x27;a&#x27;: [&#x27;apple&#x27;, &#x27;apricot&#x27;], &#x27;b&#x27;: [&#x27;banana&#x27;, &#x27;blueberry&#x27;]&#125;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># OrderedDict：有序字典（3.7+ 普通 dict 已有序，但 OrderedDict 有特殊方法）</span></span><br><span class="line">od = OrderedDict([(<span class="string">&quot;a&quot;</span>, <span class="number">1</span>), (<span class="string">&quot;b&quot;</span>, <span class="number">2</span>)])</span><br><span class="line">od.move_to_end(<span class="string">&quot;a&quot;</span>)       <span class="comment"># 将 a 移到最后</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ChainMap：链式字典查找（不合并，只链接）</span></span><br><span class="line">defaults = &#123;<span class="string">&quot;theme&quot;</span>: <span class="string">&quot;default&quot;</span>, <span class="string">&quot;language&quot;</span>: <span class="string">&quot;en&quot;</span>&#125;</span><br><span class="line">user_prefs = &#123;<span class="string">&quot;theme&quot;</span>: <span class="string">&quot;dark&quot;</span>&#125;</span><br><span class="line">combined = ChainMap(user_prefs, defaults)</span><br><span class="line"><span class="built_in">print</span>(combined[<span class="string">&quot;theme&quot;</span>])    <span class="comment"># dark（从 user_prefs 找到）</span></span><br><span class="line"><span class="built_in">print</span>(combined[<span class="string">&quot;language&quot;</span>]) <span class="comment"># en（从 defaults 找到）</span></span><br></pre></td></tr></table></figure><p><strong>collections 各类型对比</strong>：</p><table><thead><tr><th>类型</th><th>特点</th><th>适用场景</th></tr></thead><tbody><tr><td><code>namedtuple</code></td><td>不可变，字段可通过属性访问</td><td>轻量级数据结构、记录</td></tr><tr><td><code>deque</code></td><td>双端 O(1) 操作</td><td>队列、栈、滑动窗口</td></tr><tr><td><code>Counter</code></td><td>自动计数</td><td>词频统计、投票计数</td></tr><tr><td><code>defaultdict</code></td><td>访问不存在键自动创建默认值</td><td>分组聚合</td></tr><tr><td><code>OrderedDict</code></td><td>保持插入顺序，支持移动</td><td>LRU 缓存、有序配置</td></tr><tr><td><code>ChainMap</code></td><td>链式查找，不复制</td><td>配置优先级、多字典合并</td></tr></tbody></table><h3 id="2-2-heapq-堆队列"><a href="#2-2-heapq-堆队列" class="headerlink" title="2.2 heapq 堆队列"></a>2.2 <code>heapq</code> 堆队列</h3><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><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><span class="line"><span class="keyword">import</span> heapq</span><br><span class="line"></span><br><span class="line"><span class="comment"># 堆（最小堆）：快速获取最小/最大元素</span></span><br><span class="line">nums = [<span class="number">5</span>, <span class="number">2</span>, <span class="number">8</span>, <span class="number">1</span>, <span class="number">9</span>, <span class="number">3</span>]</span><br><span class="line">heapq.heapify(nums)       <span class="comment"># 原地转堆，O(n)</span></span><br><span class="line"><span class="built_in">print</span>(heapq.heappop(nums))  <span class="comment"># 1（最小元素）</span></span><br><span class="line">heapq.heappush(nums, <span class="number">0</span>)   <span class="comment"># 添加元素，O(log n)</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 获取最大的 N 个元素（比排序后切片快）</span></span><br><span class="line">largest = heapq.nlargest(<span class="number">3</span>, [<span class="number">5</span>, <span class="number">2</span>, <span class="number">8</span>, <span class="number">1</span>, <span class="number">9</span>, <span class="number">3</span>])</span><br><span class="line">smallest = heapq.nsmallest(<span class="number">3</span>, [<span class="number">5</span>, <span class="number">2</span>, <span class="number">8</span>, <span class="number">1</span>, <span class="number">9</span>, <span class="number">3</span>])</span><br><span class="line"></span><br><span class="line"><span class="comment"># 实现优先队列</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">PriorityQueue</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="variable language_">self</span>._queue = []</span><br><span class="line">        <span class="variable language_">self</span>._index = <span class="number">0</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">push</span>(<span class="params">self, item, priority</span>):</span><br><span class="line">        <span class="comment"># (priority, index, item) 保证相同优先级按插入顺序排列</span></span><br><span class="line">        heapq.heappush(<span class="variable language_">self</span>._queue, (priority, <span class="variable language_">self</span>._index, item))</span><br><span class="line">        <span class="variable language_">self</span>._index += <span class="number">1</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">pop</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> heapq.heappop(<span class="variable language_">self</span>._queue)[-<span class="number">1</span>]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用优先队列</span></span><br><span class="line">pq = PriorityQueue()</span><br><span class="line">pq.push(<span class="string">&quot;task1&quot;</span>, <span class="number">2</span>)</span><br><span class="line">pq.push(<span class="string">&quot;task2&quot;</span>, <span class="number">1</span>)</span><br><span class="line">pq.push(<span class="string">&quot;task3&quot;</span>, <span class="number">1</span>)</span><br><span class="line"><span class="built_in">print</span>(pq.pop())   <span class="comment"># task2（优先级最高）</span></span><br><span class="line"><span class="built_in">print</span>(pq.pop())   <span class="comment"># task3（同优先级，先插入的先出）</span></span><br></pre></td></tr></table></figure><p><strong>堆操作时间复杂度</strong>：</p><table><thead><tr><th>操作</th><th>时间复杂度</th></tr></thead><tbody><tr><td><code>heapify()</code></td><td>O(n)</td></tr><tr><td><code>heappush()</code></td><td>O(log n)</td></tr><tr><td><code>heappop()</code></td><td>O(log n)</td></tr><tr><td><code>nlargest(n)</code></td><td>O(n log n)</td></tr><tr><td><code>nsmallest(n)</code></td><td>O(n log n)</td></tr></tbody></table><p><strong>应用场景</strong>：</p><ul><li>任务调度（优先队列）</li><li>Top-K 问题（获取最大&#x2F;最小的 N 个元素）</li><li>数据流中位数</li></ul><h3 id="2-3-bisect-二分查找"><a href="#2-3-bisect-二分查找" class="headerlink" title="2.3 bisect 二分查找"></a>2.3 <code>bisect</code> 二分查找</h3><p><code>bisect</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> bisect</span><br><span class="line"></span><br><span class="line"><span class="comment"># 维护有序列表</span></span><br><span class="line">sorted_list = [<span class="number">1</span>, <span class="number">3</span>, <span class="number">5</span>, <span class="number">7</span>, <span class="number">9</span>]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查找插入位置</span></span><br><span class="line">pos = bisect.bisect_left(sorted_list, <span class="number">6</span>)   <span class="comment"># 3（在 5 后面）</span></span><br><span class="line">pos = bisect.bisect_right(sorted_list, <span class="number">5</span>)  <span class="comment"># 3（在 5 后面）</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 插入并保持有序</span></span><br><span class="line">bisect.insort(sorted_list, <span class="number">6</span>)   <span class="comment"># [1, 3, 5, 6, 7, 9]</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 查找元素是否存在</span></span><br><span class="line">idx = bisect.bisect_left(sorted_list, <span class="number">7</span>)</span><br><span class="line"><span class="keyword">if</span> idx &lt; <span class="built_in">len</span>(sorted_list) <span class="keyword">and</span> sorted_list[idx] == <span class="number">7</span>:</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;找到 7&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 二分查找实现</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">binary_search</span>(<span class="params">arr, target</span>):</span><br><span class="line">    left, right = <span class="number">0</span>, <span class="built_in">len</span>(arr) - <span class="number">1</span></span><br><span class="line">    <span class="keyword">while</span> left &lt;= right:</span><br><span class="line">        mid = (left + right) // <span class="number">2</span></span><br><span class="line">        <span class="keyword">if</span> arr[mid] == target:</span><br><span class="line">            <span class="keyword">return</span> mid</span><br><span class="line">        <span class="keyword">elif</span> arr[mid] &lt; target:</span><br><span class="line">            left = mid + <span class="number">1</span></span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            right = mid - <span class="number">1</span></span><br><span class="line">    <span class="keyword">return</span> -<span class="number">1</span></span><br></pre></td></tr></table></figure><p><strong>bisect 函数对比</strong>：</p><table><thead><tr><th>函数</th><th>行为</th><th>返回值含义</th></tr></thead><tbody><tr><td><code>bisect_left()</code></td><td>查找第一个大于等于 target 的位置</td><td>插入位置（左侧）</td></tr><tr><td><code>bisect_right()</code></td><td>查找第一个大于 target 的位置</td><td>插入位置（右侧）</td></tr><tr><td><code>insort_left()</code></td><td>在 bisect_left 位置插入</td><td>无</td></tr><tr><td><code>insort_right()</code></td><td>在 bisect_right 位置插入</td><td>无</td></tr></tbody></table><p><strong>应用场景</strong>：</p><ul><li>维护有序数据结构</li><li>区间查询</li><li>时间序列数据查找</li></ul><hr><h2 id="3-函数式工具"><a href="#3-函数式工具" class="headerlink" title="3. 函数式工具"></a>3. 函数式工具</h2><h3 id="3-1-functools"><a href="#3-1-functools" class="headerlink" title="3.1 functools"></a>3.1 <code>functools</code></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><span class="line"><span class="keyword">from</span> functools <span class="keyword">import</span> partial, reduce, lru_cache, wraps, cmp_to_key, total_ordering</span><br><span class="line"></span><br><span class="line"><span class="comment"># partial：固定部分参数</span></span><br><span class="line">base_two = partial(<span class="built_in">int</span>, base=<span class="number">2</span>)</span><br><span class="line"><span class="built_in">print</span>(base_two(<span class="string">&quot;1010&quot;</span>))   <span class="comment"># 10</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># lru_cache：最近最少使用缓存</span></span><br><span class="line"><span class="meta">@lru_cache(<span class="params">maxsize=<span class="number">128</span></span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">fibonacci</span>(<span class="params">n</span>):</span><br><span class="line">    <span class="keyword">if</span> n &lt; <span class="number">2</span>:</span><br><span class="line">        <span class="keyword">return</span> n</span><br><span class="line">    <span class="keyword">return</span> fibonacci(n - <span class="number">1</span>) + fibonacci(n - <span class="number">2</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># total_ordering：自动生成比较方法</span></span><br><span class="line"><span class="meta">@total_ordering</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Version</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, major, minor, patch</span>):</span><br><span class="line">        <span class="variable language_">self</span>.version = (major, minor, patch)</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__eq__</span>(<span class="params">self, other</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>.version == other.version</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__lt__</span>(<span class="params">self, other</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>.version &lt; other.version</span><br><span class="line"></span><br><span class="line"><span class="comment"># cmp_to_key：将比较函数转为 key 函数</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">compare_length</span>(<span class="params">a, b</span>):</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">len</span>(a) - <span class="built_in">len</span>(b)</span><br><span class="line"></span><br><span class="line">words = [<span class="string">&quot;apple&quot;</span>, <span class="string">&quot;pie&quot;</span>, <span class="string">&quot;banana&quot;</span>]</span><br><span class="line">words.sort(key=cmp_to_key(compare_length))</span><br><span class="line"></span><br><span class="line"><span class="comment"># wraps：保留原函数元信息</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">my_decorator</span>(<span class="params">func</span>):</span><br><span class="line"><span class="meta">    @wraps(<span class="params">func</span>)</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">wrapper</span>(<span class="params">*args, **kwargs</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;Before&quot;</span>)</span><br><span class="line">        result = func(*args, **kwargs)</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;After&quot;</span>)</span><br><span class="line">        <span class="keyword">return</span> result</span><br><span class="line">    <span class="keyword">return</span> wrapper</span><br></pre></td></tr></table></figure><p><strong>functools 常用工具对比</strong>：</p><table><thead><tr><th>工具</th><th>作用</th><th>场景</th></tr></thead><tbody><tr><td><code>partial</code></td><td>固定函数部分参数</td><td>简化函数调用</td></tr><tr><td><code>reduce</code></td><td>累积操作</td><td>归约计算（求和、乘积等）</td></tr><tr><td><code>lru_cache</code></td><td>函数结果缓存</td><td>避免重复计算</td></tr><tr><td><code>wraps</code></td><td>保留函数元信息</td><td>装饰器中使用</td></tr><tr><td><code>cmp_to_key</code></td><td>比较函数转 key</td><td>自定义排序</td></tr><tr><td><code>total_ordering</code></td><td>自动生成比较方法</td><td>类定义</td></tr></tbody></table><h3 id="3-2-itertools"><a href="#3-2-itertools" class="headerlink" title="3.2 itertools"></a>3.2 <code>itertools</code></h3><p><code>itertools</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> itertools</span><br><span class="line"></span><br><span class="line"><span class="comment"># 无限迭代器</span></span><br><span class="line">count = itertools.count(start=<span class="number">10</span>, step=<span class="number">2</span>)      <span class="comment"># 10, 12, 14, ...</span></span><br><span class="line">cycle = itertools.cycle([<span class="string">&quot;A&quot;</span>, <span class="string">&quot;B&quot;</span>, <span class="string">&quot;C&quot;</span>])        <span class="comment"># A, B, C, A, B, C, ...</span></span><br><span class="line">repeat = itertools.repeat(<span class="string">&quot;X&quot;</span>, <span class="number">5</span>)              <span class="comment"># X, X, X, X, X</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 组合生成器</span></span><br><span class="line">items = [<span class="string">&quot;A&quot;</span>, <span class="string">&quot;B&quot;</span>, <span class="string">&quot;C&quot;</span>]</span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">list</span>(itertools.permutations(items, <span class="number">2</span>)))   <span class="comment"># 排列：AB, AC, BA, BC, CA, CB</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">list</span>(itertools.combinations(items, <span class="number">2</span>)))   <span class="comment"># 组合：AB, AC, BC</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">list</span>(itertools.combinations_with_replacement(items, <span class="number">2</span>)))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 累积</span></span><br><span class="line">cumulative = <span class="built_in">list</span>(itertools.accumulate([<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>]))   <span class="comment"># [1, 3, 6, 10, 15]</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 分组（注意：需要先排序）</span></span><br><span class="line">data = [(<span class="string">&quot;A&quot;</span>, <span class="number">1</span>), (<span class="string">&quot;A&quot;</span>, <span class="number">2</span>), (<span class="string">&quot;B&quot;</span>, <span class="number">3</span>), (<span class="string">&quot;B&quot;</span>, <span class="number">4</span>)]</span><br><span class="line">sorted_data = <span class="built_in">sorted</span>(data, key=<span class="keyword">lambda</span> x: x[<span class="number">0</span>])</span><br><span class="line"><span class="keyword">for</span> key, group <span class="keyword">in</span> itertools.groupby(sorted_data, key=<span class="keyword">lambda</span> x: x[<span class="number">0</span>]):</span><br><span class="line">    <span class="built_in">print</span>(key, <span class="built_in">list</span>(group))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 链式迭代</span></span><br><span class="line">combined = itertools.chain([<span class="number">1</span>, <span class="number">2</span>], [<span class="number">3</span>, <span class="number">4</span>], [<span class="number">5</span>, <span class="number">6</span>])</span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">list</span>(combined))   <span class="comment"># [1, 2, 3, 4, 5, 6]</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># zip_longest：不等长序列</span></span><br><span class="line">names = [<span class="string">&quot;Alice&quot;</span>, <span class="string">&quot;Bob&quot;</span>]</span><br><span class="line">ages = [<span class="number">30</span>, <span class="number">25</span>, <span class="number">35</span>]</span><br><span class="line"><span class="keyword">for</span> name, age <span class="keyword">in</span> itertools.zip_longest(names, ages, fillvalue=<span class="string">&quot;N/A&quot;</span>):</span><br><span class="line">    <span class="built_in">print</span>(name, age)</span><br><span class="line"></span><br><span class="line"><span class="comment"># islice：切片迭代器</span></span><br><span class="line">nums = <span class="built_in">iter</span>(<span class="built_in">range</span>(<span class="number">10</span>))</span><br><span class="line">sliced = itertools.islice(nums, <span class="number">2</span>, <span class="number">8</span>, <span class="number">2</span>)   <span class="comment"># 2, 4, 6</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># tee：复制迭代器</span></span><br><span class="line">it1, it2 = itertools.tee(<span class="built_in">range</span>(<span class="number">5</span>))</span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">list</span>(it1))   <span class="comment"># [0, 1, 2, 3, 4]</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">list</span>(it2))   <span class="comment"># [0, 1, 2, 3, 4]</span></span><br></pre></td></tr></table></figure><p><strong>itertools 分类速查</strong>：</p><table><thead><tr><th>类别</th><th>函数</th><th>功能</th></tr></thead><tbody><tr><td><strong>无限迭代</strong></td><td><code>count</code>, <code>cycle</code>, <code>repeat</code></td><td>生成无限序列</td></tr><tr><td><strong>终止迭代</strong></td><td><code>accumulate</code>, <code>chain</code>, <code>islice</code>, <code>tee</code></td><td>处理有限序列</td></tr><tr><td><strong>组合生成</strong></td><td><code>permutations</code>, <code>combinations</code>, <code>product</code></td><td>排列组合</td></tr><tr><td><strong>分组筛选</strong></td><td><code>groupby</code>, <code>filterfalse</code>, <code>compress</code></td><td>分组和过滤</td></tr></tbody></table><h3 id="3-3-operator"><a href="#3-3-operator" class="headerlink" title="3.3 operator"></a>3.3 <code>operator</code></h3><p><code>operator</code> 模块提供了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><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><span class="line"><span class="keyword">from</span> operator <span class="keyword">import</span> itemgetter, attrgetter, methodcaller, add, mul, lt, eq</span><br><span class="line"></span><br><span class="line"><span class="comment"># itemgetter：用于排序和取值</span></span><br><span class="line">data = [&#123;<span class="string">&quot;name&quot;</span>: <span class="string">&quot;Alice&quot;</span>, <span class="string">&quot;age&quot;</span>: <span class="number">30</span>&#125;, &#123;<span class="string">&quot;name&quot;</span>: <span class="string">&quot;Bob&quot;</span>, <span class="string">&quot;age&quot;</span>: <span class="number">25</span>&#125;]</span><br><span class="line">sorted_data = <span class="built_in">sorted</span>(data, key=itemgetter(<span class="string">&quot;age&quot;</span>))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 多个键排序</span></span><br><span class="line">sorted_by_age_name = <span class="built_in">sorted</span>(data, key=itemgetter(<span class="string">&quot;age&quot;</span>, <span class="string">&quot;name&quot;</span>))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 批量取值</span></span><br><span class="line">get_name = itemgetter(<span class="string">&quot;name&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">list</span>(<span class="built_in">map</span>(get_name, data)))   <span class="comment"># [&#x27;Alice&#x27;, &#x27;Bob&#x27;]</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># attrgetter：获取对象属性</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Person</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, name, age</span>):</span><br><span class="line">        <span class="variable language_">self</span>.name = name</span><br><span class="line">        <span class="variable language_">self</span>.age = age</span><br><span class="line"></span><br><span class="line">people = [Person(<span class="string">&quot;Alice&quot;</span>, <span class="number">30</span>), Person(<span class="string">&quot;Bob&quot;</span>, <span class="number">25</span>)]</span><br><span class="line">sorted_people = <span class="built_in">sorted</span>(people, key=attrgetter(<span class="string">&quot;age&quot;</span>))</span><br><span class="line"></span><br><span class="line"><span class="comment"># methodcaller：调用方法</span></span><br><span class="line">upper = methodcaller(<span class="string">&quot;upper&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(upper(<span class="string">&quot;hello&quot;</span>))   <span class="comment"># HELLO</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 指定参数调用</span></span><br><span class="line">replace = methodcaller(<span class="string">&quot;replace&quot;</span>, <span class="string">&quot;l&quot;</span>, <span class="string">&quot;x&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(replace(<span class="string">&quot;hello&quot;</span>))   <span class="comment"># hexlo</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 算术操作符</span></span><br><span class="line"><span class="built_in">print</span>(add(<span class="number">3</span>, <span class="number">5</span>))        <span class="comment"># 8</span></span><br><span class="line"><span class="built_in">print</span>(mul(<span class="number">3</span>, <span class="number">5</span>))        <span class="comment"># 15</span></span><br><span class="line"><span class="built_in">print</span>(lt(<span class="number">3</span>, <span class="number">5</span>))         <span class="comment"># True</span></span><br></pre></td></tr></table></figure><p><strong>operator 常用函数</strong>：</p><table><thead><tr><th>类别</th><th>函数</th><th>对应操作</th></tr></thead><tbody><tr><td><strong>算术</strong></td><td><code>add</code>, <code>sub</code>, <code>mul</code>, <code>truediv</code>, <code>floordiv</code></td><td><code>+</code>, <code>-</code>, <code>*</code>, <code>/</code>, <code>//</code></td></tr><tr><td><strong>比较</strong></td><td><code>eq</code>, <code>ne</code>, <code>lt</code>, <code>le</code>, <code>gt</code>, <code>ge</code></td><td><code>==</code>, <code>!=</code>, <code>&lt;</code>, <code>&lt;=</code>, <code>&gt;</code>, <code>&gt;=</code></td></tr><tr><td><strong>逻辑</strong></td><td><code>not_</code>, <code>truth</code>, <code>is_</code>, <code>is_not</code></td><td><code>not</code>, <code>bool()</code>, <code>is</code>, <code>is not</code></td></tr><tr><td><strong>序列</strong></td><td><code>itemgetter</code>, <code>attrgetter</code>, <code>methodcaller</code></td><td>索引、属性、方法调用</td></tr></tbody></table><hr><h2 id="4-文件与路径"><a href="#4-文件与路径" class="headerlink" title="4. 文件与路径"></a>4. 文件与路径</h2><h3 id="4-1-os-模块"><a href="#4-1-os-模块" class="headerlink" title="4.1 os 模块"></a>4.1 <code>os</code> 模块</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><span class="line"><span class="keyword">import</span> os</span><br><span class="line"></span><br><span class="line"><span class="comment"># 目录操作</span></span><br><span class="line"><span class="built_in">print</span>(os.getcwd())           <span class="comment"># 获取当前工作目录</span></span><br><span class="line">os.chdir(<span class="string">&quot;/path/to/dir&quot;</span>)     <span class="comment"># 切换目录</span></span><br><span class="line">os.makedirs(<span class="string">&quot;dir/subdir&quot;</span>, exist_ok=<span class="literal">True</span>)  <span class="comment"># 创建多级目录</span></span><br><span class="line">os.rmdir(<span class="string">&quot;empty_dir&quot;</span>)        <span class="comment"># 删除空目录</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 文件操作</span></span><br><span class="line">os.rename(<span class="string">&quot;old.txt&quot;</span>, <span class="string">&quot;new.txt&quot;</span>)</span><br><span class="line">os.remove(<span class="string">&quot;file.txt&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 路径操作（建议使用 pathlib）</span></span><br><span class="line">os.path.join(<span class="string">&quot;dir&quot;</span>, <span class="string">&quot;file.txt&quot;</span>)</span><br><span class="line">os.path.abspath(<span class="string">&quot;relative/path&quot;</span>)</span><br><span class="line">os.path.exists(<span class="string">&quot;path/to/file&quot;</span>)</span><br><span class="line">os.path.isfile(<span class="string">&quot;file.txt&quot;</span>)</span><br><span class="line">os.path.isdir(<span class="string">&quot;dir&quot;</span>)</span><br><span class="line">os.path.getsize(<span class="string">&quot;file.txt&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 环境变量</span></span><br><span class="line"><span class="built_in">print</span>(os.environ.get(<span class="string">&quot;PATH&quot;</span>))</span><br><span class="line">os.environ[<span class="string">&quot;MY_VAR&quot;</span>] = <span class="string">&quot;value&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 执行命令</span></span><br><span class="line">result = os.system(<span class="string">&quot;ls -l&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 遍历目录</span></span><br><span class="line"><span class="keyword">for</span> root, dirs, files <span class="keyword">in</span> os.walk(<span class="string">&quot;directory&quot;</span>):</span><br><span class="line">    <span class="keyword">for</span> file <span class="keyword">in</span> files:</span><br><span class="line">        <span class="built_in">print</span>(os.path.join(root, file))</span><br></pre></td></tr></table></figure><h3 id="4-2-pathlib（面向对象路径）"><a href="#4-2-pathlib（面向对象路径）" class="headerlink" title="4.2 pathlib（面向对象路径）"></a>4.2 <code>pathlib</code>（面向对象路径）</h3><p>Python 3.4+ 引入的面向对象路径处理方式，比 <code>os.path</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> pathlib <span class="keyword">import</span> Path</span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建路径对象</span></span><br><span class="line">p = Path(<span class="string">&quot;docs&quot;</span>) / <span class="string">&quot;guide&quot;</span> / <span class="string">&quot;readme.txt&quot;</span></span><br><span class="line"><span class="built_in">print</span>(p)                    <span class="comment"># docs/guide/readme.txt</span></span><br><span class="line"><span class="built_in">print</span>(p.resolve())          <span class="comment"># 绝对路径</span></span><br><span class="line"><span class="built_in">print</span>(p.exists())           <span class="comment"># 是否存在</span></span><br><span class="line"><span class="built_in">print</span>(p.is_file())          <span class="comment"># 是否是文件</span></span><br><span class="line"><span class="built_in">print</span>(p.is_dir())           <span class="comment"># 是否是目录</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 获取路径信息</span></span><br><span class="line"><span class="built_in">print</span>(p.name)               <span class="comment"># readme.txt</span></span><br><span class="line"><span class="built_in">print</span>(p.stem)               <span class="comment"># readme</span></span><br><span class="line"><span class="built_in">print</span>(p.suffix)             <span class="comment"># .txt</span></span><br><span class="line"><span class="built_in">print</span>(p.parent)             <span class="comment"># docs/guide</span></span><br><span class="line"><span class="built_in">print</span>(p.parents[<span class="number">0</span>])         <span class="comment"># docs/guide</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建目录</span></span><br><span class="line">p.mkdir(parents=<span class="literal">True</span>, exist_ok=<span class="literal">True</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 读取文件（推荐使用 with 语句）</span></span><br><span class="line">content = p.read_text(encoding=<span class="string">&quot;utf-8&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 写入文件</span></span><br><span class="line">p.write_text(<span class="string">&quot;Hello, World!&quot;</span>, encoding=<span class="string">&quot;utf-8&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 遍历目录</span></span><br><span class="line"><span class="keyword">for</span> item <span class="keyword">in</span> Path(<span class="string">&quot;.&quot;</span>).glob(<span class="string">&quot;**/*.py&quot;</span>):</span><br><span class="line">    <span class="built_in">print</span>(item)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 路径比较</span></span><br><span class="line">p1 = Path(<span class="string">&quot;docs/readme.txt&quot;</span>)</span><br><span class="line">p2 = Path(<span class="string">&quot;docs&quot;</span>) / <span class="string">&quot;readme.txt&quot;</span></span><br><span class="line"><span class="built_in">print</span>(p1 == p2)             <span class="comment"># True</span></span><br></pre></td></tr></table></figure><p><strong>pathlib vs os.path</strong>：</p><table><thead><tr><th>操作</th><th>os.path</th><th>pathlib</th></tr></thead><tbody><tr><td>连接路径</td><td><code>os.path.join(a, b)</code></td><td><code>a / b</code></td></tr><tr><td>绝对路径</td><td><code>os.path.abspath(p)</code></td><td><code>p.resolve()</code></td></tr><tr><td>判断存在</td><td><code>os.path.exists(p)</code></td><td><code>p.exists()</code></td></tr><tr><td>读取文本</td><td><code>open(p).read()</code></td><td><code>p.read_text()</code></td></tr><tr><td>写入文本</td><td><code>open(p, &#39;w&#39;).write()</code></td><td><code>p.write_text()</code></td></tr></tbody></table><h3 id="4-3-shutil（高级文件操作）"><a href="#4-3-shutil（高级文件操作）" class="headerlink" title="4.3 shutil（高级文件操作）"></a>4.3 <code>shutil</code>（高级文件操作）</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><span class="line"><span class="keyword">import</span> shutil</span><br><span class="line"></span><br><span class="line"><span class="comment"># 复制文件</span></span><br><span class="line">shutil.copy(<span class="string">&quot;src.txt&quot;</span>, <span class="string">&quot;dst.txt&quot;</span>)</span><br><span class="line">shutil.copy2(<span class="string">&quot;src.txt&quot;</span>, <span class="string">&quot;dst.txt&quot;</span>)  <span class="comment"># 保留元数据</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 复制目录（递归）</span></span><br><span class="line">shutil.copytree(<span class="string">&quot;src_dir&quot;</span>, <span class="string">&quot;dst_dir&quot;</span>, dirs_exist_ok=<span class="literal">True</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 移动文件/目录</span></span><br><span class="line">shutil.move(<span class="string">&quot;src&quot;</span>, <span class="string">&quot;dst&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 删除目录（递归）</span></span><br><span class="line">shutil.rmtree(<span class="string">&quot;dir_to_delete&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 文件压缩</span></span><br><span class="line">shutil.make_archive(<span class="string">&quot;archive&quot;</span>, <span class="string">&quot;zip&quot;</span>, <span class="string">&quot;source_dir&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 文件解压</span></span><br><span class="line">shutil.unpack_archive(<span class="string">&quot;archive.zip&quot;</span>, <span class="string">&quot;target_dir&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 获取磁盘使用情况</span></span><br><span class="line">disk_usage = shutil.disk_usage(<span class="string">&quot;/&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;总空间: <span class="subst">&#123;disk_usage.total / <span class="number">1e9</span>:<span class="number">.2</span>f&#125;</span> GB&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;已使用: <span class="subst">&#123;disk_usage.used / <span class="number">1e9</span>:<span class="number">.2</span>f&#125;</span> GB&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;可用空间: <span class="subst">&#123;disk_usage.free / <span class="number">1e9</span>:<span class="number">.2</span>f&#125;</span> GB&quot;</span>)</span><br></pre></td></tr></table></figure><hr><h2 id="5-网络与通信"><a href="#5-网络与通信" class="headerlink" title="5. 网络与通信"></a>5. 网络与通信</h2><h3 id="5-1-urllib（HTTP-客户端）"><a href="#5-1-urllib（HTTP-客户端）" class="headerlink" title="5.1 urllib（HTTP 客户端）"></a>5.1 <code>urllib</code>（HTTP 客户端）</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><span class="line"><span class="keyword">from</span> urllib <span class="keyword">import</span> request, parse, error</span><br><span class="line"></span><br><span class="line"><span class="comment"># 发送 GET 请求</span></span><br><span class="line">response = request.urlopen(<span class="string">&quot;https://api.example.com/data&quot;</span>)</span><br><span class="line">data = response.read().decode(<span class="string">&quot;utf-8&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 发送 POST 请求</span></span><br><span class="line">data = &#123;<span class="string">&quot;key&quot;</span>: <span class="string">&quot;value&quot;</span>&#125;</span><br><span class="line">encoded_data = parse.urlencode(data).encode(<span class="string">&quot;utf-8&quot;</span>)</span><br><span class="line">response = request.urlopen(<span class="string">&quot;https://api.example.com/submit&quot;</span>, data=encoded_data)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 设置请求头</span></span><br><span class="line">headers = &#123;<span class="string">&quot;User-Agent&quot;</span>: <span class="string">&quot;Mozilla/5.0&quot;</span>&#125;</span><br><span class="line">req = request.Request(<span class="string">&quot;https://example.com&quot;</span>, headers=headers)</span><br><span class="line">response = request.urlopen(req)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 处理异常</span></span><br><span class="line"><span class="keyword">try</span>:</span><br><span class="line">    response = request.urlopen(<span class="string">&quot;https://nonexistent.example.com&quot;</span>)</span><br><span class="line"><span class="keyword">except</span> error.HTTPError <span class="keyword">as</span> e:</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;HTTP Error: <span class="subst">&#123;e.code&#125;</span>&quot;</span>)</span><br><span class="line"><span class="keyword">except</span> error.URLError <span class="keyword">as</span> e:</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;URL Error: <span class="subst">&#123;e.reason&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 下载文件</span></span><br><span class="line">request.urlretrieve(<span class="string">&quot;https://example.com/image.jpg&quot;</span>, <span class="string">&quot;local_image.jpg&quot;</span>)</span><br></pre></td></tr></table></figure><h3 id="5-2-http-server（内置-Web-服务器）"><a href="#5-2-http-server（内置-Web-服务器）" class="headerlink" title="5.2 http.server（内置 Web 服务器）"></a>5.2 <code>http.server</code>（内置 Web 服务器）</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><span class="line"><span class="keyword">import</span> http.server</span><br><span class="line"><span class="keyword">import</span> socketserver</span><br><span class="line"></span><br><span class="line"><span class="comment"># 启动简单 HTTP 服务器</span></span><br><span class="line">PORT = <span class="number">8000</span></span><br><span class="line">Handler = http.server.SimpleHTTPRequestHandler</span><br><span class="line"></span><br><span class="line"><span class="keyword">with</span> socketserver.TCPServer((<span class="string">&quot;&quot;</span>, PORT), Handler) <span class="keyword">as</span> httpd:</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;Serving at port <span class="subst">&#123;PORT&#125;</span>&quot;</span>)</span><br><span class="line">    httpd.serve_forever()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 命令行方式：python -m http.server 8000</span></span><br></pre></td></tr></table></figure><hr><h2 id="6-并发与异步"><a href="#6-并发与异步" class="headerlink" title="6. 并发与异步"></a>6. 并发与异步</h2><h3 id="6-1-threading（线程）"><a href="#6-1-threading（线程）" class="headerlink" title="6.1 threading（线程）"></a>6.1 <code>threading</code>（线程）</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><span class="line"><span class="keyword">import</span> threading</span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">worker</span>(<span class="params">name, delay</span>):</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">5</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;<span class="subst">&#123;name&#125;</span>: <span class="subst">&#123;i&#125;</span>&quot;</span>)</span><br><span class="line">        time.sleep(delay)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建线程</span></span><br><span class="line">t1 = threading.Thread(target=worker, args=(<span class="string">&quot;Thread-1&quot;</span>, <span class="number">0.5</span>))</span><br><span class="line">t2 = threading.Thread(target=worker, args=(<span class="string">&quot;Thread-2&quot;</span>, <span class="number">0.5</span>))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 启动线程</span></span><br><span class="line">t1.start()</span><br><span class="line">t2.start()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 等待线程结束</span></span><br><span class="line">t1.join()</span><br><span class="line">t2.join()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 线程锁（防止竞态条件）</span></span><br><span class="line">lock = threading.Lock()</span><br><span class="line">shared_counter = <span class="number">0</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">increment</span>():</span><br><span class="line">    <span class="keyword">global</span> shared_counter</span><br><span class="line">    <span class="keyword">with</span> lock:</span><br><span class="line">        shared_counter += <span class="number">1</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 线程本地存储</span></span><br><span class="line">local_data = threading.local()</span><br><span class="line">local_data.user_id = <span class="string">&quot;123&quot;</span></span><br></pre></td></tr></table></figure><h3 id="6-2-multiprocessing（进程）"><a href="#6-2-multiprocessing（进程）" class="headerlink" title="6.2 multiprocessing（进程）"></a>6.2 <code>multiprocessing</code>（进程）</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><span class="line"><span class="keyword">import</span> multiprocessing</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">worker</span>(<span class="params">num</span>):</span><br><span class="line">    <span class="keyword">return</span> num * num</span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建进程池</span></span><br><span class="line"><span class="keyword">with</span> multiprocessing.Pool(processes=<span class="number">4</span>) <span class="keyword">as</span> pool:</span><br><span class="line">    results = pool.<span class="built_in">map</span>(worker, [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>])</span><br><span class="line">    <span class="built_in">print</span>(results)   <span class="comment"># [1, 4, 9, 16, 25]</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 进程间通信（Queue）</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">producer</span>(<span class="params">queue</span>):</span><br><span class="line">    <span class="keyword">for</span> item <span class="keyword">in</span> [<span class="string">&quot;A&quot;</span>, <span class="string">&quot;B&quot;</span>, <span class="string">&quot;C&quot;</span>]:</span><br><span class="line">        queue.put(item)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">consumer</span>(<span class="params">queue</span>):</span><br><span class="line">    <span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">        item = queue.get()</span><br><span class="line">        <span class="keyword">if</span> item <span class="keyword">is</span> <span class="literal">None</span>:</span><br><span class="line">            <span class="keyword">break</span></span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;Consumed: <span class="subst">&#123;item&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    queue = multiprocessing.Queue()</span><br><span class="line">    p1 = multiprocessing.Process(target=producer, args=(queue,))</span><br><span class="line">    p2 = multiprocessing.Process(target=consumer, args=(queue,))</span><br><span class="line">    p1.start()</span><br><span class="line">    p2.start()</span><br><span class="line">    p1.join()</span><br><span class="line">    queue.put(<span class="literal">None</span>)</span><br><span class="line">    p2.join()</span><br></pre></td></tr></table></figure><h3 id="6-3-concurrent-futures（高级并发接口）"><a href="#6-3-concurrent-futures（高级并发接口）" class="headerlink" title="6.3 concurrent.futures（高级并发接口）"></a>6.3 <code>concurrent.futures</code>（高级并发接口）</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><span class="line"><span class="keyword">from</span> concurrent.futures <span class="keyword">import</span> ThreadPoolExecutor, ProcessPoolExecutor</span><br><span class="line"></span><br><span class="line"><span class="comment"># 线程池</span></span><br><span class="line"><span class="keyword">with</span> ThreadPoolExecutor(max_workers=<span class="number">4</span>) <span class="keyword">as</span> executor:</span><br><span class="line">    future = executor.submit(worker, <span class="string">&quot;task1&quot;</span>)</span><br><span class="line">    result = future.result()</span><br><span class="line"></span><br><span class="line">    results = <span class="built_in">list</span>(executor.<span class="built_in">map</span>(worker, [<span class="string">&quot;task1&quot;</span>, <span class="string">&quot;task2&quot;</span>, <span class="string">&quot;task3&quot;</span>]))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 进程池（CPU 密集型任务）</span></span><br><span class="line"><span class="keyword">with</span> ProcessPoolExecutor(max_workers=<span class="number">4</span>) <span class="keyword">as</span> executor:</span><br><span class="line">    results = executor.<span class="built_in">map</span>(intensive_computation, data)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 获取 Future 对象</span></span><br><span class="line">futures = [executor.submit(task, arg) <span class="keyword">for</span> arg <span class="keyword">in</span> args]</span><br><span class="line"><span class="keyword">for</span> future <span class="keyword">in</span> concurrent.futures.as_completed(futures):</span><br><span class="line">    result = future.result()</span><br></pre></td></tr></table></figure><hr><h2 id="7-数据持久化"><a href="#7-数据持久化" class="headerlink" title="7. 数据持久化"></a>7. 数据持久化</h2><h3 id="7-1-json（JSON-序列化）"><a href="#7-1-json（JSON-序列化）" class="headerlink" title="7.1 json（JSON 序列化）"></a>7.1 <code>json</code>（JSON 序列化）</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><span class="line"><span class="keyword">import</span> json</span><br><span class="line"></span><br><span class="line"><span class="comment"># 序列化</span></span><br><span class="line">data = &#123;<span class="string">&quot;name&quot;</span>: <span class="string">&quot;Alice&quot;</span>, <span class="string">&quot;age&quot;</span>: <span class="number">30</span>, <span class="string">&quot;hobbies&quot;</span>: [<span class="string">&quot;reading&quot;</span>, <span class="string">&quot;coding&quot;</span>]&#125;</span><br><span class="line">json_str = json.dumps(data, indent=<span class="number">2</span>, ensure_ascii=<span class="literal">False</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 写入文件</span></span><br><span class="line"><span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">&quot;data.json&quot;</span>, <span class="string">&quot;w&quot;</span>, encoding=<span class="string">&quot;utf-8&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">    json.dump(data, f, indent=<span class="number">2</span>, ensure_ascii=<span class="literal">False</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 反序列化</span></span><br><span class="line">json_str = <span class="string">&#x27;&#123;&quot;name&quot;: &quot;Alice&quot;, &quot;age&quot;: 30&#125;&#x27;</span></span><br><span class="line">data = json.loads(json_str)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 从文件读取</span></span><br><span class="line"><span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">&quot;data.json&quot;</span>, <span class="string">&quot;r&quot;</span>, encoding=<span class="string">&quot;utf-8&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">    data = json.load(f)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 自定义编码器</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">PersonEncoder</span>(json.JSONEncoder):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">default</span>(<span class="params">self, obj</span>):</span><br><span class="line">        <span class="keyword">if</span> <span class="built_in">isinstance</span>(obj, Person):</span><br><span class="line">            <span class="keyword">return</span> &#123;<span class="string">&quot;name&quot;</span>: obj.name, <span class="string">&quot;age&quot;</span>: obj.age&#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">super</span>().default(obj)</span><br><span class="line"></span><br><span class="line">json.dumps(person, cls=PersonEncoder)</span><br></pre></td></tr></table></figure><h3 id="7-2-pickle（对象序列化）"><a href="#7-2-pickle（对象序列化）" class="headerlink" title="7.2 pickle（对象序列化）"></a>7.2 <code>pickle</code>（对象序列化）</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><span class="line"><span class="keyword">import</span> pickle</span><br><span class="line"></span><br><span class="line"><span class="comment"># 序列化</span></span><br><span class="line">data = &#123;<span class="string">&quot;name&quot;</span>: <span class="string">&quot;Alice&quot;</span>, <span class="string">&quot;age&quot;</span>: <span class="number">30</span>&#125;</span><br><span class="line">pickled = pickle.dumps(data)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 写入文件</span></span><br><span class="line"><span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">&quot;data.pkl&quot;</span>, <span class="string">&quot;wb&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">    pickle.dump(data, f)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 反序列化</span></span><br><span class="line">data = pickle.loads(pickled)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 从文件读取</span></span><br><span class="line"><span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">&quot;data.pkl&quot;</span>, <span class="string">&quot;rb&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">    data = pickle.load(f)</span><br></pre></td></tr></table></figure><p><strong>JSON vs Pickle</strong>：</p><table><thead><tr><th>特性</th><th>JSON</th><th>Pickle</th></tr></thead><tbody><tr><td>可读性</td><td>人类可读</td><td>二进制格式</td></tr><tr><td>兼容性</td><td>跨语言</td><td>仅 Python</td></tr><tr><td>支持类型</td><td>基础类型</td><td>几乎所有 Python 对象</td></tr><tr><td>安全性</td><td>安全</td><td>有安全风险（不要加载不可信数据）</td></tr><tr><td>文件大小</td><td>较大</td><td>较小</td></tr></tbody></table><h3 id="7-3-sqlite3（轻量级数据库）"><a href="#7-3-sqlite3（轻量级数据库）" class="headerlink" title="7.3 sqlite3（轻量级数据库）"></a>7.3 <code>sqlite3</code>（轻量级数据库）</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><span class="line"><span class="keyword">import</span> sqlite3</span><br><span class="line"></span><br><span class="line"><span class="comment"># 连接数据库（不存在则创建）</span></span><br><span class="line">conn = sqlite3.connect(<span class="string">&quot;example.db&quot;</span>)</span><br><span class="line">cursor = conn.cursor()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建表</span></span><br><span class="line">cursor.execute(<span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">    CREATE TABLE IF NOT EXISTS users (</span></span><br><span class="line"><span class="string">        id INTEGER PRIMARY KEY AUTOINCREMENT,</span></span><br><span class="line"><span class="string">        name TEXT NOT NULL,</span></span><br><span class="line"><span class="string">        age INTEGER</span></span><br><span class="line"><span class="string">    )</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 插入数据</span></span><br><span class="line">cursor.execute(<span class="string">&quot;INSERT INTO users (name, age) VALUES (?, ?)&quot;</span>, (<span class="string">&quot;Alice&quot;</span>, <span class="number">30</span>))</span><br><span class="line">conn.commit()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查询数据</span></span><br><span class="line">cursor.execute(<span class="string">&quot;SELECT * FROM users&quot;</span>)</span><br><span class="line">rows = cursor.fetchall()</span><br><span class="line"><span class="keyword">for</span> row <span class="keyword">in</span> rows:</span><br><span class="line">    <span class="built_in">print</span>(row)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 参数化查询（防止 SQL 注入）</span></span><br><span class="line">cursor.execute(<span class="string">&quot;SELECT * FROM users WHERE age &gt; ?&quot;</span>, (<span class="number">25</span>,))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用上下文管理器</span></span><br><span class="line"><span class="keyword">with</span> sqlite3.connect(<span class="string">&quot;example.db&quot;</span>) <span class="keyword">as</span> conn:</span><br><span class="line">    cursor = conn.cursor()</span><br><span class="line">    cursor.execute(<span class="string">&quot;SELECT * FROM users&quot;</span>)</span><br><span class="line"></span><br><span class="line">conn.close()</span><br></pre></td></tr></table></figure><hr><h2 id="8-时间与日期"><a href="#8-时间与日期" class="headerlink" title="8. 时间与日期"></a>8. 时间与日期</h2><h3 id="8-1-time-模块"><a href="#8-1-time-模块" class="headerlink" title="8.1 time 模块"></a>8.1 <code>time</code> 模块</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><span class="line"><span class="keyword">import</span> time</span><br><span class="line"></span><br><span class="line"><span class="comment"># 时间戳（秒）</span></span><br><span class="line">now = time.time()</span><br><span class="line"><span class="built_in">print</span>(now)   <span class="comment"># 1721430000.0</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 格式化时间</span></span><br><span class="line"><span class="built_in">print</span>(time.strftime(<span class="string">&quot;%Y-%m-%d %H:%M:%S&quot;</span>))   <span class="comment"># 2024-07-20 10:30:00</span></span><br><span class="line"><span class="built_in">print</span>(time.strftime(<span class="string">&quot;%Y-%m-%d %H:%M:%S&quot;</span>, time.localtime()))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 解析时间</span></span><br><span class="line">parsed = time.strptime(<span class="string">&quot;2024-07-20&quot;</span>, <span class="string">&quot;%Y-%m-%d&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(parsed)   <span class="comment"># time.struct_time(tm_year=2024, ...)</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 睡眠</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;开始&quot;</span>)</span><br><span class="line">time.sleep(<span class="number">1</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;1秒后&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 性能计时</span></span><br><span class="line">start = time.perf_counter()</span><br><span class="line"><span class="comment"># do_something()</span></span><br><span class="line">elapsed = time.perf_counter() - start</span><br></pre></td></tr></table></figure><h3 id="8-2-datetime-模块（最常用）"><a href="#8-2-datetime-模块（最常用）" class="headerlink" title="8.2 datetime 模块（最常用）"></a>8.2 <code>datetime</code> 模块（最常用）</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><span class="line"><span class="keyword">from</span> datetime <span class="keyword">import</span> datetime, date, time, timedelta, timezone</span><br><span class="line"><span class="keyword">from</span> zoneinfo <span class="keyword">import</span> ZoneInfo   <span class="comment"># 3.9+</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 当前时间</span></span><br><span class="line">now = datetime.now()</span><br><span class="line">utc_now = datetime.now(timezone.utc)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建特定时间</span></span><br><span class="line">dt = datetime(<span class="number">2024</span>, <span class="number">7</span>, <span class="number">20</span>, <span class="number">10</span>, <span class="number">30</span>, <span class="number">0</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 格式化</span></span><br><span class="line"><span class="built_in">print</span>(dt.strftime(<span class="string">&quot;%Y-%m-%d %H:%M:%S&quot;</span>))</span><br><span class="line"><span class="built_in">print</span>(dt.isoformat())   <span class="comment"># 2024-07-20T10:30:00</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 解析</span></span><br><span class="line">parsed = datetime.fromisoformat(<span class="string">&quot;2024-07-20T10:30:00&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 时间运算</span></span><br><span class="line">future = dt + timedelta(days=<span class="number">7</span>, hours=<span class="number">3</span>)</span><br><span class="line">diff = future - dt</span><br><span class="line"><span class="built_in">print</span>(diff.days)   <span class="comment"># 7</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 时区处理（3.9+ 推荐）</span></span><br><span class="line">nyc = ZoneInfo(<span class="string">&quot;America/New_York&quot;</span>)</span><br><span class="line">shanghai = ZoneInfo(<span class="string">&quot;Asia/Shanghai&quot;</span>)</span><br><span class="line"></span><br><span class="line">dt_shanghai = datetime(<span class="number">2024</span>, <span class="number">7</span>, <span class="number">20</span>, <span class="number">10</span>, <span class="number">30</span>, tzinfo=shanghai)</span><br><span class="line">dt_nyc = dt_shanghai.astimezone(nyc)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;上海: <span class="subst">&#123;dt_shanghai&#125;</span>&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;纽约: <span class="subst">&#123;dt_nyc&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 时间戳转换</span></span><br><span class="line">timestamp = dt.timestamp()</span><br><span class="line"><span class="built_in">print</span>(datetime.fromtimestamp(timestamp))</span><br></pre></td></tr></table></figure><p><strong>strftime 格式代码</strong>：</p><table><thead><tr><th>代码</th><th>含义</th><th>示例</th></tr></thead><tbody><tr><td><code>%Y</code></td><td>4 位年份</td><td>2024</td></tr><tr><td><code>%m</code></td><td>2 位月份</td><td>07</td></tr><tr><td><code>%d</code></td><td>2 位日期</td><td>20</td></tr><tr><td><code>%H</code></td><td>24 小时制</td><td>15</td></tr><tr><td><code>%M</code></td><td>分钟</td><td>30</td></tr><tr><td><code>%S</code></td><td>秒</td><td>00</td></tr><tr><td><code>%A</code></td><td>星期全称</td><td>Saturday</td></tr><tr><td><code>%a</code></td><td>星期缩写</td><td>Sat</td></tr></tbody></table><h3 id="8-3-calendar"><a href="#8-3-calendar" class="headerlink" title="8.3 calendar"></a>8.3 <code>calendar</code></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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> calendar</span><br><span class="line"></span><br><span class="line"><span class="comment"># 打印月历</span></span><br><span class="line"><span class="built_in">print</span>(calendar.month(<span class="number">2024</span>, <span class="number">7</span>))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 判断闰年</span></span><br><span class="line"><span class="built_in">print</span>(calendar.isleap(<span class="number">2024</span>))   <span class="comment"># True</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 某月第一天是星期几，该月有多少天</span></span><br><span class="line">first_weekday, days_in_month = calendar.monthrange(<span class="number">2024</span>, <span class="number">7</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;7月有<span class="subst">&#123;days_in_month&#125;</span>天，第一天是星期<span class="subst">&#123;first_weekday&#125;</span>&quot;</span>)</span><br></pre></td></tr></table></figure><hr><h2 id="9-数学与随机"><a href="#9-数学与随机" class="headerlink" title="9. 数学与随机"></a>9. 数学与随机</h2><h3 id="9-1-math-与-cmath"><a href="#9-1-math-与-cmath" class="headerlink" title="9.1 math 与 cmath"></a>9.1 <code>math</code> 与 <code>cmath</code></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><span class="line"><span class="keyword">import</span> math</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(math.sqrt(<span class="number">16</span>))        <span class="comment"># 4.0</span></span><br><span class="line"><span class="built_in">print</span>(math.<span class="built_in">pow</span>(<span class="number">2</span>, <span class="number">10</span>))      <span class="comment"># 1024.0</span></span><br><span class="line"><span class="built_in">print</span>(math.log(<span class="number">100</span>, <span class="number">10</span>))    <span class="comment"># 2.0</span></span><br><span class="line"><span class="built_in">print</span>(math.log2(<span class="number">1024</span>))      <span class="comment"># 10.0</span></span><br><span class="line"><span class="built_in">print</span>(math.exp(<span class="number">1</span>))          <span class="comment"># 2.718...</span></span><br><span class="line"><span class="built_in">print</span>(math.factorial(<span class="number">5</span>))    <span class="comment"># 120</span></span><br><span class="line"><span class="built_in">print</span>(math.gcd(<span class="number">48</span>, <span class="number">18</span>))     <span class="comment"># 6</span></span><br><span class="line"><span class="built_in">print</span>(math.pi, math.e)      <span class="comment"># 3.14159... 2.71828...</span></span><br><span class="line"><span class="built_in">print</span>(math.floor(<span class="number">3.7</span>))      <span class="comment"># 3</span></span><br><span class="line"><span class="built_in">print</span>(math.ceil(<span class="number">3.2</span>))       <span class="comment"># 4</span></span><br><span class="line"><span class="built_in">print</span>(math.trunc(<span class="number">3.9</span>))      <span class="comment"># 3</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 三角函数</span></span><br><span class="line"><span class="built_in">print</span>(math.sin(math.pi / <span class="number">2</span>))   <span class="comment"># 1.0</span></span><br><span class="line"><span class="built_in">print</span>(math.degrees(math.pi))   <span class="comment"># 180.0</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 双曲函数</span></span><br><span class="line"><span class="built_in">print</span>(math.sinh(<span class="number">1</span>))   <span class="comment"># 1.1752...</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 特殊函数</span></span><br><span class="line"><span class="built_in">print</span>(math.erf(<span class="number">1</span>))    <span class="comment"># 误差函数</span></span><br><span class="line"><span class="built_in">print</span>(math.gamma(<span class="number">5</span>))  <span class="comment"># 伽马函数</span></span><br></pre></td></tr></table></figure><h3 id="9-2-random-与-secrets"><a href="#9-2-random-与-secrets" class="headerlink" title="9.2 random 与 secrets"></a>9.2 <code>random</code> 与 <code>secrets</code></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><span class="line"><span class="keyword">import</span> random</span><br><span class="line"><span class="keyword">import</span> secrets</span><br><span class="line"></span><br><span class="line"><span class="comment"># random：伪随机（可预测，不用于安全场景）</span></span><br><span class="line"><span class="built_in">print</span>(random.random())           <span class="comment"># [0, 1) 浮点数</span></span><br><span class="line"><span class="built_in">print</span>(random.randint(<span class="number">1</span>, <span class="number">100</span>))    <span class="comment"># [1, 100] 整数</span></span><br><span class="line"><span class="built_in">print</span>(random.choice([<span class="string">&quot;A&quot;</span>, <span class="string">&quot;B&quot;</span>, <span class="string">&quot;C&quot;</span>]))</span><br><span class="line"><span class="built_in">print</span>(random.choices([<span class="string">&quot;A&quot;</span>, <span class="string">&quot;B&quot;</span>, <span class="string">&quot;C&quot;</span>], k=<span class="number">10</span>, weights=[<span class="number">5</span>, <span class="number">3</span>, <span class="number">2</span>]))</span><br><span class="line"><span class="built_in">print</span>(random.shuffle([<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>]))    <span class="comment"># 原地打乱</span></span><br><span class="line"><span class="built_in">print</span>(random.sample([<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>], <span class="number">3</span>))  <span class="comment"># 不重复抽样</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># secrets：密码学安全的随机（用于 token、密码等）</span></span><br><span class="line">token = secrets.token_hex(<span class="number">16</span>)     <span class="comment"># 32 字符随机十六进制</span></span><br><span class="line">url_safe = secrets.token_urlsafe(<span class="number">16</span>)   <span class="comment"># URL 安全的随机字符串</span></span><br><span class="line">secure_choice = secrets.choice([<span class="string">&quot;A&quot;</span>, <span class="string">&quot;B&quot;</span>, <span class="string">&quot;C&quot;</span>])</span><br></pre></td></tr></table></figure><p><strong>random vs secrets 对比</strong>：</p><table><thead><tr><th>特性</th><th>random</th><th>secrets</th></tr></thead><tbody><tr><td>随机性</td><td>伪随机（可复现）</td><td>密码学安全（不可预测）</td></tr><tr><td>用途</td><td>游戏、模拟、测试</td><td>Token、密码、验证码</td></tr><tr><td>可重复性</td><td>可通过 seed 复现</td><td>不可复现</td></tr></tbody></table><h3 id="9-3-statistics-与-decimal"><a href="#9-3-statistics-与-decimal" class="headerlink" title="9.3 statistics 与 decimal"></a>9.3 <code>statistics</code> 与 <code>decimal</code></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><span class="line"><span class="keyword">import</span> statistics</span><br><span class="line"><span class="keyword">from</span> decimal <span class="keyword">import</span> Decimal, getcontext</span><br><span class="line"></span><br><span class="line"><span class="comment"># statistics：基础统计</span></span><br><span class="line">data = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>, <span class="number">5</span>, <span class="number">5</span>]</span><br><span class="line"><span class="built_in">print</span>(statistics.mean(data))      <span class="comment"># 平均数</span></span><br><span class="line"><span class="built_in">print</span>(statistics.median(data))    <span class="comment"># 中位数</span></span><br><span class="line"><span class="built_in">print</span>(statistics.mode(data))      <span class="comment"># 众数</span></span><br><span class="line"><span class="built_in">print</span>(statistics.stdev(data))     <span class="comment"># 标准差</span></span><br><span class="line"><span class="built_in">print</span>(statistics.quantiles(data)) <span class="comment"># 分位数</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># decimal：精确小数（金融计算）</span></span><br><span class="line">getcontext().prec = <span class="number">6</span>   <span class="comment"># 设置全局精度</span></span><br><span class="line">a = Decimal(<span class="string">&quot;0.1&quot;</span>)</span><br><span class="line">b = Decimal(<span class="string">&quot;0.2&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(a + b)   <span class="comment"># 0.3（精确！）</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 不要用 float 构造 Decimal</span></span><br><span class="line"><span class="built_in">print</span>(Decimal(<span class="number">0.1</span>))       <span class="comment"># 不精确</span></span><br><span class="line"><span class="built_in">print</span>(Decimal(<span class="string">&quot;0.1&quot;</span>))     <span class="comment"># 精确</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 高精度计算</span></span><br><span class="line">getcontext().prec = <span class="number">20</span></span><br><span class="line">result = Decimal(<span class="string">&quot;1&quot;</span>) / Decimal(<span class="string">&quot;7&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(result)   <span class="comment"># 0.14285714285714285714</span></span><br></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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 浮点精度陷阱</span></span><br><span class="line"><span class="built_in">print</span>(<span class="number">0.1</span> + <span class="number">0.2</span>)   <span class="comment"># 0.30000000000000004（不精确！）</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用 decimal 解决</span></span><br><span class="line"><span class="keyword">from</span> decimal <span class="keyword">import</span> Decimal</span><br><span class="line"><span class="built_in">print</span>(Decimal(<span class="string">&quot;0.1&quot;</span>) + Decimal(<span class="string">&quot;0.2&quot;</span>))   <span class="comment"># 0.3（精确！）</span></span><br></pre></td></tr></table></figure><hr><h2 id="10-并发与并行"><a href="#10-并发与并行" class="headerlink" title="10. 并发与并行"></a>10. 并发与并行</h2><h3 id="10-1-threading-与-GIL"><a href="#10-1-threading-与-GIL" class="headerlink" title="10.1 threading 与 GIL"></a>10.1 <code>threading</code> 与 GIL</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><span class="line"><span class="keyword">import</span> threading</span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">worker</span>(<span class="params">name, delay</span>):</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;线程 <span class="subst">&#123;name&#125;</span> 开始&quot;</span>)</span><br><span class="line">    time.sleep(delay)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;线程 <span class="subst">&#123;name&#125;</span> 结束&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建并启动线程</span></span><br><span class="line">threads = []</span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">3</span>):</span><br><span class="line">    t = threading.Thread(target=worker, args=(<span class="string">f&quot;T<span class="subst">&#123;i&#125;</span>&quot;</span>, <span class="number">1</span>))</span><br><span class="line">    t.start()</span><br><span class="line">    threads.append(t)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 等待所有线程完成</span></span><br><span class="line"><span class="keyword">for</span> t <span class="keyword">in</span> threads:</span><br><span class="line">    t.join()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 线程安全：Lock</span></span><br><span class="line"><span class="keyword">from</span> threading <span class="keyword">import</span> Lock</span><br><span class="line">counter = <span class="number">0</span></span><br><span class="line">lock = Lock()</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">increment</span>():</span><br><span class="line">    <span class="keyword">global</span> counter</span><br><span class="line">    <span class="keyword">for</span> _ <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">100000</span>):</span><br><span class="line">        <span class="keyword">with</span> lock:          <span class="comment"># 获取锁</span></span><br><span class="line">            counter += <span class="number">1</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 线程本地存储</span></span><br><span class="line">local = threading.local()</span><br><span class="line">local.user_id = <span class="string">&quot;123&quot;</span>   <span class="comment"># 每个线程独立</span></span><br></pre></td></tr></table></figure><p><strong>GIL（全局解释器锁）</strong>：</p><figure class="highlight plaintext"><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><span class="line">┌──────────────────────────────────────────────┐</span><br><span class="line">│              Python 解释器                   │</span><br><span class="line">├──────────────────────────────────────────────┤</span><br><span class="line">│                                              │</span><br><span class="line">│   ┌──────────────────────────────────────┐   │</span><br><span class="line">│   │           GIL（全局解释器锁）         │   │</span><br><span class="line">│   └───────────────┬──────────────────────┘   │</span><br><span class="line">│                   │                          │</span><br><span class="line">│   ┌───────────────┴───────────────┐          │</span><br><span class="line">│   │                               │          │</span><br><span class="line">│   │  Thread 1   │  Thread 2       │          │</span><br><span class="line">│   │  Python代码 │  Python代码     │          │</span><br><span class="line">│   │             │                 │          │</span><br><span class="line">│   └─────────────┴─────────────────┘          │</span><br><span class="line">│                                              │</span><br><span class="line">│   同一时刻只有一个线程执行 Python 字节码        │</span><br><span class="line">│   I/O 操作时会释放 GIL                        │</span><br><span class="line">└──────────────────────────────────────────────┘</span><br></pre></td></tr></table></figure><h3 id="10-2-concurrent-futures"><a href="#10-2-concurrent-futures" class="headerlink" title="10.2 concurrent.futures"></a>10.2 <code>concurrent.futures</code></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><span class="line"><span class="keyword">from</span> concurrent.futures <span class="keyword">import</span> ThreadPoolExecutor, ProcessPoolExecutor, as_completed</span><br><span class="line"><span class="keyword">import</span> requests</span><br><span class="line"></span><br><span class="line"><span class="comment"># 线程池（I/O 密集型）</span></span><br><span class="line"><span class="keyword">with</span> ThreadPoolExecutor(max_workers=<span class="number">5</span>) <span class="keyword">as</span> executor:</span><br><span class="line">    urls = [<span class="string">&quot;http://example.com&quot;</span>] * <span class="number">10</span></span><br><span class="line">    <span class="comment"># map：按顺序返回结果</span></span><br><span class="line">    results = executor.<span class="built_in">map</span>(fetch_url, urls)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># submit：异步提交，可获取 Future</span></span><br><span class="line">    futures = [executor.submit(fetch_url, url) <span class="keyword">for</span> url <span class="keyword">in</span> urls]</span><br><span class="line">    <span class="keyword">for</span> future <span class="keyword">in</span> as_completed(futures):</span><br><span class="line">        <span class="keyword">try</span>:</span><br><span class="line">            result = future.result()</span><br><span class="line">            <span class="built_in">print</span>(result)</span><br><span class="line">        <span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">f&quot;错误: <span class="subst">&#123;e&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 进程池（CPU 密集型，绕过 GIL）</span></span><br><span class="line"><span class="keyword">with</span> ProcessPoolExecutor(max_workers=<span class="number">4</span>) <span class="keyword">as</span> executor:</span><br><span class="line">    numbers = <span class="built_in">range</span>(<span class="number">100</span>)</span><br><span class="line">    results = <span class="built_in">list</span>(executor.<span class="built_in">map</span>(is_prime, numbers))</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">fetch_url</span>(<span class="params">url</span>):</span><br><span class="line">    <span class="keyword">return</span> requests.get(url, timeout=<span class="number">5</span>).status_code</span><br></pre></td></tr></table></figure><h3 id="10-3-multiprocessing"><a href="#10-3-multiprocessing" class="headerlink" title="10.3 multiprocessing"></a>10.3 <code>multiprocessing</code></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><span class="line"><span class="keyword">from</span> multiprocessing <span class="keyword">import</span> Pool, Queue, Process</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">square</span>(<span class="params">n</span>):</span><br><span class="line">    <span class="keyword">return</span> n * n</span><br><span class="line"></span><br><span class="line"><span class="comment"># 进程池</span></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    <span class="keyword">with</span> Pool(<span class="number">4</span>) <span class="keyword">as</span> p:</span><br><span class="line">        results = p.<span class="built_in">map</span>(square, <span class="built_in">range</span>(<span class="number">10</span>))</span><br><span class="line">        <span class="built_in">print</span>(results)   <span class="comment"># [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 进程间通信</span></span><br><span class="line"><span class="keyword">from</span> multiprocessing <span class="keyword">import</span> Manager</span><br><span class="line"></span><br><span class="line"><span class="keyword">with</span> Manager() <span class="keyword">as</span> manager:</span><br><span class="line">    shared_dict = manager.<span class="built_in">dict</span>()</span><br><span class="line">    shared_list = manager.<span class="built_in">list</span>()</span><br></pre></td></tr></table></figure><h3 id="10-4-asyncio"><a href="#10-4-asyncio" class="headerlink" title="10.4 asyncio"></a>10.4 <code>asyncio</code></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><span class="line"><span class="keyword">import</span> asyncio</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">fetch</span>(<span class="params">url</span>):</span><br><span class="line">    <span class="keyword">await</span> asyncio.sleep(<span class="number">0.1</span>)   <span class="comment"># 模拟异步 I/O</span></span><br><span class="line">    <span class="keyword">return</span> <span class="string">f&quot;Data from <span class="subst">&#123;url&#125;</span>&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">main</span>():</span><br><span class="line">    urls = [<span class="string">&quot;url1&quot;</span>, <span class="string">&quot;url2&quot;</span>, <span class="string">&quot;url3&quot;</span>]</span><br><span class="line"></span><br><span class="line">    <span class="comment"># gather：并发执行</span></span><br><span class="line">    results = <span class="keyword">await</span> asyncio.gather(</span><br><span class="line">        *(fetch(url) <span class="keyword">for</span> url <span class="keyword">in</span> urls)</span><br><span class="line">    )</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 创建任务</span></span><br><span class="line">    tasks = [asyncio.create_task(fetch(url)) <span class="keyword">for</span> url <span class="keyword">in</span> urls]</span><br><span class="line">    <span class="keyword">for</span> task <span class="keyword">in</span> asyncio.as_completed(tasks):</span><br><span class="line">        result = <span class="keyword">await</span> task</span><br><span class="line">        <span class="built_in">print</span>(result)</span><br><span class="line"></span><br><span class="line"><span class="comment"># asyncio.run(main())</span></span><br></pre></td></tr></table></figure><p><strong>并发模型选择</strong>：</p><table><thead><tr><th>场景</th><th>推荐方案</th><th>原因</th></tr></thead><tbody><tr><td>I&#x2F;O 密集型（网络请求）</td><td><code>asyncio</code></td><td>单线程高并发，内存开销低</td></tr><tr><td>I&#x2F;O 密集型（简单）</td><td><code>ThreadPoolExecutor</code></td><td>代码简单，可调用同步库</td></tr><tr><td>CPU 密集型</td><td><code>ProcessPoolExecutor</code></td><td>绕过 GIL，利用多核</td></tr><tr><td>大量 CPU 计算</td><td><code>multiprocessing</code></td><td>更灵活的进程控制</td></tr></tbody></table><hr><h2 id="11-网络与通信"><a href="#11-网络与通信" class="headerlink" title="11. 网络与通信"></a>11. 网络与通信</h2><h3 id="11-1-socket-基础"><a href="#11-1-socket-基础" class="headerlink" title="11.1 socket 基础"></a>11.1 <code>socket</code> 基础</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><span class="line"><span class="keyword">import</span> socket</span><br><span class="line"></span><br><span class="line"><span class="comment"># TCP 客户端</span></span><br><span class="line"><span class="keyword">with</span> socket.socket(socket.AF_INET, socket.SOCK_STREAM) <span class="keyword">as</span> s:</span><br><span class="line">    s.connect((<span class="string">&quot;example.com&quot;</span>, <span class="number">80</span>))</span><br><span class="line">    s.sendall(<span class="string">b&quot;GET / HTTP/1.1\r\nHost: example.com\r\n\r\n&quot;</span>)</span><br><span class="line">    data = s.recv(<span class="number">4096</span>)</span><br><span class="line">    <span class="built_in">print</span>(data.decode())</span><br><span class="line"></span><br><span class="line"><span class="comment"># UDP</span></span><br><span class="line"><span class="keyword">with</span> socket.socket(socket.AF_INET, socket.SOCK_DGRAM) <span class="keyword">as</span> s:</span><br><span class="line">    s.sendto(<span class="string">b&quot;Hello&quot;</span>, (<span class="string">&quot;localhost&quot;</span>, <span class="number">9999</span>))</span><br><span class="line">    data, addr = s.recvfrom(<span class="number">1024</span>)</span><br></pre></td></tr></table></figure><h3 id="11-2-http-client-与-urllib"><a href="#11-2-http-client-与-urllib" class="headerlink" title="11.2 http.client 与 urllib"></a>11.2 <code>http.client</code> 与 <code>urllib</code></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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 标准库的 HTTP 请求（生产环境推荐使用 requests 第三方库）</span></span><br><span class="line"><span class="keyword">import</span> urllib.request</span><br><span class="line"><span class="keyword">import</span> json</span><br><span class="line"></span><br><span class="line">req = urllib.request.Request(</span><br><span class="line">    <span class="string">&quot;https://api.example.com/data&quot;</span>,</span><br><span class="line">    headers=&#123;<span class="string">&quot;Accept&quot;</span>: <span class="string">&quot;application/json&quot;</span>&#125;</span><br><span class="line">)</span><br><span class="line"><span class="keyword">with</span> urllib.request.urlopen(req, timeout=<span class="number">10</span>) <span class="keyword">as</span> response:</span><br><span class="line">    data = json.loads(response.read())</span><br><span class="line">    <span class="built_in">print</span>(data)</span><br></pre></td></tr></table></figure><h3 id="11-3-email-模块"><a href="#11-3-email-模块" class="headerlink" title="11.3 email 模块"></a>11.3 <code>email</code> 模块</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><span class="line"><span class="keyword">from</span> email.mime.text <span class="keyword">import</span> MIMEText</span><br><span class="line"><span class="keyword">from</span> email.mime.multipart <span class="keyword">import</span> MIMEMultipart</span><br><span class="line"></span><br><span class="line">msg = MIMEMultipart()</span><br><span class="line">msg[<span class="string">&quot;From&quot;</span>] = <span class="string">&quot;sender@example.com&quot;</span></span><br><span class="line">msg[<span class="string">&quot;To&quot;</span>] = <span class="string">&quot;recipient@example.com&quot;</span></span><br><span class="line">msg[<span class="string">&quot;Subject&quot;</span>] = <span class="string">&quot;测试邮件&quot;</span></span><br><span class="line"></span><br><span class="line">body = MIMEText(<span class="string">&quot;这是邮件正文&quot;</span>, <span class="string">&quot;plain&quot;</span>, <span class="string">&quot;utf-8&quot;</span>)</span><br><span class="line">msg.attach(body)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 发送（需要 SMTP 服务器）</span></span><br><span class="line"><span class="comment"># import smtplib</span></span><br><span class="line"><span class="comment"># with smtplib.SMTP(&quot;smtp.example.com&quot;) as server:</span></span><br><span class="line"><span class="comment">#     server.send_message(msg)</span></span><br></pre></td></tr></table></figure><hr><h2 id="12-开发支持"><a href="#12-开发支持" class="headerlink" title="12. 开发支持"></a>12. 开发支持</h2><h3 id="12-1-unittest-测试框架"><a href="#12-1-unittest-测试框架" class="headerlink" title="12.1 unittest 测试框架"></a>12.1 <code>unittest</code> 测试框架</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><span class="line"><span class="keyword">import</span> unittest</span><br><span class="line"><span class="keyword">from</span> unittest.mock <span class="keyword">import</span> Mock, patch, MagicMock</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">TestStringMethods</span>(unittest.TestCase):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">setUp</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;每个测试方法前执行。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="variable language_">self</span>.sample = <span class="string">&quot;hello world&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">tearDown</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;每个测试方法后执行。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">test_upper</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="variable language_">self</span>.assertEqual(<span class="string">&quot;foo&quot;</span>.upper(), <span class="string">&quot;FOO&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">test_isupper</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="variable language_">self</span>.assertTrue(<span class="string">&quot;FOO&quot;</span>.isupper())</span><br><span class="line">        <span class="variable language_">self</span>.assertFalse(<span class="string">&quot;Foo&quot;</span>.isupper())</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">test_split</span>(<span class="params">self</span>):</span><br><span class="line">        s = <span class="string">&quot;hello world&quot;</span></span><br><span class="line">        <span class="variable language_">self</span>.assertEqual(s.split(), [<span class="string">&quot;hello&quot;</span>, <span class="string">&quot;world&quot;</span>])</span><br><span class="line">        <span class="keyword">with</span> <span class="variable language_">self</span>.assertRaises(TypeError):</span><br><span class="line">            s.split(<span class="number">2</span>)</span><br><span class="line"></span><br><span class="line"><span class="meta">    @patch(<span class="params"><span class="string">&quot;module.ClassName.method&quot;</span></span>)</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">test_with_mock</span>(<span class="params">self, mock_method</span>):</span><br><span class="line">        mock_method.return_value = <span class="string">&quot;mocked&quot;</span></span><br><span class="line">        result = module.ClassName().method()</span><br><span class="line">        <span class="variable language_">self</span>.assertEqual(result, <span class="string">&quot;mocked&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 运行测试</span></span><br><span class="line"><span class="comment"># python -m unittest test_module</span></span><br></pre></td></tr></table></figure><h3 id="12-2-doctest"><a href="#12-2-doctest" class="headerlink" title="12.2 doctest"></a>12.2 <code>doctest</code></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><span class="line"><span class="keyword">def</span> <span class="title function_">factorial</span>(<span class="params">n</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;计算阶乘。</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">    &gt;&gt;&gt; factorial(5)</span></span><br><span class="line"><span class="string">    120</span></span><br><span class="line"><span class="string">    &gt;&gt;&gt; factorial(0)</span></span><br><span class="line"><span class="string">    1</span></span><br><span class="line"><span class="string">    &gt;&gt;&gt; factorial(-1)</span></span><br><span class="line"><span class="string">    Traceback (most recent call last):</span></span><br><span class="line"><span class="string">        ...</span></span><br><span class="line"><span class="string">    ValueError: n must be &gt;= 0</span></span><br><span class="line"><span class="string">    &quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">if</span> n &lt; <span class="number">0</span>:</span><br><span class="line">        <span class="keyword">raise</span> ValueError(<span class="string">&quot;n must be &gt;= 0&quot;</span>)</span><br><span class="line">    <span class="keyword">if</span> n &lt;= <span class="number">1</span>:</span><br><span class="line">        <span class="keyword">return</span> <span class="number">1</span></span><br><span class="line">    <span class="keyword">return</span> n * factorial(n - <span class="number">1</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 运行 doctest</span></span><br><span class="line"><span class="comment"># python -m doctest your_module.py -v</span></span><br></pre></td></tr></table></figure><h3 id="12-3-timeit-与性能分析"><a href="#12-3-timeit-与性能分析" class="headerlink" title="12.3 timeit 与性能分析"></a>12.3 <code>timeit</code> 与性能分析</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><span class="line"><span class="keyword">import</span> timeit</span><br><span class="line"></span><br><span class="line"><span class="comment"># 简单计时</span></span><br><span class="line">elapsed = timeit.timeit(<span class="string">&quot;sum(range(1000))&quot;</span>, number=<span class="number">10000</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;耗时: <span class="subst">&#123;elapsed:<span class="number">.4</span>f&#125;</span>s&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 命令行使用</span></span><br><span class="line"><span class="comment"># python -m timeit &quot;sum(range(1000))&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># cProfile：函数级性能分析</span></span><br><span class="line"><span class="keyword">import</span> cProfile</span><br><span class="line"><span class="keyword">import</span> pstats</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">slow_function</span>():</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">sum</span>(i ** <span class="number">2</span> <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">100000</span>))</span><br><span class="line"></span><br><span class="line">profiler = cProfile.Profile()</span><br><span class="line">profiler.enable()</span><br><span class="line">slow_function()</span><br><span class="line">profiler.disable()</span><br><span class="line"></span><br><span class="line">stats = pstats.Stats(profiler)</span><br><span class="line">stats.sort_stats(<span class="string">&quot;cumulative&quot;</span>)</span><br><span class="line">stats.print_stats(<span class="number">10</span>)   <span class="comment"># 打印前 10 个耗时函数</span></span><br></pre></td></tr></table></figure><h3 id="12-4-enum-枚举"><a href="#12-4-enum-枚举" class="headerlink" title="12.4 enum 枚举"></a>12.4 <code>enum</code> 枚举</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><span class="line"><span class="keyword">from</span> enum <span class="keyword">import</span> Enum, auto, IntEnum</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Color</span>(<span class="title class_ inherited__">Enum</span>):</span><br><span class="line">    RED = <span class="number">1</span></span><br><span class="line">    GREEN = <span class="number">2</span></span><br><span class="line">    BLUE = <span class="number">3</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(Color.RED)           <span class="comment"># Color.RED</span></span><br><span class="line"><span class="built_in">print</span>(Color.RED.name)      <span class="comment"># RED</span></span><br><span class="line"><span class="built_in">print</span>(Color.RED.value)     <span class="comment"># 1</span></span><br><span class="line"><span class="built_in">print</span>(Color(<span class="number">1</span>))            <span class="comment"># Color.RED</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 自动赋值</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Status</span>(<span class="title class_ inherited__">Enum</span>):</span><br><span class="line">    PENDING = auto()</span><br><span class="line">    RUNNING = auto()</span><br><span class="line">    COMPLETED = auto()</span><br><span class="line"></span><br><span class="line"><span class="comment"># IntEnum：可作为整数使用</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Priority</span>(<span class="title class_ inherited__">IntEnum</span>):</span><br><span class="line">    LOW = <span class="number">1</span></span><br><span class="line">    MEDIUM = <span class="number">2</span></span><br><span class="line">    HIGH = <span class="number">3</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(Priority.HIGH &gt; Priority.LOW)   <span class="comment"># True</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 枚举比较</span></span><br><span class="line"><span class="built_in">print</span>(Color.RED == Color.GREEN)   <span class="comment"># False</span></span><br><span class="line"><span class="built_in">print</span>(Color.RED != Color.GREEN)   <span class="comment"># True</span></span><br></pre></td></tr></table></figure><hr><h2 id="13-其他实用模块"><a href="#13-其他实用模块" class="headerlink" title="13. 其他实用模块"></a>13. 其他实用模块</h2><h3 id="13-1-base64-编码"><a href="#13-1-base64-编码" class="headerlink" title="13.1 base64 编码"></a>13.1 <code>base64</code> 编码</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> base64</span><br><span class="line"></span><br><span class="line">encoded = base64.b64encode(<span class="string">b&quot;hello&quot;</span>).decode()   <span class="comment"># aGVsbG8=</span></span><br><span class="line">decoded = base64.b64decode(encoded)              <span class="comment"># b&#x27;hello&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># URL 安全编码</span></span><br><span class="line">url_safe = base64.urlsafe_b64encode(<span class="string">b&quot;hello?world&quot;</span>).decode()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 文件编码（处理二进制数据）</span></span><br><span class="line"><span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">&quot;image.png&quot;</span>, <span class="string">&quot;rb&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">    encoded_img = base64.b64encode(f.read()).decode()</span><br></pre></td></tr></table></figure><h3 id="13-2-inspect-内省"><a href="#13-2-inspect-内省" class="headerlink" title="13.2 inspect 内省"></a>13.2 <code>inspect</code> 内省</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><span class="line"><span class="keyword">import</span> inspect</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">example</span>(<span class="params">a: <span class="built_in">int</span>, b: <span class="built_in">str</span> = <span class="string">&quot;default&quot;</span></span>) -&gt; <span class="built_in">bool</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;示例函数。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">return</span> <span class="literal">True</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 获取函数签名</span></span><br><span class="line">sig = inspect.signature(example)</span><br><span class="line"><span class="keyword">for</span> name, param <span class="keyword">in</span> sig.parameters.items():</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;<span class="subst">&#123;name&#125;</span>: <span class="subst">&#123;param.annotation&#125;</span>, default=<span class="subst">&#123;param.default&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 获取源码</span></span><br><span class="line"><span class="built_in">print</span>(inspect.getsource(example))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 获取调用栈</span></span><br><span class="line"><span class="built_in">print</span>(inspect.stack())</span><br><span class="line"></span><br><span class="line"><span class="comment"># 判断对象类型</span></span><br><span class="line"><span class="built_in">print</span>(inspect.isfunction(example))    <span class="comment"># True</span></span><br><span class="line"><span class="built_in">print</span>(inspect.ismethod(example))      <span class="comment"># False</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 获取类的所有方法</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">MyClass</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">method1</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">pass</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">method2</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line">methods = inspect.getmembers(MyClass, predicate=inspect.isfunction)</span><br><span class="line"><span class="built_in">print</span>([name <span class="keyword">for</span> name, _ <span class="keyword">in</span> methods])   <span class="comment"># [&#x27;method1&#x27;, &#x27;method2&#x27;]</span></span><br></pre></td></tr></table></figure><h3 id="13-3-weakref：弱引用"><a href="#13-3-weakref：弱引用" class="headerlink" title="13.3 weakref：弱引用"></a>13.3 <code>weakref</code>：弱引用</h3><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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> weakref</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Node</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, value</span>):</span><br><span class="line">        <span class="variable language_">self</span>.value = value</span><br><span class="line">        <span class="variable language_">self</span>._parent = <span class="literal">None</span>        <span class="comment"># 强引用</span></span><br><span class="line">        <span class="variable language_">self</span>._children = []        <span class="comment"># 强引用</span></span><br><span class="line"></span><br><span class="line"><span class="meta">    @property</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">parent</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;使用弱引用避免循环引用。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>._parent() <span class="keyword">if</span> <span class="variable language_">self</span>._parent <span class="keyword">else</span> <span class="literal">None</span></span><br><span class="line"></span><br><span class="line"><span class="meta">    @parent.setter</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">parent</span>(<span class="params">self, node</span>):</span><br><span class="line">        <span class="variable language_">self</span>._parent = weakref.ref(node) <span class="keyword">if</span> node <span class="keyword">else</span> <span class="literal">None</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">add_child</span>(<span class="params">self, child</span>):</span><br><span class="line">        <span class="variable language_">self</span>._children.append(child)</span><br><span class="line">        child.parent = <span class="variable language_">self</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 弱引用被回收时的回调</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">on_delete</span>(<span class="params">obj</span>):</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;对象被回收: <span class="subst">&#123;obj&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line">data = &#123;<span class="string">&quot;key&quot;</span>: <span class="string">&quot;value&quot;</span>&#125;</span><br><span class="line">ref = weakref.ref(data, on_delete)   <span class="comment"># 注册回调</span></span><br><span class="line"><span class="built_in">print</span>(ref())           <span class="comment"># &#123;&#x27;key&#x27;: &#x27;value&#x27;&#125; —— 获取原对象</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">del</span> data               <span class="comment"># 删除原对象</span></span><br><span class="line"><span class="built_in">print</span>(ref())           <span class="comment"># None —— 原对象已被回收</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># WeakValueDictionary：值被回收时自动删除条目</span></span><br><span class="line">cache = weakref.WeakValueDictionary()</span><br><span class="line">obj = Node(<span class="string">&quot;temp&quot;</span>)</span><br><span class="line">cache[<span class="string">&quot;temp&quot;</span>] = obj</span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">len</span>(cache))      <span class="comment"># 1</span></span><br><span class="line"><span class="keyword">del</span> obj</span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">len</span>(cache))      <span class="comment"># 0（自动移除）</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 工程场景：缓存系统、观察者模式（避免阻止观察者回收）、对象池</span></span><br></pre></td></tr></table></figure><h3 id="13-4-copy-模块深度解析"><a href="#13-4-copy-模块深度解析" class="headerlink" title="13.4 copy 模块深度解析"></a>13.4 <code>copy</code> 模块深度解析</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> copy</span><br><span class="line"></span><br><span class="line"><span class="comment"># 浅拷贝 vs 深拷贝的底层原理</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Container</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, items</span>):</span><br><span class="line">        <span class="variable language_">self</span>.items = items</span><br><span class="line"></span><br><span class="line">original = Container([[<span class="number">1</span>, <span class="number">2</span>], [<span class="number">3</span>, <span class="number">4</span>]])</span><br><span class="line"></span><br><span class="line"><span class="comment"># 浅拷贝：只复制最外层，内层对象共享引用</span></span><br><span class="line">shallow = copy.copy(original)</span><br><span class="line"><span class="built_in">print</span>(shallow.items <span class="keyword">is</span> original.items)   <span class="comment"># True（内层同一对象）</span></span><br><span class="line">shallow.items[<span class="number">0</span>].append(<span class="number">99</span>)</span><br><span class="line"><span class="built_in">print</span>(original.items)                    <span class="comment"># [[1, 2, 99], [3, 4]] —— 被影响了！</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 深拷贝：递归复制所有嵌套对象</span></span><br><span class="line">deep = copy.deepcopy(original)</span><br><span class="line"><span class="built_in">print</span>(deep.items <span class="keyword">is</span> original.items)      <span class="comment"># False（内层独立对象）</span></span><br><span class="line">deep.items[<span class="number">0</span>].append(<span class="number">88</span>)</span><br><span class="line"><span class="built_in">print</span>(original.items)                    <span class="comment"># [[1, 2, 99], [3, 4]] —— 不受影响</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 自定义拷贝行为</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Custom</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, value</span>):</span><br><span class="line">        <span class="variable language_">self</span>.value = value</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__copy__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;浅拷贝自定义。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">return</span> Custom(<span class="variable language_">self</span>.value)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__deepcopy__</span>(<span class="params">self, memo</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;深拷贝自定义。注意 memo 参数用于处理循环引用。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">return</span> Custom(copy.deepcopy(<span class="variable language_">self</span>.value, memo))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 拷贝策略选择</span></span><br><span class="line"><span class="comment"># | 场景 | 选择 | 原因 |</span></span><br><span class="line"><span class="comment"># |------|------|------|</span></span><br><span class="line"><span class="comment"># | 不可变对象（int、str、tuple）| 无需拷贝 | 赋值即引用 |</span></span><br><span class="line"><span class="comment"># | 简单容器（列表/字典值类型）| 浅拷贝 | 性能好，够用 |</span></span><br><span class="line"><span class="comment"># | 嵌套容器（列表嵌套列表）| 深拷贝 | 需要完全独立副本 |</span></span><br><span class="line"><span class="comment"># | 大对象 | 自定义 __copy__ | 只拷贝需要的部分 |</span></span><br></pre></td></tr></table></figure><hr><h2 id="14-工作实战场景"><a href="#14-工作实战场景" class="headerlink" title="14. 工作实战场景"></a>14. 工作实战场景</h2><h3 id="14-1-数据处理管道"><a href="#14-1-数据处理管道" class="headerlink" title="14.1 数据处理管道"></a>14.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><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><span class="line"><span class="keyword">import</span> itertools</span><br><span class="line"><span class="keyword">from</span> collections <span class="keyword">import</span> Counter</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">DataPipeline</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;数据处理管道：流式处理大数据。&quot;&quot;&quot;</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, data_iter</span>):</span><br><span class="line">        <span class="variable language_">self</span>.data_iter = data_iter</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">filter</span>(<span class="params">self, predicate</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;过滤数据。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">return</span> DataPipeline(<span class="built_in">filter</span>(predicate, <span class="variable language_">self</span>.data_iter))</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">map</span>(<span class="params">self, func</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;转换数据。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">return</span> DataPipeline(<span class="built_in">map</span>(func, <span class="variable language_">self</span>.data_iter))</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">group_by</span>(<span class="params">self, key_func</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;按键分组。&quot;&quot;&quot;</span></span><br><span class="line">        sorted_data = <span class="built_in">sorted</span>(<span class="variable language_">self</span>.data_iter, key=key_func)</span><br><span class="line">        groups = []</span><br><span class="line">        <span class="keyword">for</span> key, group <span class="keyword">in</span> itertools.groupby(sorted_data, key=key_func):</span><br><span class="line">            groups.append((key, <span class="built_in">list</span>(group)))</span><br><span class="line">        <span class="keyword">return</span> groups</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">count_by</span>(<span class="params">self, key_func</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;按键计数。&quot;&quot;&quot;</span></span><br><span class="line">        counter = Counter(key_func(item) <span class="keyword">for</span> item <span class="keyword">in</span> <span class="variable language_">self</span>.data_iter)</span><br><span class="line">        <span class="keyword">return</span> counter</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">collect</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;收集结果。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">list</span>(<span class="variable language_">self</span>.data_iter)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用示例</span></span><br><span class="line">data = [</span><br><span class="line">    &#123;<span class="string">&quot;name&quot;</span>: <span class="string">&quot;Alice&quot;</span>, <span class="string">&quot;category&quot;</span>: <span class="string">&quot;A&quot;</span>, <span class="string">&quot;value&quot;</span>: <span class="number">10</span>&#125;,</span><br><span class="line">    &#123;<span class="string">&quot;name&quot;</span>: <span class="string">&quot;Bob&quot;</span>, <span class="string">&quot;category&quot;</span>: <span class="string">&quot;B&quot;</span>, <span class="string">&quot;value&quot;</span>: <span class="number">20</span>&#125;,</span><br><span class="line">    &#123;<span class="string">&quot;name&quot;</span>: <span class="string">&quot;Charlie&quot;</span>, <span class="string">&quot;category&quot;</span>: <span class="string">&quot;A&quot;</span>, <span class="string">&quot;value&quot;</span>: <span class="number">30</span>&#125;,</span><br><span class="line">]</span><br><span class="line"></span><br><span class="line">pipeline = DataPipeline(data)</span><br><span class="line">result = pipeline \</span><br><span class="line">    .<span class="built_in">filter</span>(<span class="keyword">lambda</span> x: x[<span class="string">&quot;value&quot;</span>] &gt; <span class="number">15</span>) \</span><br><span class="line">    .group_by(<span class="keyword">lambda</span> x: x[<span class="string">&quot;category&quot;</span>])</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(result)</span><br></pre></td></tr></table></figure><h3 id="14-2-高性能缓存系统"><a href="#14-2-高性能缓存系统" class="headerlink" title="14.2 高性能缓存系统"></a>14.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><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><span class="line"><span class="keyword">from</span> functools <span class="keyword">import</span> lru_cache</span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"><span class="keyword">from</span> threading <span class="keyword">import</span> Lock</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">CacheManager</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;缓存管理器：支持多种缓存策略。&quot;&quot;&quot;</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="variable language_">self</span>._caches = &#123;&#125;</span><br><span class="line">        <span class="variable language_">self</span>._lock = Lock()</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_cache</span>(<span class="params">self, name, maxsize=<span class="number">128</span></span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;获取或创建缓存。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">with</span> <span class="variable language_">self</span>._lock:</span><br><span class="line">            <span class="keyword">if</span> name <span class="keyword">not</span> <span class="keyword">in</span> <span class="variable language_">self</span>._caches:</span><br><span class="line">                <span class="keyword">def</span> <span class="title function_">decorator</span>(<span class="params">func</span>):</span><br><span class="line"><span class="meta">                    @lru_cache(<span class="params">maxsize=maxsize</span>)</span></span><br><span class="line">                    <span class="keyword">def</span> <span class="title function_">wrapper</span>(<span class="params">*args, **kwargs</span>):</span><br><span class="line">                        <span class="keyword">return</span> func(*args, **kwargs)</span><br><span class="line">                    <span class="keyword">return</span> wrapper</span><br><span class="line">                <span class="variable language_">self</span>._caches[name] = decorator</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>._caches[name]</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">clear_cache</span>(<span class="params">self, name</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;清空指定缓存。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">with</span> <span class="variable language_">self</span>._lock:</span><br><span class="line">            <span class="keyword">if</span> name <span class="keyword">in</span> <span class="variable language_">self</span>._caches:</span><br><span class="line">                <span class="comment"># lru_cache 装饰的函数有 cache_clear 方法</span></span><br><span class="line">                <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用示例</span></span><br><span class="line">cache_manager = CacheManager()</span><br><span class="line"></span><br><span class="line"><span class="meta">@cache_manager.get_cache(<span class="params"><span class="string">&quot;user_cache&quot;</span>, maxsize=<span class="number">100</span></span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">get_user</span>(<span class="params">user_id</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;模拟数据库查询。&quot;&quot;&quot;</span></span><br><span class="line">    time.sleep(<span class="number">0.1</span>)  <span class="comment"># 模拟耗时操作</span></span><br><span class="line">    <span class="keyword">return</span> &#123;<span class="string">&quot;id&quot;</span>: user_id, <span class="string">&quot;name&quot;</span>: <span class="string">f&quot;User <span class="subst">&#123;user_id&#125;</span>&quot;</span>&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 首次调用：慢</span></span><br><span class="line">start = time.time()</span><br><span class="line">user = get_user(<span class="number">1</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;首次调用耗时: <span class="subst">&#123;time.time() - start:<span class="number">.3</span>f&#125;</span>s&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 缓存命中：快</span></span><br><span class="line">start = time.time()</span><br><span class="line">user = get_user(<span class="number">1</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;缓存命中耗时: <span class="subst">&#123;time.time() - start:<span class="number">.3</span>f&#125;</span>s&quot;</span>)</span><br></pre></td></tr></table></figure><h3 id="14-3-日志分析工具"><a href="#14-3-日志分析工具" class="headerlink" title="14.3 日志分析工具"></a>14.3 日志分析工具</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><span class="line"><span class="keyword">import</span> re</span><br><span class="line"><span class="keyword">from</span> collections <span class="keyword">import</span> Counter</span><br><span class="line"><span class="keyword">from</span> pathlib <span class="keyword">import</span> Path</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">LogAnalyzer</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;日志分析工具：统计访问频率、错误类型等。&quot;&quot;&quot;</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, log_path</span>):</span><br><span class="line">        <span class="variable language_">self</span>.log_path = Path(log_path)</span><br><span class="line">        <span class="variable language_">self</span>._pattern = re.<span class="built_in">compile</span>(</span><br><span class="line">            <span class="string">r&#x27;(?P&lt;ip&gt;\d+\.\d+\.\d+\.\d+) - - \[(?P&lt;time&gt;.*?)\] &#x27;</span></span><br><span class="line">            <span class="string">r&#x27;&quot;(?P&lt;method&gt;\w+) (?P&lt;path&gt;.*?) HTTP/1\.\d&quot; &#x27;</span></span><br><span class="line">            <span class="string">r&#x27;(?P&lt;status&gt;\d+) (?P&lt;size&gt;\d+)&#x27;</span></span><br><span class="line">        )</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">parse_logs</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;解析日志文件，返回生成器。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">with</span> <span class="built_in">open</span>(<span class="variable language_">self</span>.log_path, <span class="string">&quot;r&quot;</span>, encoding=<span class="string">&quot;utf-8&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">            <span class="keyword">for</span> line <span class="keyword">in</span> f:</span><br><span class="line">                <span class="keyword">match</span> = <span class="variable language_">self</span>._pattern.<span class="keyword">match</span>(line)</span><br><span class="line">                <span class="keyword">if</span> <span class="keyword">match</span>:</span><br><span class="line">                    <span class="keyword">yield</span> <span class="keyword">match</span>.groupdict()</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_ip_stats</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;统计 IP 访问次数。&quot;&quot;&quot;</span></span><br><span class="line">        counter = Counter(entry[<span class="string">&quot;ip&quot;</span>] <span class="keyword">for</span> entry <span class="keyword">in</span> <span class="variable language_">self</span>.parse_logs())</span><br><span class="line">        <span class="keyword">return</span> counter.most_common(<span class="number">10</span>)</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_status_stats</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;统计状态码分布。&quot;&quot;&quot;</span></span><br><span class="line">        counter = Counter(entry[<span class="string">&quot;status&quot;</span>] <span class="keyword">for</span> entry <span class="keyword">in</span> <span class="variable language_">self</span>.parse_logs())</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">dict</span>(counter)</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_top_paths</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;统计访问最多的路径。&quot;&quot;&quot;</span></span><br><span class="line">        counter = Counter(entry[<span class="string">&quot;path&quot;</span>] <span class="keyword">for</span> entry <span class="keyword">in</span> <span class="variable language_">self</span>.parse_logs())</span><br><span class="line">        <span class="keyword">return</span> counter.most_common(<span class="number">10</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用示例</span></span><br><span class="line">analyzer = LogAnalyzer(<span class="string">&quot;access.log&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;Top IP:&quot;</span>, analyzer.get_ip_stats())</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;Status分布:&quot;</span>, analyzer.get_status_stats())</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;Top Paths:&quot;</span>, analyzer.get_top_paths())</span><br></pre></td></tr></table></figure><h3 id="14-4-并发任务调度器"><a href="#14-4-并发任务调度器" class="headerlink" title="14.4 并发任务调度器"></a>14.4 并发任务调度器</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> concurrent.futures <span class="keyword">import</span> ThreadPoolExecutor, as_completed</span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">TaskScheduler</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;任务调度器：并发执行任务并收集结果。&quot;&quot;&quot;</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, max_workers=<span class="number">5</span></span>):</span><br><span class="line">        <span class="variable language_">self</span>.executor = ThreadPoolExecutor(max_workers=max_workers)</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">submit</span>(<span class="params">self, task_func, *args, **kwargs</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;提交单个任务。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>.executor.submit(task_func, *args, **kwargs)</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">run_all</span>(<span class="params">self, tasks</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;并行执行所有任务。&quot;&quot;&quot;</span></span><br><span class="line">        futures = [<span class="variable language_">self</span>.executor.submit(task) <span class="keyword">for</span> task <span class="keyword">in</span> tasks]</span><br><span class="line">        </span><br><span class="line">        results = []</span><br><span class="line">        errors = []</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">for</span> future <span class="keyword">in</span> as_completed(futures):</span><br><span class="line">            <span class="keyword">try</span>:</span><br><span class="line">                results.append(future.result())</span><br><span class="line">            <span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">                errors.append(e)</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> results, errors</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">shutdown</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;关闭调度器。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="variable language_">self</span>.executor.shutdown(wait=<span class="literal">True</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用示例</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">fetch_data</span>(<span class="params">url</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;模拟网络请求。&quot;&quot;&quot;</span></span><br><span class="line">    time.sleep(<span class="number">0.5</span>)</span><br><span class="line">    <span class="keyword">return</span> <span class="string">f&quot;Data from <span class="subst">&#123;url&#125;</span>&quot;</span></span><br><span class="line"></span><br><span class="line">scheduler = TaskScheduler(max_workers=<span class="number">3</span>)</span><br><span class="line">tasks = [</span><br><span class="line">    <span class="keyword">lambda</span>: fetch_data(<span class="string">&quot;http://api1.example.com&quot;</span>),</span><br><span class="line">    <span class="keyword">lambda</span>: fetch_data(<span class="string">&quot;http://api2.example.com&quot;</span>),</span><br><span class="line">    <span class="keyword">lambda</span>: fetch_data(<span class="string">&quot;http://api3.example.com&quot;</span>),</span><br><span class="line">]</span><br><span class="line"></span><br><span class="line">results, errors = scheduler.run_all(tasks)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;Results:&quot;</span>, results)</span><br><span class="line">scheduler.shutdown()</span><br></pre></td></tr></table></figure><hr><h2 id="15-常见面试题汇总"><a href="#15-常见面试题汇总" class="headerlink" title="15. 常见面试题汇总"></a>15. 常见面试题汇总</h2><h3 id="15-1-基础概念题"><a href="#15-1-基础概念题" class="headerlink" title="15.1 基础概念题"></a>15.1 基础概念题</h3><p><strong>Q1：<code>collections</code> 模块中 <code>defaultdict</code> 和普通 <code>dict</code> 的区别？</strong></p><p><strong>A</strong>：<code>defaultdict</code> 在访问不存在的键时会自动创建该键，并使用 <code>default_factory</code> 提供默认值。普通 <code>dict</code> 会抛出 <code>KeyError</code>。</p><hr><p><strong>Q2：<code>itertools.chain</code> 和 <code>list.extend</code> 的区别？</strong></p><p><strong>A</strong>：<code>itertools.chain</code> 返回一个迭代器，不立即合并列表，内存效率更高；<code>list.extend</code> 会立即将所有元素添加到列表中，占用更多内存。</p><hr><p><strong>Q3：<code>heapq</code> 实现的是最大堆还是最小堆？如何实现最大堆？</strong></p><p><strong>A</strong>：<code>heapq</code> 实现的是最小堆。要实现最大堆，可以将元素取负值后存储。</p><hr><p><strong>Q4：<code>functools.lru_cache</code> 的工作原理是什么？</strong></p><p><strong>A</strong>：<code>lru_cache</code> 使用最近最少使用算法缓存函数结果。当缓存满时，会淘汰最近最少使用的条目。它通过装饰器实现，自动将函数参数作为键缓存结果。</p><hr><p><strong>Q5：<code>concurrent.futures.ThreadPoolExecutor</code> 和 <code>ProcessPoolExecutor</code> 的区别？</strong></p><p><strong>A</strong>：</p><ul><li><code>ThreadPoolExecutor</code>：线程池，适用于 I&#x2F;O 密集型任务，受 GIL 限制，同一时刻只有一个线程执行 Python 字节码</li><li><code>ProcessPoolExecutor</code>：进程池，适用于 CPU 密集型任务，绕过 GIL，利用多核处理器</li></ul><hr><h3 id="15-2-实战应用题"><a href="#15-2-实战应用题" class="headerlink" title="15.2 实战应用题"></a>15.2 实战应用题</h3><p><strong>Q6：如何选择合适的并发模型？</strong></p><p><strong>A</strong>：</p><ul><li>I&#x2F;O 密集型（网络请求、文件读写）：<code>asyncio</code>（单线程高并发）或 <code>ThreadPoolExecutor</code>（简单）</li><li>CPU 密集型（计算任务）：<code>ProcessPoolExecutor</code>（绕过 GIL）</li><li>需要大量进程控制：<code>multiprocessing</code></li></ul><hr><p><strong>Q7：如何处理浮点数精度问题？</strong></p><p><strong>A</strong>：</p><ol><li>使用 <code>decimal</code> 模块进行精确小数计算</li><li>避免直接比较浮点数，使用容差比较（<code>abs(a - b) &lt; 1e-9</code>）</li><li>对于金融计算，使用 <code>decimal</code> 或整数（以分为单位）</li></ol><hr><p><strong>Q8：如何实现高效的缓存系统？</strong></p><p><strong>A</strong>：</p><ol><li>使用 <code>functools.lru_cache</code> 装饰器（简单场景）</li><li>实现自定义缓存类（复杂场景）</li><li>使用 <code>weakref.WeakValueDictionary</code>（自动回收缓存）</li><li>设置合理的缓存过期策略</li></ol><hr><p><strong>Q9：如何进行性能分析？</strong></p><p><strong>A</strong>：</p><ol><li><code>timeit</code>：简单计时，比较代码片段</li><li><code>cProfile</code>：函数级性能分析，找出耗时函数</li><li><code>line_profiler</code>（第三方）：行级性能分析</li><li><code>memory_profiler</code>（第三方）：内存使用分析</li></ol><hr><p><strong>Q10：<code>re.compile()</code> 的作用是什么？何时使用？</strong></p><p><strong>A</strong>：<code>re.compile()</code> 用于编译正则表达式，生成正则表达式对象。对于频繁使用的正则表达式，编译后复用可以提升性能（避免重复编译）。</p><hr><h3 id="15-3-代码分析题"><a href="#15-3-代码分析题" class="headerlink" title="15.3 代码分析题"></a>15.3 代码分析题</h3><p><strong>Q11：分析以下代码的问题：</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><span class="line"><span class="keyword">import</span> re</span><br><span class="line"></span><br><span class="line">text = <span class="string">&quot;email: test@example.com&quot;</span></span><br><span class="line"><span class="keyword">if</span> re.<span class="keyword">match</span>(<span class="string">r&quot;\w+@\w+\.\w+&quot;</span>, text):</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;匹配成功&quot;</span>)</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;匹配失败&quot;</span>)</span><br></pre></td></tr></table></figure><p><strong>A</strong>：<code>re.match()</code> 只从字符串开头匹配，而 <code>email: test@example.com</code> 开头是 “email:”，所以匹配失败。应使用 <code>re.search()</code>。</p><hr><p><strong>Q12：分析以下代码的问题：</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> collections <span class="keyword">import</span> defaultdict</span><br><span class="line"></span><br><span class="line">data = [(<span class="string">&quot;A&quot;</span>, <span class="number">1</span>), (<span class="string">&quot;B&quot;</span>, <span class="number">2</span>), (<span class="string">&quot;A&quot;</span>, <span class="number">3</span>)]</span><br><span class="line">groups = defaultdict(<span class="built_in">list</span>)</span><br><span class="line"><span class="keyword">for</span> key, value <span class="keyword">in</span> data:</span><br><span class="line">    groups[key].append(value)</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">dict</span>(groups))</span><br></pre></td></tr></table></figure><p><strong>A</strong>：这段代码没有问题，是正确的分组方式。输出为 <code>{&#39;A&#39;: [1, 3], &#39;B&#39;: [2]}</code>。</p><hr><p><strong>Q13：以下代码的输出是什么？</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> heapq</span><br><span class="line"></span><br><span class="line">nums = [<span class="number">3</span>, <span class="number">1</span>, <span class="number">4</span>, <span class="number">1</span>, <span class="number">5</span>, <span class="number">9</span>]</span><br><span class="line">heapq.heapify(nums)</span><br><span class="line"><span class="built_in">print</span>(heapq.heappop(nums))</span><br><span class="line"><span class="built_in">print</span>(heapq.heappop(nums))</span><br></pre></td></tr></table></figure><p><strong>A</strong>：输出为 <code>1</code> 和 <code>1</code>。<code>heapify</code> 将列表转为最小堆，<code>heappop</code> 每次弹出最小元素。</p><hr><h2 id="16-实战项目：日志分析系统"><a href="#16-实战项目：日志分析系统" class="headerlink" title="16. 实战项目：日志分析系统"></a>16. 实战项目：日志分析系统</h2><h3 id="16-1-项目概述"><a href="#16-1-项目概述" class="headerlink" title="16.1 项目概述"></a>16.1 项目概述</h3><p><strong>项目目标</strong>：开发一个命令行日志分析工具 <code>loganalyzer</code>，支持分析 Web 服务器日志。</p><p><strong>功能需求</strong>：</p><ul><li><code>loganalyzer access &lt;logfile&gt;</code> - 分析访问日志</li><li><code>loganalyzer errors &lt;logfile&gt;</code> - 统计错误类型</li><li><code>loganalyzer top &lt;logfile&gt;</code> - 显示 Top 访问 IP&#x2F;路径</li><li><code>loganalyzer export &lt;logfile&gt; &lt;output&gt;</code> - 导出分析结果</li></ul><h3 id="16-2-项目结构"><a href="#16-2-项目结构" class="headerlink" title="16.2 项目结构"></a>16.2 项目结构</h3><figure class="highlight plaintext"><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><span class="line">loganalyzer/</span><br><span class="line">├── README.md</span><br><span class="line">├── pyproject.toml</span><br><span class="line">├── src/</span><br><span class="line">│   └── loganalyzer/</span><br><span class="line">│       ├── __init__.py</span><br><span class="line">│       ├── __main__.py</span><br><span class="line">│       ├── cli.py</span><br><span class="line">│       ├── analyzer.py</span><br><span class="line">│       ├── parsers/</span><br><span class="line">│       │   ├── __init__.py</span><br><span class="line">│       │   └── nginx_parser.py</span><br><span class="line">│       └── exporters/</span><br><span class="line">│           ├── __init__.py</span><br><span class="line">│           └── json_exporter.py</span><br><span class="line">└── tests/</span><br><span class="line">    ├── __init__.py</span><br><span class="line">    └── test_analyzer.py</span><br></pre></td></tr></table></figure><h3 id="16-3-核心代码"><a href="#16-3-核心代码" class="headerlink" title="16.3 核心代码"></a>16.3 核心代码</h3><p><strong>pyproject.toml</strong>：</p><figure class="highlight toml"><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><span class="line"><span class="section">[build-system]</span></span><br><span class="line"><span class="attr">requires</span> = [<span class="string">&quot;setuptools&gt;=61.0&quot;</span>, <span class="string">&quot;wheel&quot;</span>]</span><br><span class="line"><span class="attr">build-backend</span> = <span class="string">&quot;setuptools.build_meta&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="section">[project]</span></span><br><span class="line"><span class="attr">name</span> = <span class="string">&quot;loganalyzer&quot;</span></span><br><span class="line"><span class="attr">version</span> = <span class="string">&quot;1.0.0&quot;</span></span><br><span class="line"><span class="attr">description</span> = <span class="string">&quot;A log analysis tool&quot;</span></span><br><span class="line"><span class="attr">readme</span> = <span class="string">&quot;README.md&quot;</span></span><br><span class="line"><span class="attr">license</span> = &#123;text = <span class="string">&quot;MIT&quot;</span>&#125;</span><br><span class="line"><span class="attr">authors</span> = [</span><br><span class="line">    &#123;name = <span class="string">&quot;Your Name&quot;</span>, email = <span class="string">&quot;you@example.com&quot;</span>&#125;</span><br><span class="line">]</span><br><span class="line"><span class="attr">requires-python</span> = <span class="string">&quot;&gt;=3.9&quot;</span></span><br><span class="line"><span class="attr">dependencies</span> = [</span><br><span class="line">    <span class="string">&quot;click&gt;=8.0&quot;</span>,</span><br><span class="line">]</span><br><span class="line"></span><br><span class="line"><span class="section">[project.scripts]</span></span><br><span class="line"><span class="attr">loganalyzer</span> = <span class="string">&quot;loganalyzer.cli:main&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="section">[tool.setuptools.packages.find]</span></span><br><span class="line"><span class="attr">where</span> = [<span class="string">&quot;src&quot;</span>]</span><br></pre></td></tr></table></figure><p><strong>cli.py</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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> click</span><br><span class="line"><span class="keyword">from</span> loganalyzer.analyzer <span class="keyword">import</span> LogAnalyzer</span><br><span class="line"></span><br><span class="line"><span class="meta">@click.group()</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">main</span>():</span><br><span class="line">    <span class="string">&quot;&quot;&quot;LogAnalyzer - 日志分析工具&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@main.command(<span class="params"><span class="string">&quot;access&quot;</span></span>)</span></span><br><span class="line"><span class="meta">@click.argument(<span class="params"><span class="string">&quot;logfile&quot;</span></span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">analyze_access</span>(<span class="params">logfile</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;分析访问日志。&quot;&quot;&quot;</span></span><br><span class="line">    analyzer = LogAnalyzer(logfile)</span><br><span class="line">    stats = analyzer.get_access_stats()</span><br><span class="line">    click.echo(<span class="string">f&quot;总访问次数: <span class="subst">&#123;stats[<span class="string">&#x27;total&#x27;</span>]&#125;</span>&quot;</span>)</span><br><span class="line">    click.echo(<span class="string">f&quot;平均响应大小: <span class="subst">&#123;stats[<span class="string">&#x27;avg_size&#x27;</span>]:<span class="number">.2</span>f&#125;</span> bytes&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="meta">@main.command(<span class="params"><span class="string">&quot;errors&quot;</span></span>)</span></span><br><span class="line"><span class="meta">@click.argument(<span class="params"><span class="string">&quot;logfile&quot;</span></span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">analyze_errors</span>(<span class="params">logfile</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;统计错误类型。&quot;&quot;&quot;</span></span><br><span class="line">    analyzer = LogAnalyzer(logfile)</span><br><span class="line">    errors = analyzer.get_error_stats()</span><br><span class="line">    <span class="keyword">for</span> status, count <span class="keyword">in</span> errors.items():</span><br><span class="line">        click.echo(<span class="string">f&quot;<span class="subst">&#123;status&#125;</span>: <span class="subst">&#123;count&#125;</span> 次&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="meta">@main.command(<span class="params"><span class="string">&quot;top&quot;</span></span>)</span></span><br><span class="line"><span class="meta">@click.argument(<span class="params"><span class="string">&quot;logfile&quot;</span></span>)</span></span><br><span class="line"><span class="meta">@click.option(<span class="params"><span class="string">&quot;-t&quot;</span>, <span class="string">&quot;--type&quot;</span>, default=<span class="string">&quot;ip&quot;</span>, <span class="built_in">type</span>=click.Choice(<span class="params">[<span class="string">&quot;ip&quot;</span>, <span class="string">&quot;path&quot;</span>]</span>)</span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">show_top</span>(<span class="params">logfile, <span class="built_in">type</span></span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;显示 Top IP 或路径。&quot;&quot;&quot;</span></span><br><span class="line">    analyzer = LogAnalyzer(logfile)</span><br><span class="line">    <span class="keyword">if</span> <span class="built_in">type</span> == <span class="string">&quot;ip&quot;</span>:</span><br><span class="line">        top = analyzer.get_top_ips()</span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        top = analyzer.get_top_paths()</span><br><span class="line">    </span><br><span class="line">    click.echo(<span class="string">f&quot;Top 10 <span class="subst">&#123;<span class="built_in">type</span>&#125;</span>:&quot;</span>)</span><br><span class="line">    <span class="keyword">for</span> item, count <span class="keyword">in</span> top:</span><br><span class="line">        click.echo(<span class="string">f&quot;  <span class="subst">&#123;item&#125;</span>: <span class="subst">&#123;count&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="meta">@main.command(<span class="params"><span class="string">&quot;export&quot;</span></span>)</span></span><br><span class="line"><span class="meta">@click.argument(<span class="params"><span class="string">&quot;logfile&quot;</span></span>)</span></span><br><span class="line"><span class="meta">@click.argument(<span class="params"><span class="string">&quot;output&quot;</span></span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">export_results</span>(<span class="params">logfile, output</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;导出分析结果。&quot;&quot;&quot;</span></span><br><span class="line">    analyzer = LogAnalyzer(logfile)</span><br><span class="line">    analyzer.export(output)</span><br><span class="line">    click.echo(<span class="string">f&quot;结果已导出到: <span class="subst">&#123;output&#125;</span>&quot;</span>)</span><br></pre></td></tr></table></figure><p><strong>analyzer.py</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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> collections <span class="keyword">import</span> Counter</span><br><span class="line"><span class="keyword">from</span> loganalyzer.parsers.nginx_parser <span class="keyword">import</span> NginxParser</span><br><span class="line"><span class="keyword">from</span> loganalyzer.exporters.json_exporter <span class="keyword">import</span> JsonExporter</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">LogAnalyzer</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;日志分析器。&quot;&quot;&quot;</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, logfile</span>):</span><br><span class="line">        <span class="variable language_">self</span>.logfile = logfile</span><br><span class="line">        <span class="variable language_">self</span>.parser = NginxParser()</span><br><span class="line">        <span class="variable language_">self</span>.exporter = JsonExporter()</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_entries</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;获取所有日志条目。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>.parser.parse(<span class="variable language_">self</span>.logfile)</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_access_stats</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;获取访问统计。&quot;&quot;&quot;</span></span><br><span class="line">        entries = <span class="variable language_">self</span>.get_entries()</span><br><span class="line">        total = <span class="built_in">len</span>(entries)</span><br><span class="line">        total_size = <span class="built_in">sum</span>(<span class="built_in">int</span>(e[<span class="string">&quot;size&quot;</span>]) <span class="keyword">for</span> e <span class="keyword">in</span> entries)</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> &#123;</span><br><span class="line">            <span class="string">&quot;total&quot;</span>: total,</span><br><span class="line">            <span class="string">&quot;avg_size&quot;</span>: total_size / total <span class="keyword">if</span> total &gt; <span class="number">0</span> <span class="keyword">else</span> <span class="number">0</span>,</span><br><span class="line">        &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_error_stats</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;获取错误统计。&quot;&quot;&quot;</span></span><br><span class="line">        entries = <span class="variable language_">self</span>.get_entries()</span><br><span class="line">        errors = [e[<span class="string">&quot;status&quot;</span>] <span class="keyword">for</span> e <span class="keyword">in</span> entries <span class="keyword">if</span> e[<span class="string">&quot;status&quot;</span>].startswith(<span class="string">&quot;4&quot;</span>) <span class="keyword">or</span> e[<span class="string">&quot;status&quot;</span>].startswith(<span class="string">&quot;5&quot;</span>)]</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">dict</span>(Counter(errors))</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_top_ips</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;获取 Top IP。&quot;&quot;&quot;</span></span><br><span class="line">        entries = <span class="variable language_">self</span>.get_entries()</span><br><span class="line">        counter = Counter(e[<span class="string">&quot;ip&quot;</span>] <span class="keyword">for</span> e <span class="keyword">in</span> entries)</span><br><span class="line">        <span class="keyword">return</span> counter.most_common(<span class="number">10</span>)</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_top_paths</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;获取 Top 路径。&quot;&quot;&quot;</span></span><br><span class="line">        entries = <span class="variable language_">self</span>.get_entries()</span><br><span class="line">        counter = Counter(e[<span class="string">&quot;path&quot;</span>] <span class="keyword">for</span> e <span class="keyword">in</span> entries)</span><br><span class="line">        <span class="keyword">return</span> counter.most_common(<span class="number">10</span>)</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">export</span>(<span class="params">self, output</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;导出分析结果。&quot;&quot;&quot;</span></span><br><span class="line">        data = &#123;</span><br><span class="line">            <span class="string">&quot;access_stats&quot;</span>: <span class="variable language_">self</span>.get_access_stats(),</span><br><span class="line">            <span class="string">&quot;error_stats&quot;</span>: <span class="variable language_">self</span>.get_error_stats(),</span><br><span class="line">            <span class="string">&quot;top_ips&quot;</span>: <span class="variable language_">self</span>.get_top_ips(),</span><br><span class="line">            <span class="string">&quot;top_paths&quot;</span>: <span class="variable language_">self</span>.get_top_paths(),</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="variable language_">self</span>.exporter.export(data, output)</span><br></pre></td></tr></table></figure><p><strong>parsers&#x2F;nginx_parser.py</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><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> re</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">NginxParser</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;Nginx 日志解析器。&quot;&quot;&quot;</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="variable language_">self</span>._pattern = re.<span class="built_in">compile</span>(</span><br><span class="line">            <span class="string">r&#x27;(?P&lt;ip&gt;\d+\.\d+\.\d+\.\d+) - - \[(?P&lt;time&gt;.*?)\] &#x27;</span></span><br><span class="line">            <span class="string">r&#x27;&quot;(?P&lt;method&gt;\w+) (?P&lt;path&gt;.*?) HTTP/1\.\d&quot; &#x27;</span></span><br><span class="line">            <span class="string">r&#x27;(?P&lt;status&gt;\d+) (?P&lt;size&gt;\d+)&#x27;</span></span><br><span class="line">        )</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">parse</span>(<span class="params">self, logfile</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;解析日志文件。&quot;&quot;&quot;</span></span><br><span class="line">        entries = []</span><br><span class="line">        <span class="keyword">with</span> <span class="built_in">open</span>(logfile, <span class="string">&quot;r&quot;</span>, encoding=<span class="string">&quot;utf-8&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">            <span class="keyword">for</span> line <span class="keyword">in</span> f:</span><br><span class="line">                <span class="keyword">match</span> = <span class="variable language_">self</span>._pattern.<span class="keyword">match</span>(line)</span><br><span class="line">                <span class="keyword">if</span> <span class="keyword">match</span>:</span><br><span class="line">                    entries.append(<span class="keyword">match</span>.groupdict())</span><br><span class="line">        <span class="keyword">return</span> entries</span><br></pre></td></tr></table></figure><p><strong>exporters&#x2F;json_exporter.py</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> json</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">JsonExporter</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;JSON 导出器。&quot;&quot;&quot;</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">export</span>(<span class="params">self, data, output</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;导出数据到 JSON 文件。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">with</span> <span class="built_in">open</span>(output, <span class="string">&quot;w&quot;</span>, encoding=<span class="string">&quot;utf-8&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">            json.dump(data, f, ensure_ascii=<span class="literal">False</span>, indent=<span class="number">2</span>)</span><br></pre></td></tr></table></figure><h3 id="16-4-项目运行"><a href="#16-4-项目运行" class="headerlink" title="16.4 项目运行"></a>16.4 项目运行</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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 开发模式安装</span></span><br><span class="line">pip install -e .</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用命令</span></span><br><span class="line">loganalyzer access access.log          <span class="comment"># 分析访问统计</span></span><br><span class="line">loganalyzer errors access.log          <span class="comment"># 统计错误</span></span><br><span class="line">loganalyzer top access.log --<span class="built_in">type</span> ip   <span class="comment"># Top IP</span></span><br><span class="line">loganalyzer top access.log --<span class="built_in">type</span> path <span class="comment"># Top 路径</span></span><br><span class="line">loganalyzer <span class="built_in">export</span> access.log result.json</span><br><span class="line"></span><br><span class="line"><span class="comment"># 或者使用 python -m</span></span><br><span class="line">python -m loganalyzer access access.log</span><br></pre></td></tr></table></figure><h3 id="16-5-项目亮点"><a href="#16-5-项目亮点" class="headerlink" title="16.5 项目亮点"></a>16.5 项目亮点</h3><ol><li><strong>模块化设计</strong>：解析器和导出器独立，易于扩展</li><li><strong>正则优化</strong>：预编译正则表达式，提升性能</li><li><strong>流式处理</strong>：逐行读取日志，内存效率高</li><li><strong>命令行友好</strong>：使用 click 库，提供丰富的命令和选项</li></ol><hr><h2 id="附录：第八阶段自检清单"><a href="#附录：第八阶段自检清单" class="headerlink" title="附录：第八阶段自检清单"></a>附录：第八阶段自检清单</h2><p>在继续学习第九阶段之前，请确保你能：</p><ul><li><input disabled="" type="checkbox"> 使用 <code>re</code> 模块完成常见的匹配、查找、替换操作</li><li><input disabled="" type="checkbox"> 使用 <code>collections</code> 中的 <code>deque</code>、<code>Counter</code>、<code>defaultdict</code>、<code>namedtuple</code></li><li><input disabled="" type="checkbox"> 使用 <code>heapq</code> 实现优先队列，使用 <code>bisect</code> 维护有序列表</li><li><input disabled="" type="checkbox"> 使用 <code>itertools</code> 生成排列组合、无限序列、分组</li><li><input disabled="" type="checkbox"> 使用 <code>datetime</code> 进行时间计算、格式化、时区转换</li><li><input disabled="" type="checkbox"> 使用 <code>decimal</code> 进行精确的小数计算</li><li><input disabled="" type="checkbox"> 使用 <code>secrets</code> 生成安全的随机数和 token</li><li><input disabled="" type="checkbox"> 选择合适的并发模型（threading&#x2F;multiprocessing&#x2F;asyncio）</li><li><input disabled="" type="checkbox"> 使用 <code>concurrent.futures</code> 的线程池&#x2F;进程池</li><li><input disabled="" type="checkbox"> 编写 <code>unittest</code> 测试用例，使用 <code>mock</code> 进行依赖隔离</li><li><input disabled="" type="checkbox"> 使用 <code>argparse</code> 处理命令行参数</li><li><input disabled="" type="checkbox"> 使用 <code>base64</code> 进行编码解码</li><li><input disabled="" type="checkbox"> 使用 <code>inspect</code> 进行代码内省</li><li><input disabled="" type="checkbox"> 使用 <code>weakref</code> 解决循环引用问题</li><li><input disabled="" type="checkbox"> 区分浅拷贝和深拷贝的使用场景</li></ul><hr><blockquote><p><strong>工程师寄语</strong>：标准库是 Python 最大的宝藏。在考虑安装第三方包之前，先问一句：”标准库里有吗？” 熟悉标准库不仅能减少依赖，还能提高代码的可维护性和可移植性。<code>collections</code>、<code>itertools</code>、<code>functools</code> 这三个模块特别值得反复研读。</p></blockquote><blockquote><p><strong>性能提示</strong>：对于频繁调用的正则表达式，务必使用 <code>re.compile()</code> 编译后复用。对于大数据量处理，优先使用 <code>itertools</code> 和生成器表达式，避免一次性加载所有数据到内存。</p></blockquote>]]>
    </content>
    <id>https://itdxb.cn/2026/07/23/python/document/%E3%80%90Python%E3%80%91%E6%A0%87%E5%87%86%E5%BA%93%E6%A0%B8%E5%BF%83%E6%A8%A1%E5%9D%97%EF%BC%88%E7%AC%AC%E5%85%AB%E9%98%B6%E6%AE%B5%EF%BC%89/</id>
    <link href="https://itdxb.cn/2026/07/23/python/document/%E3%80%90Python%E3%80%91%E6%A0%87%E5%87%86%E5%BA%93%E6%A0%B8%E5%BF%83%E6%A8%A1%E5%9D%97%EF%BC%88%E7%AC%AC%E5%85%AB%E9%98%B6%E6%AE%B5%EF%BC%89/"/>
    <published>2026-07-23T07:00:00.000Z</published>
    <summary>Python 标准库核心模块详解，按功能分类梳理常用库，建立&quot;标准库优先&quot;的工程思维。</summary>
    <title>【Python】标准库核心模块（第八阶段）</title>
    <updated>2026-07-24T05:46:02.738Z</updated>
  </entry>
  <entry>
    <author>
      <name>Super Bing</name>
    </author>
    <category term="Python" scheme="https://itdxb.cn/categories/Python/"/>
    <category term="教程" scheme="https://itdxb.cn/tags/%E6%95%99%E7%A8%8B/"/>
    <category term="Python" scheme="https://itdxb.cn/tags/Python/"/>
    <category term="高级特性" scheme="https://itdxb.cn/tags/%E9%AB%98%E7%BA%A7%E7%89%B9%E6%80%A7/"/>
    <category term="迭代器" scheme="https://itdxb.cn/tags/%E8%BF%AD%E4%BB%A3%E5%99%A8/"/>
    <category term="装饰器" scheme="https://itdxb.cn/tags/%E8%A3%85%E9%A5%B0%E5%99%A8/"/>
    <content>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><h1 id="七、Python-高级特性（语言表达能力）"><a href="#七、Python-高级特性（语言表达能力）" class="headerlink" title="七、Python 高级特性（语言表达能力）"></a>七、Python 高级特性（语言表达能力）</h1><blockquote><p><strong>阶段定位</strong>：本阶段深入 Python 的核心机制，理解这些特性不仅能写出更优雅的代码，还能让你真正”读懂” Python 的内部运作方式。这些知识是区分”会用 Python”和”精通 Python”的分水岭。</p></blockquote><figure class="highlight plaintext"><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><span class="line">┌─────────────────────────────────────────────────────────────────┐</span><br><span class="line">│                    Python 高级特性知识图谱                      │</span><br><span class="line">├─────────────────────────────────────────────────────────────────┤</span><br><span class="line">│                                                                 │</span><br><span class="line">│  1. 迭代器协议 ─→ Iterable / Iterator / __iter__ / __next__     │</span><br><span class="line">│       │                                                         │</span><br><span class="line">│  2. 生成器 ─────→ send() / throw() / close() / 状态机          │</span><br><span class="line">│       │                                                         │</span><br><span class="line">│  3. 装饰器 ─────→ 带参数装饰器 / 类装饰器 / 叠加顺序 / lru_cache │</span><br><span class="line">│       │                                                         │</span><br><span class="line">│  4. 上下文管理器 ─→ @contextmanager / asynccontextmanager       │</span><br><span class="line">│       │                                                         │</span><br><span class="line">│  5. 描述符 ─────→ __get__ / __set__ / __delete__ / @property    │</span><br><span class="line">│       │                                                         │</span><br><span class="line">│  6. 元类 ───────→ type / __init_subclass__ / 类创建控制          │</span><br><span class="line">│       │                                                         │</span><br><span class="line">│  7. 协程 ───────→ async / await / asyncio / 异步迭代器           │</span><br><span class="line">│       │                                                         │</span><br><span class="line">│  8. 类型系统 ───→ typing / Protocol / TypedDict / Generic       │</span><br><span class="line">│       │                                                         │</span><br><span class="line">│  9. 高级函数 ───→ functools / operator / contextvars / singledispatch │</span><br><span class="line">│                                                                 │</span><br><span class="line">└─────────────────────────────────────────────────────────────────┘</span><br></pre></td></tr></table></figure><hr><h2 id="1-迭代器协议与可迭代对象"><a href="#1-迭代器协议与可迭代对象" class="headerlink" title="1. 迭代器协议与可迭代对象"></a>1. 迭代器协议与可迭代对象</h2><h3 id="1-1-迭代器-vs-可迭代对象"><a href="#1-1-迭代器-vs-可迭代对象" class="headerlink" title="1.1 迭代器 vs 可迭代对象"></a>1.1 迭代器 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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 可迭代对象（Iterable）：实现了 __iter__() 的对象</span></span><br><span class="line">my_list = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]</span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">hasattr</span>(my_list, <span class="string">&quot;__iter__&quot;</span>))   <span class="comment"># True</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 迭代器（Iterator）：实现了 __iter__() 和 __next__() 的对象</span></span><br><span class="line">iterator = <span class="built_in">iter</span>(my_list)</span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">hasattr</span>(iterator, <span class="string">&quot;__next__&quot;</span>))  <span class="comment"># True</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 迭代器只能遍历一次</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">next</span>(iterator))   <span class="comment"># 1</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">next</span>(iterator))   <span class="comment"># 2</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">next</span>(iterator))   <span class="comment"># 3</span></span><br><span class="line"><span class="comment"># print(next(iterator))   # StopIteration</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 再次遍历需要重新创建迭代器</span></span><br><span class="line"><span class="keyword">for</span> item <span class="keyword">in</span> <span class="built_in">iter</span>(my_list):</span><br><span class="line">    <span class="built_in">print</span>(item, end=<span class="string">&quot; &quot;</span>)   <span class="comment"># 1 2 3</span></span><br></pre></td></tr></table></figure><p><strong>迭代器协议关系图</strong>：</p><figure class="highlight plaintext"><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><span class="line">┌─────────────────────────────────────────────────────────┐</span><br><span class="line">│                  迭代器协议关系图                         │</span><br><span class="line">├─────────────────────────────────────────────────────────┤</span><br><span class="line">│                                                         │</span><br><span class="line">│  Iterable（可迭代对象）                                   │</span><br><span class="line">│    │                                                    │</span><br><span class="line">│    ├── 实现 __iter__()                                   │</span><br><span class="line">│    │    └── 返回 Iterator                                │</span><br><span class="line">│    │                                                    │</span><br><span class="line">│    └── 例子: list, tuple, str, dict, range              │</span><br><span class="line">│                                                         │</span><br><span class="line">│  Iterator（迭代器）                                       │</span><br><span class="line">│    │                                                    │</span><br><span class="line">│    ├── 实现 __iter__() → 返回 self                      │</span><br><span class="line">│    ├── 实现 __next__() → 返回下一个元素                  │</span><br><span class="line">│    │    └── 无元素时抛出 StopIteration                   │</span><br><span class="line">│    │                                                    │</span><br><span class="line">│    └── 特性: 惰性计算，只能遍历一次                       │</span><br><span class="line">│                                                         │</span><br><span class="line">│  for 循环的工作原理:                                      │</span><br><span class="line">│    for item in iterable:                                 │</span><br><span class="line">│      ↓                                                  │</span><br><span class="line">│    iterator = iter(iterable)                             │</span><br><span class="line">│    while True:                                          │</span><br><span class="line">│        try:                                             │</span><br><span class="line">│            item = next(iterator)                         │</span><br><span class="line">│            ...                                          │</span><br><span class="line">│        except StopIteration:                             │</span><br><span class="line">│            break                                        │</span><br><span class="line">│                                                         │</span><br><span class="line">└─────────────────────────────────────────────────────────┘</span><br></pre></td></tr></table></figure><h3 id="1-2-自定义迭代器"><a href="#1-2-自定义迭代器" class="headerlink" title="1.2 自定义迭代器"></a>1.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></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Countdown</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;倒计时迭代器。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, start</span>):</span><br><span class="line">        <span class="variable language_">self</span>.start = start</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__iter__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>          <span class="comment"># 迭代器本身返回 self</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__next__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">if</span> <span class="variable language_">self</span>.start &lt;= <span class="number">0</span>:</span><br><span class="line">            <span class="keyword">raise</span> StopIteration</span><br><span class="line">        <span class="variable language_">self</span>.start -= <span class="number">1</span></span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>.start + <span class="number">1</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用</span></span><br><span class="line"><span class="keyword">for</span> num <span class="keyword">in</span> Countdown(<span class="number">5</span>):</span><br><span class="line">    <span class="built_in">print</span>(num, end=<span class="string">&quot; &quot;</span>)   <span class="comment"># 5 4 3 2 1</span></span><br></pre></td></tr></table></figure><h3 id="1-3-自定义可迭代对象（分离迭代器）"><a href="#1-3-自定义可迭代对象（分离迭代器）" class="headerlink" title="1.3 自定义可迭代对象（分离迭代器）"></a>1.3 自定义可迭代对象（分离迭代器）</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><span class="line"><span class="keyword">class</span> <span class="title class_">Range</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;简化版 range 实现。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, start, stop=<span class="literal">None</span>, step=<span class="number">1</span></span>):</span><br><span class="line">        <span class="keyword">if</span> stop <span class="keyword">is</span> <span class="literal">None</span>:</span><br><span class="line">            start, stop = <span class="number">0</span>, start</span><br><span class="line">        <span class="variable language_">self</span>.start = start</span><br><span class="line">        <span class="variable language_">self</span>.stop = stop</span><br><span class="line">        <span class="variable language_">self</span>.step = step</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__iter__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="comment"># 每次迭代都返回全新的迭代器，支持多次遍历</span></span><br><span class="line">        <span class="keyword">return</span> _RangeIterator(<span class="variable language_">self</span>.start, <span class="variable language_">self</span>.stop, <span class="variable language_">self</span>.step)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__repr__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="string">f&quot;Range(<span class="subst">&#123;self.start&#125;</span>, <span class="subst">&#123;self.stop&#125;</span>, <span class="subst">&#123;self.step&#125;</span>)&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">_RangeIterator</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;独立的迭代器类。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, start, stop, step</span>):</span><br><span class="line">        <span class="variable language_">self</span>.current = start</span><br><span class="line">        <span class="variable language_">self</span>.stop = stop</span><br><span class="line">        <span class="variable language_">self</span>.step = step</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__iter__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__next__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">if</span> (<span class="variable language_">self</span>.step &gt; <span class="number">0</span> <span class="keyword">and</span> <span class="variable language_">self</span>.current &gt;= <span class="variable language_">self</span>.stop) <span class="keyword">or</span> \</span><br><span class="line">           (<span class="variable language_">self</span>.step &lt; <span class="number">0</span> <span class="keyword">and</span> <span class="variable language_">self</span>.current &lt;= <span class="variable language_">self</span>.stop):</span><br><span class="line">            <span class="keyword">raise</span> StopIteration</span><br><span class="line">        value = <span class="variable language_">self</span>.current</span><br><span class="line">        <span class="variable language_">self</span>.current += <span class="variable language_">self</span>.step</span><br><span class="line">        <span class="keyword">return</span> value</span><br><span class="line"></span><br><span class="line"><span class="comment"># 可多次遍历</span></span><br><span class="line">r = Range(<span class="number">3</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">list</span>(r))   <span class="comment"># [0, 1, 2]</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">list</span>(r))   <span class="comment"># [0, 1, 2] —— 可以再次遍历</span></span><br></pre></td></tr></table></figure><blockquote><p><strong>工程建议</strong>：如果需要支持多次遍历，将迭代器与可迭代对象分离；如果只需要单次遍历，让类同时实现两种协议（如 <code>Countdown</code>）。</p></blockquote><hr><h2 id="2-生成器的深层次应用"><a href="#2-生成器的深层次应用" class="headerlink" title="2. 生成器的深层次应用"></a>2. 生成器的深层次应用</h2><h3 id="2-1-send-双向通信"><a href="#2-1-send-双向通信" class="headerlink" title="2.1 send() 双向通信"></a>2.1 <code>send()</code> 双向通信</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><span class="line"><span class="keyword">def</span> <span class="title function_">accumulator</span>():</span><br><span class="line">    <span class="string">&quot;&quot;&quot;累加器：通过 send() 接收外部输入。&quot;&quot;&quot;</span></span><br><span class="line">    total = <span class="number">0</span></span><br><span class="line">    <span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">        value = <span class="keyword">yield</span> total    <span class="comment"># 发送当前总和，接收新值</span></span><br><span class="line">        <span class="keyword">if</span> value <span class="keyword">is</span> <span class="literal">None</span>:</span><br><span class="line">            <span class="keyword">break</span></span><br><span class="line">        total += value</span><br><span class="line"></span><br><span class="line">acc = accumulator()</span><br><span class="line"><span class="built_in">next</span>(acc)              <span class="comment"># 预激生成器（或用 acc.send(None)）</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(acc.send(<span class="number">10</span>))    <span class="comment"># 10</span></span><br><span class="line"><span class="built_in">print</span>(acc.send(<span class="number">20</span>))    <span class="comment"># 30</span></span><br><span class="line"><span class="built_in">print</span>(acc.send(<span class="number">5</span>))     <span class="comment"># 35</span></span><br><span class="line">acc.close()            <span class="comment"># 关闭生成器</span></span><br></pre></td></tr></table></figure><p><strong><code>send()</code> 工作原理</strong>：</p><figure class="highlight plaintext"><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><span class="line">┌─────────────────────────────────────────────────────────┐</span><br><span class="line">│                  send() 双向通信流程                     │</span><br><span class="line">├─────────────────────────────────────────────────────────┤</span><br><span class="line">│                                                         │</span><br><span class="line">│  1. next(gen) 或 gen.send(None) ─→ 预激生成器          │</span><br><span class="line">│     └── 执行到 yield，发送初始值，暂停                    │</span><br><span class="line">│                                                         │</span><br><span class="line">│  2. gen.send(value) ─→ 将 value 注入生成器              │</span><br><span class="line">│     └── yield 表达式返回 value，继续执行到下一个 yield   │</span><br><span class="line">│                                                         │</span><br><span class="line">│  3. gen.close() ─→ 关闭生成器                          │</span><br><span class="line">│     └── 在 yield 处抛出 GeneratorExit 异常              │</span><br><span class="line">│                                                         │</span><br><span class="line">│  执行序列示例:                                           │</span><br><span class="line">│    acc = accumulator()                                  │</span><br><span class="line">│    next(acc)       → 执行到 yield total，发送 0，暂停    │</span><br><span class="line">│    acc.send(10)    → yield 返回 10，total=10，发送 10   │</span><br><span class="line">│    acc.send(20)    → yield 返回 20，total=30，发送 30   │</span><br><span class="line">│                                                         │</span><br><span class="line">└─────────────────────────────────────────────────────────┘</span><br></pre></td></tr></table></figure><h3 id="2-2-throw-注入异常"><a href="#2-2-throw-注入异常" class="headerlink" title="2.2 throw() 注入异常"></a>2.2 <code>throw()</code> 注入异常</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">resilient_generator</span>():</span><br><span class="line">    <span class="string">&quot;&quot;&quot;可以处理被注入异常的生成器。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        <span class="keyword">yield</span> <span class="string">&quot;开始&quot;</span></span><br><span class="line">        <span class="keyword">yield</span> <span class="string">&quot;进行中&quot;</span></span><br><span class="line">    <span class="keyword">except</span> ValueError:</span><br><span class="line">        <span class="keyword">yield</span> <span class="string">&quot;捕获了 ValueError&quot;</span></span><br><span class="line">    <span class="keyword">yield</span> <span class="string">&quot;结束&quot;</span></span><br><span class="line"></span><br><span class="line">g = resilient_generator()</span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">next</span>(g))           <span class="comment"># 开始</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">next</span>(g))           <span class="comment"># 进行中</span></span><br><span class="line"><span class="built_in">print</span>(g.throw(ValueError, <span class="string">&quot;测试异常&quot;</span>))   <span class="comment"># 捕获了 ValueError</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">next</span>(g))           <span class="comment"># 结束</span></span><br></pre></td></tr></table></figure><h3 id="2-3-close-与生成器清理"><a href="#2-3-close-与生成器清理" class="headerlink" title="2.3 close() 与生成器清理"></a>2.3 <code>close()</code> 与生成器清理</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><span class="line"><span class="keyword">def</span> <span class="title function_">database_cursor</span>():</span><br><span class="line">    <span class="string">&quot;&quot;&quot;带清理逻辑的生成器。&quot;&quot;&quot;</span></span><br><span class="line">    conn = create_connection()</span><br><span class="line">    cursor = conn.cursor()</span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        cursor.execute(<span class="string">&quot;SELECT * FROM users&quot;</span>)</span><br><span class="line">        <span class="keyword">for</span> row <span class="keyword">in</span> cursor:</span><br><span class="line">            <span class="keyword">yield</span> row</span><br><span class="line">    <span class="keyword">finally</span>:</span><br><span class="line">        cursor.close()</span><br><span class="line">        conn.close()</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;资源已清理&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用</span></span><br><span class="line">g = database_cursor()</span><br><span class="line"><span class="keyword">for</span> row <span class="keyword">in</span> g:</span><br><span class="line">    <span class="keyword">if</span> row[<span class="string">&quot;id&quot;</span>] == <span class="number">5</span>:</span><br><span class="line">        g.close()   <span class="comment"># 提前终止，触发 finally 块</span></span><br><span class="line">        <span class="keyword">break</span></span><br></pre></td></tr></table></figure><h3 id="2-4-生成器状态机"><a href="#2-4-生成器状态机" class="headerlink" title="2.4 生成器状态机"></a>2.4 生成器状态机</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">traffic_light</span>():</span><br><span class="line">    <span class="string">&quot;&quot;&quot;用生成器实现状态机。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">        <span class="comment"># 红灯</span></span><br><span class="line">        <span class="keyword">yield</span> <span class="string">&quot;红灯&quot;</span></span><br><span class="line">        <span class="comment"># 绿灯</span></span><br><span class="line">        <span class="keyword">yield</span> <span class="string">&quot;绿灯&quot;</span></span><br><span class="line">        <span class="comment"># 黄灯</span></span><br><span class="line">        <span class="keyword">yield</span> <span class="string">&quot;黄灯&quot;</span></span><br><span class="line"></span><br><span class="line">light = traffic_light()</span><br><span class="line"><span class="keyword">for</span> _ <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">6</span>):</span><br><span class="line">    <span class="built_in">print</span>(<span class="built_in">next</span>(light), end=<span class="string">&quot; -&gt; &quot;</span>)</span><br><span class="line"><span class="comment"># 红灯 -&gt; 绿灯 -&gt; 黄灯 -&gt; 红灯 -&gt; 绿灯 -&gt; 黄灯 -&gt;</span></span><br></pre></td></tr></table></figure><hr><h2 id="3-装饰器进阶"><a href="#3-装饰器进阶" class="headerlink" title="3. 装饰器进阶"></a>3. 装饰器进阶</h2><h3 id="3-1-带参数的装饰器（复习-深入）"><a href="#3-1-带参数的装饰器（复习-深入）" class="headerlink" title="3.1 带参数的装饰器（复习+深入）"></a>3.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><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><span class="line"><span class="keyword">import</span> functools</span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">retry</span>(<span class="params">max_attempts=<span class="number">3</span>, delay=<span class="number">1</span>, backoff=<span class="number">2</span>, exceptions=(<span class="params">Exception,</span>)</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;可配置的重试装饰器。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">decorator</span>(<span class="params">func</span>):</span><br><span class="line"><span class="meta">        @functools.wraps(<span class="params">func</span>)</span></span><br><span class="line">        <span class="keyword">def</span> <span class="title function_">wrapper</span>(<span class="params">*args, **kwargs</span>):</span><br><span class="line">            last_exception = <span class="literal">None</span></span><br><span class="line">            current_delay = delay</span><br><span class="line">            <span class="keyword">for</span> attempt <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">1</span>, max_attempts + <span class="number">1</span>):</span><br><span class="line">                <span class="keyword">try</span>:</span><br><span class="line">                    <span class="keyword">return</span> func(*args, **kwargs)</span><br><span class="line">                <span class="keyword">except</span> exceptions <span class="keyword">as</span> e:</span><br><span class="line">                    last_exception = e</span><br><span class="line">                    <span class="keyword">if</span> attempt &lt; max_attempts:</span><br><span class="line">                        time.sleep(current_delay)</span><br><span class="line">                        current_delay *= backoff</span><br><span class="line">            <span class="keyword">raise</span> last_exception</span><br><span class="line">        <span class="keyword">return</span> wrapper</span><br><span class="line">    <span class="keyword">return</span> decorator</span><br><span class="line"></span><br><span class="line"><span class="meta">@retry(<span class="params">max_attempts=<span class="number">3</span>, delay=<span class="number">0.5</span>, exceptions=(<span class="params">ConnectionError,</span>)</span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">fetch_data</span>(<span class="params">url</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;模拟网络请求。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">import</span> random</span><br><span class="line">    <span class="keyword">if</span> random.random() &lt; <span class="number">0.7</span>:</span><br><span class="line">        <span class="keyword">raise</span> ConnectionError(<span class="string">f&quot;请求失败: <span class="subst">&#123;url&#125;</span>&quot;</span>)</span><br><span class="line">    <span class="keyword">return</span> &#123;<span class="string">&quot;status&quot;</span>: <span class="string">&quot;ok&quot;</span>, <span class="string">&quot;data&quot;</span>: []&#125;</span><br></pre></td></tr></table></figure><h3 id="3-2-类装饰器"><a href="#3-2-类装饰器" class="headerlink" title="3.2 类装饰器"></a>3.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><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">singleton</span>(<span class="params">cls</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;类装饰器：将任何类变为单例。&quot;&quot;&quot;</span></span><br><span class="line">    instances = &#123;&#125;</span><br><span class="line"><span class="meta">    @functools.wraps(<span class="params">cls</span>)</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">wrapper</span>(<span class="params">*args, **kwargs</span>):</span><br><span class="line">        <span class="keyword">if</span> cls <span class="keyword">not</span> <span class="keyword">in</span> instances:</span><br><span class="line">            instances[cls] = cls(*args, **kwargs)</span><br><span class="line">        <span class="keyword">return</span> instances[cls]</span><br><span class="line">    <span class="keyword">return</span> wrapper</span><br><span class="line"></span><br><span class="line"><span class="meta">@singleton</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Database</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, host=<span class="string">&quot;localhost&quot;</span></span>):</span><br><span class="line">        <span class="variable language_">self</span>.host = host</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;初始化 Database: <span class="subst">&#123;host&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line">db1 = Database(<span class="string">&quot;prod&quot;</span>)</span><br><span class="line">db2 = Database(<span class="string">&quot;test&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(db1 <span class="keyword">is</span> db2)        <span class="comment"># True</span></span><br><span class="line"><span class="built_in">print</span>(db1.host)          <span class="comment"># prod —— 第一次的值</span></span><br></pre></td></tr></table></figure><h3 id="3-3-装饰器叠加顺序"><a href="#3-3-装饰器叠加顺序" class="headerlink" title="3.3 装饰器叠加顺序"></a>3.3 装饰器叠加顺序</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><span class="line"><span class="meta">@decorator_a</span></span><br><span class="line"><span class="meta">@decorator_b</span></span><br><span class="line"><span class="meta">@decorator_c</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">my_func</span>():</span><br><span class="line">    <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 等价于：my_func = decorator_a(decorator_b(decorator_c(my_func)))</span></span><br><span class="line"><span class="comment"># 执行顺序：</span></span><br><span class="line"><span class="comment"># 1. 装饰时（定义阶段）：从内到外</span></span><br><span class="line"><span class="comment">#    decorator_c → decorator_b → decorator_a</span></span><br><span class="line"><span class="comment"># 2. 调用时（运行阶段）：从外到内</span></span><br><span class="line"><span class="comment">#    decorator_a 的 wrapper → decorator_b 的 wrapper → decorator_c 的 wrapper → my_func</span></span><br></pre></td></tr></table></figure><p><strong>装饰器叠加流程图</strong>：</p><figure class="highlight plaintext"><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><span class="line">┌─────────────────────────────────────────────────────────┐</span><br><span class="line">│                  装饰器叠加执行流程                       │</span><br><span class="line">├─────────────────────────────────────────────────────────┤</span><br><span class="line">│                                                         │</span><br><span class="line">│  定义阶段（装饰时）：                                      │</span><br><span class="line">│    @decorator_a                                          │</span><br><span class="line">│    @decorator_b                                          │</span><br><span class="line">│    @decorator_c                                          │</span><br><span class="line">│    def my_func():                                        │</span><br><span class="line">│        pass                                              │</span><br><span class="line">│                                                         │</span><br><span class="line">│    执行顺序: decorator_c → decorator_b → decorator_a     │</span><br><span class="line">│                                                         │</span><br><span class="line">│    等价于:                                                │</span><br><span class="line">│    temp1 = decorator_c(my_func)                          │</span><br><span class="line">│    temp2 = decorator_b(temp1)                            │</span><br><span class="line">│    my_func = decorator_a(temp2)                          │</span><br><span class="line">│                                                         │</span><br><span class="line">│  调用阶段（运行时）：                                      │</span><br><span class="line">│    my_func()                                             │</span><br><span class="line">│                                                         │</span><br><span class="line">│    执行顺序: decorator_a → decorator_b → decorator_c     │</span><br><span class="line">│              ↓              ↓              ↓             │</span><br><span class="line">│         wrapper_a     wrapper_b     wrapper_c           │</span><br><span class="line">│              ↓              ↓              ↓             │</span><br><span class="line">│         my_func()（原始函数）                             │</span><br><span class="line">│                                                         │</span><br><span class="line">└─────────────────────────────────────────────────────────┘</span><br></pre></td></tr></table></figure><h3 id="3-4-实用的装饰器模式"><a href="#3-4-实用的装饰器模式" class="headerlink" title="3.4 实用的装饰器模式"></a>3.4 实用的装饰器模式</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><span class="line"><span class="comment"># 1. 方法计时器</span></span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"><span class="keyword">import</span> functools</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">timer</span>(<span class="params">func</span>):</span><br><span class="line"><span class="meta">    @functools.wraps(<span class="params">func</span>)</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">wrapper</span>(<span class="params">*args, **kwargs</span>):</span><br><span class="line">        start = time.perf_counter()</span><br><span class="line">        result = func(*args, **kwargs)</span><br><span class="line">        elapsed = time.perf_counter() - start</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;<span class="subst">&#123;func.__name__&#125;</span> 耗时: <span class="subst">&#123;elapsed:<span class="number">.4</span>f&#125;</span>s&quot;</span>)</span><br><span class="line">        <span class="keyword">return</span> result</span><br><span class="line">    <span class="keyword">return</span> wrapper</span><br><span class="line"></span><br><span class="line"><span class="comment"># 2. 缓存（memoization）</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">memoize</span>(<span class="params">func</span>):</span><br><span class="line">    cache = &#123;&#125;</span><br><span class="line"><span class="meta">    @functools.wraps(<span class="params">func</span>)</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">wrapper</span>(<span class="params">*args</span>):</span><br><span class="line">        <span class="keyword">if</span> args <span class="keyword">not</span> <span class="keyword">in</span> cache:</span><br><span class="line">            cache[args] = func(*args)</span><br><span class="line">        <span class="keyword">return</span> cache[args]</span><br><span class="line">    wrapper.cache = cache</span><br><span class="line">    <span class="keyword">return</span> wrapper</span><br><span class="line"></span><br><span class="line"><span class="comment"># 3. 使用 functools.lru_cache（标准库推荐）</span></span><br><span class="line"><span class="keyword">from</span> functools <span class="keyword">import</span> lru_cache</span><br><span class="line"></span><br><span class="line"><span class="meta">@lru_cache(<span class="params">maxsize=<span class="number">128</span></span>)   </span><span class="comment"># 最多缓存 128 个结果</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">fibonacci</span>(<span class="params">n</span>):</span><br><span class="line">    <span class="keyword">if</span> n &lt; <span class="number">2</span>:</span><br><span class="line">        <span class="keyword">return</span> n</span><br><span class="line">    <span class="keyword">return</span> fibonacci(n - <span class="number">1</span>) + fibonacci(n - <span class="number">2</span>)</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(fibonacci(<span class="number">100</span>))     <span class="comment"># 瞬间完成</span></span><br><span class="line"><span class="built_in">print</span>(fibonacci.cache_info())   <span class="comment"># CacheInfo(hits=98, misses=101, maxsize=128, currsize=101)</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 4. 类型检查装饰器</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">type_check</span>(<span class="params">**types</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">decorator</span>(<span class="params">func</span>):</span><br><span class="line"><span class="meta">        @functools.wraps(<span class="params">func</span>)</span></span><br><span class="line">        <span class="keyword">def</span> <span class="title function_">wrapper</span>(<span class="params">*args, **kwargs</span>):</span><br><span class="line">            <span class="keyword">import</span> inspect</span><br><span class="line">            sig = inspect.signature(func)</span><br><span class="line">            bound = sig.bind(*args, **kwargs)</span><br><span class="line">            bound.apply_defaults()</span><br><span class="line">            <span class="keyword">for</span> name, expected <span class="keyword">in</span> types.items():</span><br><span class="line">                <span class="keyword">if</span> name <span class="keyword">in</span> bound.arguments:</span><br><span class="line">                    value = bound.arguments[name]</span><br><span class="line">                    <span class="keyword">if</span> <span class="keyword">not</span> <span class="built_in">isinstance</span>(value, expected):</span><br><span class="line">                        <span class="keyword">raise</span> TypeError(<span class="string">f&quot;<span class="subst">&#123;name&#125;</span> 期望 <span class="subst">&#123;expected.__name__&#125;</span>，实际 <span class="subst">&#123;<span class="built_in">type</span>(value).__name__&#125;</span>&quot;</span>)</span><br><span class="line">            <span class="keyword">return</span> func(*args, **kwargs)</span><br><span class="line">        <span class="keyword">return</span> wrapper</span><br><span class="line">    <span class="keyword">return</span> decorator</span><br><span class="line"></span><br><span class="line"><span class="meta">@type_check(<span class="params">name=<span class="built_in">str</span>, age=<span class="built_in">int</span></span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">create_user</span>(<span class="params">name, age</span>):</span><br><span class="line">    <span class="keyword">return</span> &#123;<span class="string">&quot;name&quot;</span>: name, <span class="string">&quot;age&quot;</span>: age&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># create_user(&quot;Alice&quot;, &quot;30&quot;)   # TypeError</span></span><br></pre></td></tr></table></figure><hr><h2 id="4-上下文管理器进阶"><a href="#4-上下文管理器进阶" class="headerlink" title="4. 上下文管理器进阶"></a>4. 上下文管理器进阶</h2><h3 id="4-1-contextlib-模块"><a href="#4-1-contextlib-模块" class="headerlink" title="4.1 contextlib 模块"></a>4.1 <code>contextlib</code> 模块</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><span class="line"><span class="keyword">from</span> contextlib <span class="keyword">import</span> contextmanager, suppress, redirect_stdout, ExitStack</span><br><span class="line"></span><br><span class="line"><span class="comment"># @contextmanager：用生成器写上下文管理器</span></span><br><span class="line"><span class="meta">@contextmanager</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">managed_resource</span>(<span class="params">name</span>):</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;获取资源: <span class="subst">&#123;name&#125;</span>&quot;</span>)</span><br><span class="line">    resource = &#123;<span class="string">&quot;name&quot;</span>: name, <span class="string">&quot;status&quot;</span>: <span class="string">&quot;active&quot;</span>&#125;</span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        <span class="keyword">yield</span> resource</span><br><span class="line">    <span class="keyword">finally</span>:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;释放资源: <span class="subst">&#123;name&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">with</span> managed_resource(<span class="string">&quot;db_connection&quot;</span>) <span class="keyword">as</span> res:</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;使用资源: <span class="subst">&#123;res&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># suppress：优雅地忽略异常</span></span><br><span class="line"><span class="keyword">with</span> suppress(FileNotFoundError):</span><br><span class="line">    os.remove(<span class="string">&quot;nonexistent.txt&quot;</span>)   <span class="comment"># 不会报错</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># redirect_stdout：重定向输出</span></span><br><span class="line"><span class="keyword">import</span> io</span><br><span class="line">buffer = io.StringIO()</span><br><span class="line"><span class="keyword">with</span> redirect_stdout(buffer):</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;这行被重定向到 buffer&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(buffer.getvalue())</span><br><span class="line"></span><br><span class="line"><span class="comment"># ExitStack：动态管理多个上下文</span></span><br><span class="line"><span class="keyword">with</span> ExitStack() <span class="keyword">as</span> stack:</span><br><span class="line">    files = [stack.enter_context(<span class="built_in">open</span>(<span class="string">f&quot;data<span class="subst">&#123;i&#125;</span>.txt&quot;</span>)) <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">3</span>)]</span><br><span class="line">    <span class="comment"># 所有文件会在 with 结束时自动关闭</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 甚至可以延迟决定进入哪些上下文</span></span><br><span class="line"><span class="keyword">with</span> ExitStack() <span class="keyword">as</span> stack:</span><br><span class="line">    <span class="keyword">if</span> condition:</span><br><span class="line">        file = stack.enter_context(<span class="built_in">open</span>(<span class="string">&quot;optional.txt&quot;</span>))</span><br><span class="line">    <span class="comment"># 条件满足时 file 会自动关闭</span></span><br></pre></td></tr></table></figure><h3 id="4-2-异步上下文管理器"><a href="#4-2-异步上下文管理器" class="headerlink" title="4.2 异步上下文管理器"></a>4.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><span class="line"><span class="keyword">import</span> asyncio</span><br><span class="line"><span class="keyword">from</span> contextlib <span class="keyword">import</span> asynccontextmanager</span><br><span class="line"></span><br><span class="line"><span class="meta">@asynccontextmanager</span></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">async_database</span>():</span><br><span class="line">    <span class="string">&quot;&quot;&quot;异步上下文管理器。&quot;&quot;&quot;</span></span><br><span class="line">    conn = <span class="keyword">await</span> create_async_connection()</span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        <span class="keyword">yield</span> conn</span><br><span class="line">    <span class="keyword">finally</span>:</span><br><span class="line">        <span class="keyword">await</span> conn.close()</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">main</span>():</span><br><span class="line">    <span class="keyword">async</span> <span class="keyword">with</span> async_database() <span class="keyword">as</span> db:</span><br><span class="line">        result = <span class="keyword">await</span> db.query(<span class="string">&quot;SELECT * FROM users&quot;</span>)</span><br><span class="line">        <span class="built_in">print</span>(result)</span><br><span class="line"></span><br><span class="line"><span class="comment"># asyncio.run(main())</span></span><br></pre></td></tr></table></figure><hr><h2 id="5-描述符与属性访问拦截"><a href="#5-描述符与属性访问拦截" class="headerlink" title="5. 描述符与属性访问拦截"></a>5. 描述符与属性访问拦截</h2><h3 id="5-1-描述符协议回顾"><a href="#5-1-描述符协议回顾" class="headerlink" title="5.1 描述符协议回顾"></a>5.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><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><span class="line"><span class="keyword">class</span> <span class="title class_">TypedAttribute</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;类型检查描述符。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, name, expected_type</span>):</span><br><span class="line">        <span class="variable language_">self</span>.name = name</span><br><span class="line">        <span class="variable language_">self</span>.expected_type = expected_type</span><br><span class="line">        <span class="variable language_">self</span>.storage_name = <span class="string">f&quot;_<span class="subst">&#123;name&#125;</span>&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__get__</span>(<span class="params">self, instance, owner</span>):</span><br><span class="line">        <span class="keyword">if</span> instance <span class="keyword">is</span> <span class="literal">None</span>:</span><br><span class="line">            <span class="keyword">return</span> <span class="variable language_">self</span></span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">getattr</span>(instance, <span class="variable language_">self</span>.storage_name, <span class="literal">None</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__set__</span>(<span class="params">self, instance, value</span>):</span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> <span class="built_in">isinstance</span>(value, <span class="variable language_">self</span>.expected_type):</span><br><span class="line">            <span class="keyword">raise</span> TypeError(<span class="string">f&quot;<span class="subst">&#123;self.name&#125;</span> 必须是 <span class="subst">&#123;self.expected_type.__name__&#125;</span>&quot;</span>)</span><br><span class="line">        <span class="built_in">setattr</span>(instance, <span class="variable language_">self</span>.storage_name, value)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__delete__</span>(<span class="params">self, instance</span>):</span><br><span class="line">        <span class="keyword">raise</span> AttributeError(<span class="string">f&quot;不能删除 <span class="subst">&#123;self.name&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Person</span>:</span><br><span class="line">    name = TypedAttribute(<span class="string">&quot;name&quot;</span>, <span class="built_in">str</span>)</span><br><span class="line">    age = TypedAttribute(<span class="string">&quot;age&quot;</span>, <span class="built_in">int</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, name, age</span>):</span><br><span class="line">        <span class="variable language_">self</span>.name = name</span><br><span class="line">        <span class="variable language_">self</span>.age = age</span><br><span class="line"></span><br><span class="line">p = Person(<span class="string">&quot;Alice&quot;</span>, <span class="number">30</span>)</span><br><span class="line"><span class="comment"># p.name = 123   # TypeError</span></span><br></pre></td></tr></table></figure><h3 id="5-2-getattr-与-getattribute"><a href="#5-2-getattr-与-getattribute" class="headerlink" title="5.2 __getattr__ 与 __getattribute__"></a>5.2 <code>__getattr__</code> 与 <code>__getattribute__</code></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></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">LazyObject</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;延迟加载属性。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="variable language_">self</span>._loaded = <span class="literal">False</span></span><br><span class="line">        <span class="variable language_">self</span>._data = &#123;&#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">_load</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> <span class="variable language_">self</span>._loaded:</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">&quot;加载数据...&quot;</span>)</span><br><span class="line">            <span class="variable language_">self</span>._data = &#123;<span class="string">&quot;name&quot;</span>: <span class="string">&quot;Alice&quot;</span>, <span class="string">&quot;age&quot;</span>: <span class="number">30</span>, <span class="string">&quot;city&quot;</span>: <span class="string">&quot;Beijing&quot;</span>&#125;</span><br><span class="line">            <span class="variable language_">self</span>._loaded = <span class="literal">True</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__getattr__</span>(<span class="params">self, name</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;访问不存在的属性时调用。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="variable language_">self</span>._load()</span><br><span class="line">        <span class="keyword">if</span> name <span class="keyword">in</span> <span class="variable language_">self</span>._data:</span><br><span class="line">            <span class="keyword">return</span> <span class="variable language_">self</span>._data[name]</span><br><span class="line">        <span class="keyword">raise</span> AttributeError(<span class="string">f&quot;&#x27;<span class="subst">&#123;<span class="built_in">type</span>(self).__name__&#125;</span>&#x27; 对象没有属性 &#x27;<span class="subst">&#123;name&#125;</span>&#x27;&quot;</span>)</span><br><span class="line"></span><br><span class="line">obj = LazyObject()</span><br><span class="line"><span class="built_in">print</span>(obj.name)    <span class="comment"># 加载数据... Alice</span></span><br><span class="line"><span class="built_in">print</span>(obj.age)     <span class="comment"># 30（不再加载）</span></span><br><span class="line"><span class="comment"># print(obj.xxx)   # AttributeError</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">StrictObject</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;严格拦截所有属性访问。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="variable language_">self</span>.allowed = &#123;<span class="string">&quot;name&quot;</span>, <span class="string">&quot;age&quot;</span>&#125;</span><br><span class="line">        <span class="variable language_">self</span>._name = <span class="string">&quot;Alice&quot;</span></span><br><span class="line">        <span class="variable language_">self</span>._age = <span class="number">30</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__getattribute__</span>(<span class="params">self, name</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;拦截所有属性访问（包括存在的属性）。&quot;&quot;&quot;</span></span><br><span class="line">        allowed = <span class="built_in">object</span>.__getattribute__(<span class="variable language_">self</span>, <span class="string">&quot;allowed&quot;</span>)</span><br><span class="line">        <span class="keyword">if</span> name <span class="keyword">not</span> <span class="keyword">in</span> allowed <span class="keyword">and</span> <span class="keyword">not</span> name.startswith(<span class="string">&quot;_&quot;</span>):</span><br><span class="line">            <span class="keyword">raise</span> AttributeError(<span class="string">f&quot;&#x27;<span class="subst">&#123;name&#125;</span>&#x27; 不在允许列表中&quot;</span>)</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">object</span>.__getattribute__(<span class="variable language_">self</span>, name)</span><br><span class="line"></span><br><span class="line">s = StrictObject()</span><br><span class="line"><span class="built_in">print</span>(s._name)     <span class="comment"># Alice</span></span><br><span class="line"><span class="comment"># print(s.email)   # AttributeError</span></span><br></pre></td></tr></table></figure><p><strong><code>__getattr__</code> vs <code>__getattribute__</code></strong>：</p><table><thead><tr><th>特性</th><th><code>__getattr__</code></th><th><code>__getattribute__</code></th></tr></thead><tbody><tr><td>触发时机</td><td>属性<strong>不存在</strong>时</td><td><strong>所有</strong>属性访问</td></tr><tr><td>默认行为</td><td>抛出 <code>AttributeError</code></td><td>正常查找属性</td></tr><tr><td>无限递归风险</td><td>无</td><td>高（必须用 <code>object.__getattribute__</code>）</td></tr><tr><td>使用场景</td><td>延迟加载、动态属性</td><td>严格访问控制、代理模式</td></tr></tbody></table><hr><h2 id="6-元类实战"><a href="#6-元类实战" class="headerlink" title="6. 元类实战"></a>6. 元类实战</h2><h3 id="6-1-init-subclass-（推荐替代方案）"><a href="#6-1-init-subclass-（推荐替代方案）" class="headerlink" title="6.1 __init_subclass__（推荐替代方案）"></a>6.1 <code>__init_subclass__</code>（推荐替代方案）</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><span class="line"><span class="keyword">class</span> <span class="title class_">PluginBase</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;插件基类：自动注册所有子类。&quot;&quot;&quot;</span></span><br><span class="line">    _registry = &#123;&#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init_subclass__</span>(<span class="params">cls, plugin_name=<span class="literal">None</span>, **kwargs</span>):</span><br><span class="line">        <span class="built_in">super</span>().__init_subclass__(**kwargs)</span><br><span class="line">        name = plugin_name <span class="keyword">or</span> cls.__name__</span><br><span class="line">        cls._registry[name] = cls</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;注册插件: <span class="subst">&#123;name&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">ImagePlugin</span>(PluginBase, plugin_name=<span class="string">&quot;image&quot;</span>):</span><br><span class="line">    <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">VideoPlugin</span>(<span class="title class_ inherited__">PluginBase</span>):</span><br><span class="line">    <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(PluginBase._registry)</span><br><span class="line"><span class="comment"># &#123;&#x27;image&#x27;: &lt;class &#x27;ImagePlugin&#x27;&gt;, &#x27;VideoPlugin&#x27;: &lt;class &#x27;VideoPlugin&#x27;&gt;&#125;</span></span><br></pre></td></tr></table></figure><h3 id="6-2-元类控制类创建"><a href="#6-2-元类控制类创建" class="headerlink" title="6.2 元类控制类创建"></a>6.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><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><span class="line"><span class="keyword">class</span> <span class="title class_">ValidateFieldsMeta</span>(<span class="title class_ inherited__">type</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;元类：自动为类添加字段验证。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__new__</span>(<span class="params">mcs, name, bases, namespace</span>):</span><br><span class="line">        annotations = namespace.get(<span class="string">&quot;__annotations__&quot;</span>, &#123;&#125;)</span><br><span class="line">        <span class="keyword">for</span> field_name, field_type <span class="keyword">in</span> annotations.items():</span><br><span class="line">            <span class="keyword">if</span> field_name.startswith(<span class="string">&quot;_&quot;</span>):</span><br><span class="line">                <span class="keyword">continue</span></span><br><span class="line">            <span class="comment"># 为每个注解字段创建 property</span></span><br><span class="line">            private_name = <span class="string">f&quot;_<span class="subst">&#123;field_name&#125;</span>&quot;</span></span><br><span class="line">            <span class="keyword">def</span> <span class="title function_">make_getter</span>(<span class="params">private</span>):</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">lambda</span> <span class="variable language_">self</span>: <span class="built_in">getattr</span>(<span class="variable language_">self</span>, private)</span><br><span class="line">            <span class="keyword">def</span> <span class="title function_">make_setter</span>(<span class="params">private, expected_type</span>):</span><br><span class="line">                <span class="keyword">def</span> <span class="title function_">setter</span>(<span class="params">self, value</span>):</span><br><span class="line">                    <span class="keyword">if</span> <span class="keyword">not</span> <span class="built_in">isinstance</span>(value, expected_type):</span><br><span class="line">                        <span class="keyword">raise</span> TypeError(<span class="string">f&quot;<span class="subst">&#123;field_name&#125;</span> 期望 <span class="subst">&#123;expected_type.__name__&#125;</span>&quot;</span>)</span><br><span class="line">                    <span class="built_in">setattr</span>(<span class="variable language_">self</span>, private, value)</span><br><span class="line">                <span class="keyword">return</span> setter</span><br><span class="line">            namespace[field_name] = <span class="built_in">property</span>(</span><br><span class="line">                make_getter(private_name),</span><br><span class="line">                make_setter(private_name, field_type)</span><br><span class="line">            )</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">super</span>().__new__(mcs, name, bases, namespace)</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Person</span>(metaclass=ValidateFieldsMeta):</span><br><span class="line">    name: <span class="built_in">str</span></span><br><span class="line">    age: <span class="built_in">int</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, name, age</span>):</span><br><span class="line">        <span class="variable language_">self</span>._name = name</span><br><span class="line">        <span class="variable language_">self</span>._age = age</span><br><span class="line"></span><br><span class="line">p = Person(<span class="string">&quot;Alice&quot;</span>, <span class="number">30</span>)</span><br><span class="line"><span class="comment"># p.age = &quot;abc&quot;   # TypeError</span></span><br></pre></td></tr></table></figure><hr><h2 id="7-协程与异步编程（async-await）"><a href="#7-协程与异步编程（async-await）" class="headerlink" title="7. 协程与异步编程（async&#x2F;await）"></a>7. 协程与异步编程（async&#x2F;await）</h2><h3 id="7-1-基础概念"><a href="#7-1-基础概念" class="headerlink" title="7.1 基础概念"></a>7.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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> asyncio</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">say_hello</span>():</span><br><span class="line">    <span class="string">&quot;&quot;&quot;协程函数：使用 async def 定义。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;Hello&quot;</span>)</span><br><span class="line">    <span class="keyword">await</span> asyncio.sleep(<span class="number">1</span>)   <span class="comment"># 非阻塞等待</span></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;World&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 协程对象（调用协程函数返回）</span></span><br><span class="line">coro = say_hello()</span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">type</span>(coro))   <span class="comment"># &lt;class &#x27;coroutine&#x27;&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 运行协程</span></span><br><span class="line">asyncio.run(say_hello())</span><br></pre></td></tr></table></figure><h3 id="7-2-asyncio-核心-API"><a href="#7-2-asyncio-核心-API" class="headerlink" title="7.2 asyncio 核心 API"></a>7.2 asyncio 核心 API</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><span class="line"><span class="keyword">import</span> asyncio</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">main</span>():</span><br><span class="line">    <span class="comment"># 创建任务</span></span><br><span class="line">    task1 = asyncio.create_task(fetch_data(<span class="string">&quot;url1&quot;</span>))</span><br><span class="line">    task2 = asyncio.create_task(fetch_data(<span class="string">&quot;url2&quot;</span>))</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 等待多个任务</span></span><br><span class="line">    results = <span class="keyword">await</span> asyncio.gather(task1, task2, return_exceptions=<span class="literal">True</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 等待任一完成</span></span><br><span class="line">    done, pending = <span class="keyword">await</span> asyncio.wait(</span><br><span class="line">        [task1, task2],</span><br><span class="line">        return_when=asyncio.FIRST_COMPLETED</span><br><span class="line">    )</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 超时控制</span></span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        result = <span class="keyword">await</span> asyncio.wait_for(risky_operation(), timeout=<span class="number">5</span>)</span><br><span class="line">    <span class="keyword">except</span> asyncio.TimeoutError:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;操作超时&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># shield：保护任务不被取消</span></span><br><span class="line">    important_task = asyncio.create_task(critical_operation())</span><br><span class="line">    result = <span class="keyword">await</span> asyncio.shield(important_task)</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">fetch_data</span>(<span class="params">url</span>):</span><br><span class="line">    <span class="keyword">await</span> asyncio.sleep(<span class="number">0.1</span>)</span><br><span class="line">    <span class="keyword">return</span> <span class="string">f&quot;data from <span class="subst">&#123;url&#125;</span>&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># asyncio.run(main())</span></span><br></pre></td></tr></table></figure><h3 id="7-3-异步迭代器与异步生成器"><a href="#7-3-异步迭代器与异步生成器" class="headerlink" title="7.3 异步迭代器与异步生成器"></a>7.3 异步迭代器与异步生成器</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><span class="line"><span class="keyword">import</span> asyncio</span><br><span class="line"><span class="keyword">import</span> aiohttp</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">AsyncDataSource</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;异步迭代器。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, urls</span>):</span><br><span class="line">        <span class="variable language_">self</span>.urls = urls</span><br><span class="line">        <span class="variable language_">self</span>.index = <span class="number">0</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__aiter__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">__anext__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">if</span> <span class="variable language_">self</span>.index &gt;= <span class="built_in">len</span>(<span class="variable language_">self</span>.urls):</span><br><span class="line">            <span class="keyword">raise</span> StopAsyncIteration</span><br><span class="line">        url = <span class="variable language_">self</span>.urls[<span class="variable language_">self</span>.index]</span><br><span class="line">        <span class="variable language_">self</span>.index += <span class="number">1</span></span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">await</span> fetch_url(url)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 异步生成器（更简洁）</span></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">async_paginator</span>(<span class="params">api, page_size=<span class="number">10</span></span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;异步分页生成器。&quot;&quot;&quot;</span></span><br><span class="line">    page = <span class="number">1</span></span><br><span class="line">    <span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">        data = <span class="keyword">await</span> api.fetch_page(page, page_size)</span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> data:</span><br><span class="line">            <span class="keyword">break</span></span><br><span class="line">        <span class="keyword">for</span> item <span class="keyword">in</span> data:</span><br><span class="line">            <span class="keyword">yield</span> item</span><br><span class="line">        page += <span class="number">1</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用</span></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">process</span>():</span><br><span class="line">    <span class="keyword">async</span> <span class="keyword">for</span> item <span class="keyword">in</span> async_paginator(api):</span><br><span class="line">        <span class="keyword">await</span> process_item(item)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 异步推导式</span></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">fetch_all</span>(<span class="params">urls</span>):</span><br><span class="line">    <span class="keyword">return</span> [<span class="keyword">await</span> fetch_url(url) <span class="keyword">async</span> <span class="keyword">for</span> url <span class="keyword">in</span> async_url_generator()]</span><br></pre></td></tr></table></figure><hr><h2 id="8-类型系统进阶"><a href="#8-类型系统进阶" class="headerlink" title="8. 类型系统进阶"></a>8. 类型系统进阶</h2><h3 id="8-1-typing-模块核心类型"><a href="#8-1-typing-模块核心类型" class="headerlink" title="8.1 typing 模块核心类型"></a>8.1 <code>typing</code> 模块核心类型</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><span class="line"><span class="keyword">from</span> typing <span class="keyword">import</span> (</span><br><span class="line">    <span class="type">List</span>, <span class="type">Dict</span>, <span class="type">Set</span>, <span class="type">Tuple</span>, <span class="type">Optional</span>, <span class="type">Union</span>,</span><br><span class="line">    <span class="type">Callable</span>, Iterator, Generator, <span class="type">Any</span>, <span class="type">Literal</span>,</span><br><span class="line">    TypedDict, Protocol, NewType, Final</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 基础类型</span></span><br><span class="line">numbers: <span class="type">List</span>[<span class="built_in">int</span>] = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]</span><br><span class="line">user: <span class="type">Dict</span>[<span class="built_in">str</span>, <span class="type">Union</span>[<span class="built_in">str</span>, <span class="built_in">int</span>]] = &#123;<span class="string">&quot;name&quot;</span>: <span class="string">&quot;Alice&quot;</span>, <span class="string">&quot;age&quot;</span>: <span class="number">30</span>&#125;</span><br><span class="line">point: <span class="type">Tuple</span>[<span class="built_in">float</span>, <span class="built_in">float</span>] = (<span class="number">1.0</span>, <span class="number">2.0</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Optional = Union[X, None]</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">find_user</span>(<span class="params">user_id: <span class="built_in">int</span></span>) -&gt; <span class="type">Optional</span>[User]:</span><br><span class="line">    ...</span><br><span class="line"></span><br><span class="line"><span class="comment"># Callable</span></span><br><span class="line">handler: <span class="type">Callable</span>[[<span class="built_in">int</span>, <span class="built_in">str</span>], <span class="built_in">bool</span>]</span><br><span class="line"></span><br><span class="line"><span class="comment"># Literal（限定具体值）</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">set_status</span>(<span class="params">status: <span class="type">Literal</span>[<span class="string">&quot;active&quot;</span>, <span class="string">&quot;inactive&quot;</span>, <span class="string">&quot;pending&quot;</span>]</span>) -&gt; <span class="literal">None</span>:</span><br><span class="line">    ...</span><br><span class="line"></span><br><span class="line"><span class="comment"># Final（不可变常量）</span></span><br><span class="line">MAX_RETRIES: Final[<span class="built_in">int</span>] = <span class="number">3</span></span><br><span class="line"><span class="comment"># MAX_RETRIES = 5   # mypy 会报错</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># NewType（创建语义化类型）</span></span><br><span class="line">UserId = NewType(<span class="string">&quot;UserId&quot;</span>, <span class="built_in">int</span>)</span><br><span class="line">OrderId = NewType(<span class="string">&quot;OrderId&quot;</span>, <span class="built_in">int</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">get_user</span>(<span class="params">user_id: UserId</span>) -&gt; User:</span><br><span class="line">    ...</span><br><span class="line"></span><br><span class="line"><span class="comment"># get_user(OrderId(123))   # mypy 会报错，类型不匹配</span></span><br></pre></td></tr></table></figure><h3 id="8-2-TypedDict"><a href="#8-2-TypedDict" class="headerlink" title="8.2 TypedDict"></a>8.2 TypedDict</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><span class="line"><span class="keyword">from</span> typing <span class="keyword">import</span> TypedDict, NotRequired  <span class="comment"># 3.11+</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Movie</span>(<span class="title class_ inherited__">TypedDict</span>):</span><br><span class="line">    name: <span class="built_in">str</span></span><br><span class="line">    year: <span class="built_in">int</span></span><br><span class="line">    rating: NotRequired[<span class="built_in">float</span>]   <span class="comment"># 可选字段</span></span><br><span class="line"></span><br><span class="line">movie: Movie = &#123;<span class="string">&quot;name&quot;</span>: <span class="string">&quot;Inception&quot;</span>, <span class="string">&quot;year&quot;</span>: <span class="number">2010</span>&#125;</span><br><span class="line">movie[<span class="string">&quot;rating&quot;</span>] = <span class="number">8.8</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 错误的键会被检查</span></span><br><span class="line"><span class="comment"># movie[&quot;director&quot;] = &quot;Nolan&quot;   # mypy 报错</span></span><br></pre></td></tr></table></figure><h3 id="8-3-Protocol（结构子类型）"><a href="#8-3-Protocol（结构子类型）" class="headerlink" title="8.3 Protocol（结构子类型）"></a>8.3 Protocol（结构子类型）</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><span class="line"><span class="keyword">from</span> typing <span class="keyword">import</span> Protocol</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Drawable</span>(<span class="title class_ inherited__">Protocol</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;协议：任何有 draw() 方法的对象。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">draw</span>(<span class="params">self</span>) -&gt; <span class="literal">None</span>:</span><br><span class="line">        ...</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">render</span>(<span class="params">item: Drawable</span>) -&gt; <span class="literal">None</span>:</span><br><span class="line">    item.draw()</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Circle</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">draw</span>(<span class="params">self</span>) -&gt; <span class="literal">None</span>:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;画圆&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Square</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">draw</span>(<span class="params">self</span>) -&gt; <span class="literal">None</span>:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;画方&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Circle 和 Square 都没有继承 Drawable，但满足协议</span></span><br><span class="line">render(Circle())   <span class="comment"># 通过类型检查</span></span><br><span class="line">render(Square())   <span class="comment"># 通过类型检查</span></span><br></pre></td></tr></table></figure><h3 id="8-4-泛型"><a href="#8-4-泛型" class="headerlink" title="8.4 泛型"></a>8.4 泛型</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><span class="line"><span class="keyword">from</span> typing <span class="keyword">import</span> TypeVar, <span class="type">Generic</span>, <span class="type">List</span></span><br><span class="line"></span><br><span class="line">T = TypeVar(<span class="string">&quot;T&quot;</span>)</span><br><span class="line">K = TypeVar(<span class="string">&quot;K&quot;</span>)</span><br><span class="line">V = TypeVar(<span class="string">&quot;V&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Stack</span>(<span class="type">Generic</span>[T]):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;泛型栈。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>) -&gt; <span class="literal">None</span>:</span><br><span class="line">        <span class="variable language_">self</span>._items: <span class="type">List</span>[T] = []</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">push</span>(<span class="params">self, item: T</span>) -&gt; <span class="literal">None</span>:</span><br><span class="line">        <span class="variable language_">self</span>._items.append(item)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">pop</span>(<span class="params">self</span>) -&gt; T:</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>._items.pop()</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">peek</span>(<span class="params">self</span>) -&gt; T:</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>._items[-<span class="number">1</span>]</span><br><span class="line"></span><br><span class="line">int_stack = Stack[<span class="built_in">int</span>]()</span><br><span class="line">int_stack.push(<span class="number">42</span>)</span><br><span class="line"><span class="comment"># int_stack.push(&quot;hello&quot;)   # mypy 报错</span></span><br><span class="line"></span><br><span class="line">str_stack = Stack[<span class="built_in">str</span>]()</span><br><span class="line">str_stack.push(<span class="string">&quot;hello&quot;</span>)</span><br></pre></td></tr></table></figure><hr><h2 id="9-高级函数技巧"><a href="#9-高级函数技巧" class="headerlink" title="9. 高级函数技巧"></a>9. 高级函数技巧</h2><h3 id="9-1-functools-模块"><a href="#9-1-functools-模块" class="headerlink" title="9.1 functools 模块"></a>9.1 <code>functools</code> 模块</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><span class="line"><span class="keyword">import</span> functools</span><br><span class="line"><span class="keyword">from</span> operator <span class="keyword">import</span> itemgetter, attrgetter</span><br><span class="line"></span><br><span class="line"><span class="comment"># partial：固定部分参数</span></span><br><span class="line"><span class="keyword">from</span> functools <span class="keyword">import</span> partial</span><br><span class="line"></span><br><span class="line">base_two = partial(<span class="built_in">int</span>, base=<span class="number">2</span>)</span><br><span class="line"><span class="built_in">print</span>(base_two(<span class="string">&quot;1010&quot;</span>))   <span class="comment"># 10</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># reduce</span></span><br><span class="line"><span class="keyword">from</span> functools <span class="keyword">import</span> reduce</span><br><span class="line">product = reduce(<span class="keyword">lambda</span> x, y: x * y, [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>])</span><br><span class="line"><span class="built_in">print</span>(product)   <span class="comment"># 120</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># cmp_to_key：将比较函数转为 key 函数</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">compare_length</span>(<span class="params">a, b</span>):</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">len</span>(a) - <span class="built_in">len</span>(b)</span><br><span class="line"></span><br><span class="line">words = [<span class="string">&quot;apple&quot;</span>, <span class="string">&quot;pie&quot;</span>, <span class="string">&quot;banana&quot;</span>]</span><br><span class="line">words.sort(key=functools.cmp_to_key(compare_length))</span><br><span class="line"></span><br><span class="line"><span class="comment"># cached_property：只计算一次的属性</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">DataProcessor</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, raw_data</span>):</span><br><span class="line">        <span class="variable language_">self</span>.raw_data = raw_data</span><br><span class="line"></span><br><span class="line"><span class="meta">    @functools.cached_property</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">processed</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;只计算一次，后续直接读取缓存。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;处理数据中...&quot;</span>)</span><br><span class="line">        <span class="keyword">return</span> [x.strip().lower() <span class="keyword">for</span> x <span class="keyword">in</span> <span class="variable language_">self</span>.raw_data]</span><br><span class="line"></span><br><span class="line"><span class="comment"># total_ordering：自动生成比较方法</span></span><br><span class="line"><span class="meta">@functools.total_ordering</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Version</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, major, minor, patch</span>):</span><br><span class="line">        <span class="variable language_">self</span>.major = major</span><br><span class="line">        <span class="variable language_">self</span>.minor = minor</span><br><span class="line">        <span class="variable language_">self</span>.patch = patch</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__eq__</span>(<span class="params">self, other</span>):</span><br><span class="line">        <span class="keyword">return</span> (<span class="variable language_">self</span>.major, <span class="variable language_">self</span>.minor, <span class="variable language_">self</span>.patch) == (other.major, other.minor, other.patch)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__lt__</span>(<span class="params">self, other</span>):</span><br><span class="line">        <span class="keyword">return</span> (<span class="variable language_">self</span>.major, <span class="variable language_">self</span>.minor, <span class="variable language_">self</span>.patch) &lt; (other.major, other.minor, other.patch)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 自动生成 __le__, __gt__, __ge__</span></span><br></pre></td></tr></table></figure><h3 id="9-2-operator-模块"><a href="#9-2-operator-模块" class="headerlink" title="9.2 operator 模块"></a>9.2 <code>operator</code> 模块</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><span class="line"><span class="keyword">from</span> operator <span class="keyword">import</span> itemgetter, attrgetter, methodcaller</span><br><span class="line"></span><br><span class="line">data = [</span><br><span class="line">    &#123;<span class="string">&quot;name&quot;</span>: <span class="string">&quot;Alice&quot;</span>, <span class="string">&quot;age&quot;</span>: <span class="number">30</span>&#125;,</span><br><span class="line">    &#123;<span class="string">&quot;name&quot;</span>: <span class="string">&quot;Bob&quot;</span>, <span class="string">&quot;age&quot;</span>: <span class="number">25</span>&#125;,</span><br><span class="line">    &#123;<span class="string">&quot;name&quot;</span>: <span class="string">&quot;Carol&quot;</span>, <span class="string">&quot;age&quot;</span>: <span class="number">35</span>&#125;,</span><br><span class="line">]</span><br><span class="line"></span><br><span class="line"><span class="comment"># itemgetter：按字典键排序/取值</span></span><br><span class="line">sorted_by_age = <span class="built_in">sorted</span>(data, key=itemgetter(<span class="string">&quot;age&quot;</span>))</span><br><span class="line">get_name = itemgetter(<span class="string">&quot;name&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">list</span>(<span class="built_in">map</span>(get_name, data)))   <span class="comment"># [&#x27;Alice&#x27;, &#x27;Bob&#x27;, &#x27;Carol&#x27;]</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># attrgetter：按对象属性操作</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Person</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, name, age</span>):</span><br><span class="line">        <span class="variable language_">self</span>.name = name</span><br><span class="line">        <span class="variable language_">self</span>.age = age</span><br><span class="line"></span><br><span class="line">people = [Person(<span class="string">&quot;Alice&quot;</span>, <span class="number">30</span>), Person(<span class="string">&quot;Bob&quot;</span>, <span class="number">25</span>)]</span><br><span class="line">sorted_people = <span class="built_in">sorted</span>(people, key=attrgetter(<span class="string">&quot;age&quot;</span>))</span><br><span class="line"></span><br><span class="line"><span class="comment"># methodcaller：调用对象方法</span></span><br><span class="line">upper = methodcaller(<span class="string">&quot;upper&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(upper(<span class="string">&quot;hello&quot;</span>))   <span class="comment"># HELLO</span></span><br></pre></td></tr></table></figure><h3 id="9-3-contextvars：异步上下文变量（3-7-）"><a href="#9-3-contextvars：异步上下文变量（3-7-）" class="headerlink" title="9.3 contextvars：异步上下文变量（3.7+）"></a>9.3 <code>contextvars</code>：异步上下文变量（3.7+）</h3><p>在异步编程中，<code>threading.local</code> 不适用（协程切换不换线程），<code>contextvars</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> asyncio</span><br><span class="line"><span class="keyword">from</span> contextvars <span class="keyword">import</span> ContextVar</span><br><span class="line"></span><br><span class="line"><span class="comment"># 定义上下文变量</span></span><br><span class="line">request_id = ContextVar(<span class="string">&quot;request_id&quot;</span>, default=<span class="string">&quot;unknown&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">handle_request</span>():</span><br><span class="line">    tid = request_id.get()</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;处理请求: <span class="subst">&#123;tid&#125;</span>&quot;</span>)</span><br><span class="line">    <span class="keyword">await</span> asyncio.sleep(<span class="number">0.1</span>)</span><br><span class="line">    <span class="comment"># 即使在异步切换后，上下文变量仍然保持正确</span></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;完成请求: <span class="subst">&#123;request_id.get()&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">main</span>():</span><br><span class="line">    <span class="comment"># 为不同协程设置不同的上下文</span></span><br><span class="line">    tasks = []</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">3</span>):</span><br><span class="line">        token = request_id.<span class="built_in">set</span>(<span class="string">f&quot;req_<span class="subst">&#123;i&#125;</span>&quot;</span>)</span><br><span class="line">        tasks.append(handle_request())</span><br><span class="line">        request_id.reset(token)   <span class="comment"># 恢复之前的值</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">await</span> asyncio.gather(*tasks)</span><br><span class="line"></span><br><span class="line"><span class="comment"># asyncio.run(main())</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 工程场景：在异步 Web 框架中传递请求 ID、用户信息、数据库会话</span></span><br><span class="line"><span class="comment"># FastAPI 内部就使用 contextvars 实现请求隔离</span></span><br></pre></td></tr></table></figure><h3 id="9-4-functools-singledispatch：单分派泛型函数"><a href="#9-4-functools-singledispatch：单分派泛型函数" class="headerlink" title="9.4 functools.singledispatch：单分派泛型函数"></a>9.4 <code>functools.singledispatch</code>：单分派泛型函数</h3><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><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><span class="line"><span class="keyword">from</span> functools <span class="keyword">import</span> singledispatch</span><br><span class="line"></span><br><span class="line"><span class="meta">@singledispatch</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">process</span>(<span class="params">value</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;处理不同类型数据的通用接口。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">raise</span> NotImplementedError(<span class="string">f&quot;不支持的类型: <span class="subst">&#123;<span class="built_in">type</span>(value)&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="meta">@process.register(<span class="params"><span class="built_in">int</span></span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">_</span>(<span class="params">value</span>):</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;处理整数: <span class="subst">&#123;value&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="meta">@process.register(<span class="params"><span class="built_in">str</span></span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">_</span>(<span class="params">value</span>):</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;处理字符串: <span class="subst">&#123;value&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="meta">@process.register(<span class="params"><span class="built_in">list</span></span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">_</span>(<span class="params">value</span>):</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;处理列表，长度: <span class="subst">&#123;<span class="built_in">len</span>(value)&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="meta">@process.register(<span class="params"><span class="built_in">dict</span></span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">_</span>(<span class="params">value</span>):</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;处理字典，键: <span class="subst">&#123;<span class="built_in">list</span>(value.keys())&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 注册自定义类型</span></span><br><span class="line"><span class="keyword">from</span> typing <span class="keyword">import</span> NamedTuple</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Point</span>(<span class="title class_ inherited__">NamedTuple</span>):</span><br><span class="line">    x: <span class="built_in">int</span></span><br><span class="line">    y: <span class="built_in">int</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@process.register(<span class="params">Point</span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">_</span>(<span class="params">value</span>):</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;处理 Point: (<span class="subst">&#123;value.x&#125;</span>, <span class="subst">&#123;value.y&#125;</span>)&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用</span></span><br><span class="line">process(<span class="number">42</span>)           <span class="comment"># 处理整数: 42</span></span><br><span class="line">process(<span class="string">&quot;hello&quot;</span>)      <span class="comment"># 处理字符串: hello</span></span><br><span class="line">process([<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>])    <span class="comment"># 处理列表，长度: 3</span></span><br><span class="line">process(&#123;<span class="string">&quot;a&quot;</span>: <span class="number">1</span>&#125;)     <span class="comment"># 处理字典，键: [&#x27;a&#x27;]</span></span><br><span class="line">process(Point(<span class="number">3</span>, <span class="number">4</span>))  <span class="comment"># 处理 Point: (3, 4)</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 工程场景：数据序列化、策略分发、多类型数据处理</span></span><br><span class="line"><span class="comment"># 优势：比 if/elif/elif 更优雅，添加新类型时不需要修改现有代码</span></span><br></pre></td></tr></table></figure><hr><h2 id="附录：第七阶段自检清单"><a href="#附录：第七阶段自检清单" class="headerlink" title="附录：第七阶段自检清单"></a>附录：第七阶段自检清单</h2><p>在继续学习第八阶段之前，请确保你能：</p><ul><li><input disabled="" type="checkbox"> 区分可迭代对象和迭代器，并能手写自定义迭代器</li><li><input disabled="" type="checkbox"> 解释 <code>__iter__</code> 和 <code>__next__</code> 的作用</li><li><input disabled="" type="checkbox"> 使用生成器的 <code>send()</code>、<code>throw()</code>、<code>close()</code> 方法</li><li><input disabled="" type="checkbox"> 手写带参数的装饰器、类装饰器，并理解装饰器叠加顺序</li><li><input disabled="" type="checkbox"> 使用 <code>functools.lru_cache</code> 进行函数结果缓存</li><li><input disabled="" type="checkbox"> 使用 <code>@contextmanager</code> 编写自定义上下文管理器</li><li><input disabled="" type="checkbox"> 区分 <code>__getattr__</code> 和 <code>__getattribute__</code>，并能正确使用</li><li><input disabled="" type="checkbox"> 理解元类的作用，能用 <code>__init_subclass__</code> 替代常见场景</li><li><input disabled="" type="checkbox"> 使用 <code>async</code>&#x2F;<code>await</code> 编写异步代码，理解事件循环</li><li><input disabled="" type="checkbox"> 使用 <code>typing</code> 模块进行类型注解（Protocol、TypedDict、泛型）</li><li><input disabled="" type="checkbox"> 使用 <code>functools.partial</code>、<code>reduce</code>、<code>cached_property</code> 等工具</li><li><input disabled="" type="checkbox"> 使用 <code>functools.singledispatch</code> 实现多类型分发</li><li><input disabled="" type="checkbox"> 使用 <code>contextvars</code> 在异步代码中传递上下文</li></ul><hr><h2 id="10-异步编程实战进阶"><a href="#10-异步编程实战进阶" class="headerlink" title="10. 异步编程实战进阶"></a>10. 异步编程实战进阶</h2><h3 id="10-1-异步-Web-服务器（aiohttp）"><a href="#10-1-异步-Web-服务器（aiohttp）" class="headerlink" title="10.1 异步 Web 服务器（aiohttp）"></a>10.1 异步 Web 服务器（aiohttp）</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> asyncio</span><br><span class="line"><span class="keyword">from</span> aiohttp <span class="keyword">import</span> web</span><br><span class="line"><span class="keyword">import</span> aiohttp</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">handle</span>(<span class="params">request</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;异步 HTTP 处理器。&quot;&quot;&quot;</span></span><br><span class="line">    user_id = request.match_info.get(<span class="string">&#x27;id&#x27;</span>, <span class="string">&#x27;anonymous&#x27;</span>)</span><br><span class="line">    <span class="comment"># 模拟异步数据库查询</span></span><br><span class="line">    <span class="keyword">await</span> asyncio.sleep(<span class="number">0.1</span>)</span><br><span class="line">    <span class="keyword">return</span> web.json_response(&#123;</span><br><span class="line">        <span class="string">&#x27;user_id&#x27;</span>: user_id,</span><br><span class="line">        <span class="string">&#x27;status&#x27;</span>: <span class="string">&#x27;active&#x27;</span></span><br><span class="line">    &#125;)</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">fetch_external_api</span>(<span class="params">request</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;异步调用外部 API。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">async</span> <span class="keyword">with</span> aiohttp.ClientSession() <span class="keyword">as</span> session:</span><br><span class="line">        <span class="keyword">async</span> <span class="keyword">with</span> session.get(<span class="string">&#x27;https://api.github.com/users/octocat&#x27;</span>) <span class="keyword">as</span> resp:</span><br><span class="line">            data = <span class="keyword">await</span> resp.json()</span><br><span class="line">            <span class="keyword">return</span> web.json_response(data)</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">background_task</span>(<span class="params">app</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;后台任务：定期清理缓存。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">        <span class="keyword">await</span> asyncio.sleep(<span class="number">60</span>)  <span class="comment"># 每分钟执行一次</span></span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;清理缓存...&quot;</span>)</span><br><span class="line"></span><br><span class="line">app = web.Application()</span><br><span class="line">app.router.add_get(<span class="string">&#x27;/users/&#123;id&#125;&#x27;</span>, handle)</span><br><span class="line">app.router.add_get(<span class="string">&#x27;/github&#x27;</span>, fetch_external_api)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 启动后台任务</span></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">on_startup</span>(<span class="params">app</span>):</span><br><span class="line">    app[<span class="string">&#x27;task&#x27;</span>] = asyncio.create_task(background_task(app))</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">on_cleanup</span>(<span class="params">app</span>):</span><br><span class="line">    app[<span class="string">&#x27;task&#x27;</span>].cancel()</span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        <span class="keyword">await</span> app[<span class="string">&#x27;task&#x27;</span>]</span><br><span class="line">    <span class="keyword">except</span> asyncio.CancelledError:</span><br><span class="line">        <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line">app.on_startup.append(on_startup)</span><br><span class="line">app.on_cleanup.append(on_cleanup)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 运行：python -m aiohttp.web -H localhost -P 8080 app:app</span></span><br></pre></td></tr></table></figure><h3 id="10-2-异步数据库连接池"><a href="#10-2-异步数据库连接池" class="headerlink" title="10.2 异步数据库连接池"></a>10.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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> asyncio</span><br><span class="line"><span class="keyword">import</span> asyncpg  <span class="comment"># PostgreSQL 异步驱动</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">AsyncDatabasePool</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;异步数据库连接池。&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, db_url, min_size=<span class="number">5</span>, max_size=<span class="number">10</span></span>):</span><br><span class="line">        <span class="variable language_">self</span>.db_url = db_url</span><br><span class="line">        <span class="variable language_">self</span>.min_size = min_size</span><br><span class="line">        <span class="variable language_">self</span>.max_size = max_size</span><br><span class="line">        <span class="variable language_">self</span>.pool = <span class="literal">None</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">connect</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;建立连接池。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="variable language_">self</span>.pool = <span class="keyword">await</span> asyncpg.create_pool(</span><br><span class="line">            <span class="variable language_">self</span>.db_url,</span><br><span class="line">            min_size=<span class="variable language_">self</span>.min_size,</span><br><span class="line">            max_size=<span class="variable language_">self</span>.max_size</span><br><span class="line">        )</span><br><span class="line"></span><br><span class="line">    <span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">close</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;关闭连接池。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">if</span> <span class="variable language_">self</span>.pool:</span><br><span class="line">            <span class="keyword">await</span> <span class="variable language_">self</span>.pool.close()</span><br><span class="line"></span><br><span class="line">    <span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">execute</span>(<span class="params">self, query, *args</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;执行查询。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">async</span> <span class="keyword">with</span> <span class="variable language_">self</span>.pool.acquire() <span class="keyword">as</span> conn:</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">await</span> conn.execute(query, *args)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">fetch</span>(<span class="params">self, query, *args</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;获取多行。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">async</span> <span class="keyword">with</span> <span class="variable language_">self</span>.pool.acquire() <span class="keyword">as</span> conn:</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">await</span> conn.fetch(query, *args)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">fetchrow</span>(<span class="params">self, query, *args</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;获取单行。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">async</span> <span class="keyword">with</span> <span class="variable language_">self</span>.pool.acquire() <span class="keyword">as</span> conn:</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">await</span> conn.fetchrow(query, *args)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用</span></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">main</span>():</span><br><span class="line">    db = AsyncDatabasePool(<span class="string">&quot;postgresql://user:pass@localhost/db&quot;</span>)</span><br><span class="line">    <span class="keyword">await</span> db.connect()</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 并发查询</span></span><br><span class="line">    users = <span class="keyword">await</span> db.fetch(<span class="string">&quot;SELECT * FROM users WHERE age &gt; $1&quot;</span>, <span class="number">18</span>)</span><br><span class="line">    <span class="keyword">for</span> user <span class="keyword">in</span> users:</span><br><span class="line">        <span class="built_in">print</span>(user[<span class="string">&#x27;name&#x27;</span>])</span><br><span class="line"></span><br><span class="line">    <span class="keyword">await</span> db.close()</span><br><span class="line"></span><br><span class="line"><span class="comment"># asyncio.run(main())</span></span><br></pre></td></tr></table></figure><h3 id="10-3-异步生产者-消费者模式"><a href="#10-3-异步生产者-消费者模式" class="headerlink" title="10.3 异步生产者-消费者模式"></a>10.3 异步生产者-消费者模式</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> asyncio</span><br><span class="line"><span class="keyword">from</span> collections <span class="keyword">import</span> deque</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">AsyncQueue</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;异步队列：支持生产者-消费者模式。&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, maxsize=<span class="number">10</span></span>):</span><br><span class="line">        <span class="variable language_">self</span>._queue = deque()</span><br><span class="line">        <span class="variable language_">self</span>._maxsize = maxsize</span><br><span class="line">        <span class="variable language_">self</span>._not_empty = asyncio.Event()</span><br><span class="line">        <span class="variable language_">self</span>._not_full = asyncio.Event()</span><br><span class="line">        <span class="variable language_">self</span>._not_full.<span class="built_in">set</span>()</span><br><span class="line"></span><br><span class="line">    <span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">put</span>(<span class="params">self, item</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;生产者：添加元素。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">await</span> <span class="variable language_">self</span>._not_full.wait()</span><br><span class="line">        <span class="variable language_">self</span>._queue.append(item)</span><br><span class="line">        <span class="variable language_">self</span>._not_empty.<span class="built_in">set</span>()</span><br><span class="line">        <span class="keyword">if</span> <span class="built_in">len</span>(<span class="variable language_">self</span>._queue) &gt;= <span class="variable language_">self</span>._maxsize:</span><br><span class="line">            <span class="variable language_">self</span>._not_full.clear()</span><br><span class="line"></span><br><span class="line">    <span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">get</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;消费者：获取元素。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">await</span> <span class="variable language_">self</span>._not_empty.wait()</span><br><span class="line">        item = <span class="variable language_">self</span>._queue.popleft()</span><br><span class="line">        <span class="variable language_">self</span>._not_full.<span class="built_in">set</span>()</span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> <span class="variable language_">self</span>._queue:</span><br><span class="line">            <span class="variable language_">self</span>._not_empty.clear()</span><br><span class="line">        <span class="keyword">return</span> item</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">qsize</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">len</span>(<span class="variable language_">self</span>._queue)</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">producer</span>(<span class="params">queue, producer_id</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;生产者协程。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">5</span>):</span><br><span class="line">        item = <span class="string">f&quot;商品-<span class="subst">&#123;producer_id&#125;</span>-<span class="subst">&#123;i&#125;</span>&quot;</span></span><br><span class="line">        <span class="keyword">await</span> queue.put(item)</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;[生产者 <span class="subst">&#123;producer_id&#125;</span>] 生产: <span class="subst">&#123;item&#125;</span>, 队列大小: <span class="subst">&#123;queue.qsize()&#125;</span>&quot;</span>)</span><br><span class="line">        <span class="keyword">await</span> asyncio.sleep(<span class="number">0.5</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">consumer</span>(<span class="params">queue, consumer_id</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;消费者协程。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">        item = <span class="keyword">await</span> queue.get()</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;[消费者 <span class="subst">&#123;consumer_id&#125;</span>] 消费: <span class="subst">&#123;item&#125;</span>, 队列大小: <span class="subst">&#123;queue.qsize()&#125;</span>&quot;</span>)</span><br><span class="line">        <span class="keyword">await</span> asyncio.sleep(<span class="number">1</span>)  <span class="comment"># 模拟处理时间</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">main</span>():</span><br><span class="line">    queue = AsyncQueue(maxsize=<span class="number">5</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 启动 2 个生产者和 3 个消费者</span></span><br><span class="line">    producers = [asyncio.create_task(producer(queue, i)) <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">2</span>)]</span><br><span class="line">    consumers = [asyncio.create_task(consumer(queue, i)) <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">3</span>)]</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 等待所有生产者完成</span></span><br><span class="line">    <span class="keyword">await</span> asyncio.gather(*producers)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 取消消费者（实际应用中应有更优雅的退出机制）</span></span><br><span class="line">    <span class="keyword">for</span> c <span class="keyword">in</span> consumers:</span><br><span class="line">        c.cancel()</span><br><span class="line"></span><br><span class="line"><span class="comment"># asyncio.run(main())</span></span><br></pre></td></tr></table></figure><p><strong>生产者-消费者流程图</strong>：</p><figure class="highlight plaintext"><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><span class="line">┌─────────────────────────────────────────────────────────┐</span><br><span class="line">│              异步生产者-消费者模型                         │</span><br><span class="line">├─────────────────────────────────────────────────────────┤</span><br><span class="line">│                                                         │</span><br><span class="line">│  生产者协程                                              │</span><br><span class="line">│    │                                                    │</span><br><span class="line">│    ├── asyncio.create_task(producer(queue, id))          │</span><br><span class="line">│    │    └── await queue.put(item)                        │</span><br><span class="line">│    │        └── 队列未满：添加元素，通知消费者             │</span><br><span class="line">│    │        └── 队列已满：等待 not_full 事件              │</span><br><span class="line">│    │                                                    │</span><br><span class="line">│  异步队列 (AsyncQueue)                                   │</span><br><span class="line">│    │                                                    │</span><br><span class="line">│    ├── put(): 生产者添加元素                             │</span><br><span class="line">│    ├── get(): 消费者获取元素                             │</span><br><span class="line">│    ├── _not_empty: 有元素时通知消费者                    │</span><br><span class="line">│    └── _not_full: 有空间时通知生产者                     │</span><br><span class="line">│                                                         │</span><br><span class="line">│  消费者协程                                              │</span><br><span class="line">│    │                                                    │</span><br><span class="line">│    ├── asyncio.create_task(consumer(queue, id))          │</span><br><span class="line">│    │    └── item = await queue.get()                     │</span><br><span class="line">│    │        └── 队列非空：获取元素，通知生产者             │</span><br><span class="line">│    │        └── 队列为空：等待 not_empty 事件             │</span><br><span class="line">│                                                         │</span><br><span class="line">│  优势：                                                  │</span><br><span class="line">│    - 生产者不会阻塞消费者                                 │</span><br><span class="line">│    - 背压控制：队列满时生产者等待                         │</span><br><span class="line">│    - 协程切换开销远小于线程                               │</span><br><span class="line">│                                                         │</span><br><span class="line">└─────────────────────────────────────────────────────────┘</span><br></pre></td></tr></table></figure><h3 id="10-4-异步超时与重试"><a href="#10-4-异步超时与重试" class="headerlink" title="10.4 异步超时与重试"></a>10.4 异步超时与重试</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> asyncio</span><br><span class="line"><span class="keyword">import</span> functools</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">async_retry</span>(<span class="params">max_attempts=<span class="number">3</span>, delay=<span class="number">1</span>, exceptions=(<span class="params">Exception,</span>)</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;异步重试装饰器。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">decorator</span>(<span class="params">func</span>):</span><br><span class="line"><span class="meta">        @functools.wraps(<span class="params">func</span>)</span></span><br><span class="line">        <span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">wrapper</span>(<span class="params">*args, **kwargs</span>):</span><br><span class="line">            last_exception = <span class="literal">None</span></span><br><span class="line">            <span class="keyword">for</span> attempt <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">1</span>, max_attempts + <span class="number">1</span>):</span><br><span class="line">                <span class="keyword">try</span>:</span><br><span class="line">                    <span class="keyword">return</span> <span class="keyword">await</span> func(*args, **kwargs)</span><br><span class="line">                <span class="keyword">except</span> exceptions <span class="keyword">as</span> e:</span><br><span class="line">                    last_exception = e</span><br><span class="line">                    <span class="keyword">if</span> attempt &lt; max_attempts:</span><br><span class="line">                        <span class="keyword">await</span> asyncio.sleep(delay)</span><br><span class="line">                        <span class="built_in">print</span>(<span class="string">f&quot;重试 <span class="subst">&#123;attempt&#125;</span>/<span class="subst">&#123;max_attempts&#125;</span>: <span class="subst">&#123;e&#125;</span>&quot;</span>)</span><br><span class="line">            <span class="keyword">raise</span> last_exception</span><br><span class="line">        <span class="keyword">return</span> wrapper</span><br><span class="line">    <span class="keyword">return</span> decorator</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">fetch_with_timeout</span>(<span class="params">url, timeout=<span class="number">5</span></span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;带超时的异步请求。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        <span class="keyword">async</span> <span class="keyword">with</span> asyncio.timeout(timeout):</span><br><span class="line">            <span class="keyword">async</span> <span class="keyword">with</span> aiohttp.ClientSession() <span class="keyword">as</span> session:</span><br><span class="line">                <span class="keyword">async</span> <span class="keyword">with</span> session.get(url) <span class="keyword">as</span> resp:</span><br><span class="line">                    <span class="keyword">return</span> <span class="keyword">await</span> resp.json()</span><br><span class="line">    <span class="keyword">except</span> asyncio.TimeoutError:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;请求超时: <span class="subst">&#123;url&#125;</span>&quot;</span>)</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">None</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@async_retry(<span class="params">max_attempts=<span class="number">3</span>, delay=<span class="number">1</span>, exceptions=(<span class="params">ConnectionError,</span>)</span>)</span></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">fetch_unstable_data</span>(<span class="params">url</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;模拟不稳定的异步请求。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">import</span> random</span><br><span class="line">    <span class="keyword">if</span> random.random() &lt; <span class="number">0.7</span>:</span><br><span class="line">        <span class="keyword">raise</span> ConnectionError(<span class="string">f&quot;请求失败: <span class="subst">&#123;url&#125;</span>&quot;</span>)</span><br><span class="line">    <span class="keyword">return</span> &#123;<span class="string">&quot;status&quot;</span>: <span class="string">&quot;ok&quot;</span>, <span class="string">&quot;data&quot;</span>: []&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">main</span>():</span><br><span class="line">    <span class="comment"># 超时控制</span></span><br><span class="line">    data = <span class="keyword">await</span> fetch_with_timeout(<span class="string">&quot;https://api.github.com&quot;</span>, timeout=<span class="number">3</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 重试机制</span></span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">5</span>):</span><br><span class="line">        <span class="keyword">try</span>:</span><br><span class="line">            result = <span class="keyword">await</span> fetch_unstable_data(<span class="string">f&quot;https://api.example.com/<span class="subst">&#123;i&#125;</span>&quot;</span>)</span><br><span class="line">            <span class="built_in">print</span>(result)</span><br><span class="line">            <span class="keyword">break</span></span><br><span class="line">        <span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">f&quot;最终失败: <span class="subst">&#123;e&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># asyncio.run(main())</span></span><br></pre></td></tr></table></figure><hr><h2 id="11-工作实战场景"><a href="#11-工作实战场景" class="headerlink" title="11. 工作实战场景"></a>11. 工作实战场景</h2><h3 id="11-1-场景一：异步爬虫框架"><a href="#11-1-场景一：异步爬虫框架" class="headerlink" title="11.1 场景一：异步爬虫框架"></a>11.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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> asyncio</span><br><span class="line"><span class="keyword">import</span> aiohttp</span><br><span class="line"><span class="keyword">from</span> urllib.parse <span class="keyword">import</span> urljoin, urlparse</span><br><span class="line"><span class="keyword">from</span> collections <span class="keyword">import</span> deque</span><br><span class="line"><span class="keyword">import</span> re</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">AsyncCrawler</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;异步网页爬虫。&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, max_concurrent=<span class="number">10</span>, max_depth=<span class="number">3</span></span>):</span><br><span class="line">        <span class="variable language_">self</span>.max_concurrent = max_concurrent</span><br><span class="line">        <span class="variable language_">self</span>.max_depth = max_depth</span><br><span class="line">        <span class="variable language_">self</span>.visited = <span class="built_in">set</span>()</span><br><span class="line">        <span class="variable language_">self</span>.results = []</span><br><span class="line">        <span class="variable language_">self</span>.semaphore = asyncio.Semaphore(max_concurrent)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">fetch</span>(<span class="params">self, session, url</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;带并发限制的异步请求。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">async</span> <span class="keyword">with</span> <span class="variable language_">self</span>.semaphore:</span><br><span class="line">            <span class="keyword">try</span>:</span><br><span class="line">                <span class="keyword">async</span> <span class="keyword">with</span> session.get(url, timeout=aiohttp.ClientTimeout(total=<span class="number">10</span>)) <span class="keyword">as</span> resp:</span><br><span class="line">                    <span class="keyword">if</span> resp.status == <span class="number">200</span>:</span><br><span class="line">                        <span class="keyword">return</span> <span class="keyword">await</span> resp.text()</span><br><span class="line">            <span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">                <span class="built_in">print</span>(<span class="string">f&quot;抓取失败: <span class="subst">&#123;url&#125;</span>, 错误: <span class="subst">&#123;e&#125;</span>&quot;</span>)</span><br><span class="line">                <span class="keyword">return</span> <span class="literal">None</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">extract_links</span>(<span class="params">self, html, base_url</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;提取页面中的链接。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> html:</span><br><span class="line">            <span class="keyword">return</span> []</span><br><span class="line">        link_pattern = re.<span class="built_in">compile</span>(<span class="string">r&#x27;href=[&quot;\&#x27;](https?://[^&quot;\&#x27;]+)[&quot;\&#x27;]&#x27;</span>)</span><br><span class="line">        links = link_pattern.findall(html)</span><br><span class="line">        <span class="comment"># 过滤同域名链接</span></span><br><span class="line">        base_domain = urlparse(base_url).netloc</span><br><span class="line">        <span class="keyword">return</span> [link <span class="keyword">for</span> link <span class="keyword">in</span> links <span class="keyword">if</span> urlparse(link).netloc == base_domain]</span><br><span class="line"></span><br><span class="line">    <span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">crawl_page</span>(<span class="params">self, session, url, depth</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;爬取单个页面。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">if</span> depth &gt; <span class="variable language_">self</span>.max_depth <span class="keyword">or</span> url <span class="keyword">in</span> <span class="variable language_">self</span>.visited:</span><br><span class="line">            <span class="keyword">return</span></span><br><span class="line"></span><br><span class="line">        <span class="variable language_">self</span>.visited.add(url)</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;[深度 <span class="subst">&#123;depth&#125;</span>] 爬取: <span class="subst">&#123;url&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line">        html = <span class="keyword">await</span> <span class="variable language_">self</span>.fetch(session, url)</span><br><span class="line">        <span class="keyword">if</span> html:</span><br><span class="line">            <span class="variable language_">self</span>.results.append(&#123;</span><br><span class="line">                <span class="string">&#x27;url&#x27;</span>: url,</span><br><span class="line">                <span class="string">&#x27;depth&#x27;</span>: depth,</span><br><span class="line">                <span class="string">&#x27;size&#x27;</span>: <span class="built_in">len</span>(html)</span><br><span class="line">            &#125;)</span><br><span class="line"></span><br><span class="line">            <span class="comment"># 提取并递归爬取链接</span></span><br><span class="line">            links = <span class="variable language_">self</span>.extract_links(html, url)</span><br><span class="line">            tasks = []</span><br><span class="line">            <span class="keyword">for</span> link <span class="keyword">in</span> links[:<span class="number">10</span>]:  <span class="comment"># 限制每页爬取的链接数</span></span><br><span class="line">                <span class="keyword">if</span> link <span class="keyword">not</span> <span class="keyword">in</span> <span class="variable language_">self</span>.visited:</span><br><span class="line">                    tasks.append(<span class="variable language_">self</span>.crawl_page(session, link, depth + <span class="number">1</span>))</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> tasks:</span><br><span class="line">                <span class="keyword">await</span> asyncio.gather(*tasks, return_exceptions=<span class="literal">True</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">run</span>(<span class="params">self, start_url</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;启动爬虫。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">async</span> <span class="keyword">with</span> aiohttp.ClientSession() <span class="keyword">as</span> session:</span><br><span class="line">            <span class="keyword">await</span> <span class="variable language_">self</span>.crawl_page(session, start_url, <span class="number">1</span>)</span><br><span class="line"></span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;\n爬取完成！共访问 <span class="subst">&#123;<span class="built_in">len</span>(self.visited)&#125;</span> 个页面&quot;</span>)</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>.results</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用</span></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">main</span>():</span><br><span class="line">    crawler = AsyncCrawler(max_concurrent=<span class="number">5</span>, max_depth=<span class="number">2</span>)</span><br><span class="line">    results = <span class="keyword">await</span> crawler.run(<span class="string">&quot;https://example.com&quot;</span>)</span><br><span class="line">    <span class="keyword">for</span> r <span class="keyword">in</span> results[:<span class="number">10</span>]:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;URL: <span class="subst">&#123;r[<span class="string">&#x27;url&#x27;</span>]&#125;</span>, 大小: <span class="subst">&#123;r[<span class="string">&#x27;size&#x27;</span>]&#125;</span> 字节&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># asyncio.run(main())</span></span><br></pre></td></tr></table></figure><h3 id="11-2-场景二：异步任务调度器"><a href="#11-2-场景二：异步任务调度器" class="headerlink" title="11.2 场景二：异步任务调度器"></a>11.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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> asyncio</span><br><span class="line"><span class="keyword">from</span> datetime <span class="keyword">import</span> datetime</span><br><span class="line"><span class="keyword">from</span> typing <span class="keyword">import</span> <span class="type">Callable</span>, <span class="type">Dict</span>, <span class="type">List</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">AsyncTaskScheduler</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;异步任务调度器：支持定时任务、依赖管理。&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="variable language_">self</span>.tasks: <span class="type">Dict</span>[<span class="built_in">str</span>, <span class="type">Callable</span>] = &#123;&#125;</span><br><span class="line">        <span class="variable language_">self</span>.dependencies: <span class="type">Dict</span>[<span class="built_in">str</span>, <span class="type">List</span>[<span class="built_in">str</span>]] = &#123;&#125;</span><br><span class="line">        <span class="variable language_">self</span>.schedule: <span class="type">Dict</span>[<span class="built_in">str</span>, <span class="built_in">float</span>] = &#123;&#125;  <span class="comment"># task_name: interval_seconds</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">register</span>(<span class="params">self, name: <span class="built_in">str</span>, interval: <span class="built_in">float</span> = <span class="literal">None</span>, depends_on: <span class="type">List</span>[<span class="built_in">str</span>] = <span class="literal">None</span></span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;注册异步任务。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">def</span> <span class="title function_">decorator</span>(<span class="params">func</span>):</span><br><span class="line">            <span class="variable language_">self</span>.tasks[name] = func</span><br><span class="line">            <span class="keyword">if</span> interval:</span><br><span class="line">                <span class="variable language_">self</span>.schedule[name] = interval</span><br><span class="line">            <span class="keyword">if</span> depends_on:</span><br><span class="line">                <span class="variable language_">self</span>.dependencies[name] = depends_on</span><br><span class="line">            <span class="keyword">return</span> func</span><br><span class="line">        <span class="keyword">return</span> decorator</span><br><span class="line"></span><br><span class="line">    <span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">_run_with_dependencies</span>(<span class="params">self, task_name: <span class="built_in">str</span></span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;按依赖顺序执行任务。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="comment"># 先执行依赖的任务</span></span><br><span class="line">        <span class="keyword">if</span> task_name <span class="keyword">in</span> <span class="variable language_">self</span>.dependencies:</span><br><span class="line">            <span class="keyword">for</span> dep <span class="keyword">in</span> <span class="variable language_">self</span>.dependencies[task_name]:</span><br><span class="line">                <span class="keyword">await</span> <span class="variable language_">self</span>._run_with_dependencies(dep)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 执行当前任务</span></span><br><span class="line">        <span class="keyword">if</span> task_name <span class="keyword">in</span> <span class="variable language_">self</span>.tasks:</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">f&quot;[<span class="subst">&#123;datetime.now()&#125;</span>] 执行任务: <span class="subst">&#123;task_name&#125;</span>&quot;</span>)</span><br><span class="line">            <span class="keyword">await</span> <span class="variable language_">self</span>.tasks[task_name]()</span><br><span class="line"></span><br><span class="line">    <span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">run_once</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;执行所有任务一次（按依赖顺序）。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="comment"># 拓扑排序</span></span><br><span class="line">        executed = <span class="built_in">set</span>()</span><br><span class="line"></span><br><span class="line">        <span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">execute</span>(<span class="params">task_name</span>):</span><br><span class="line">            <span class="keyword">if</span> task_name <span class="keyword">in</span> executed:</span><br><span class="line">                <span class="keyword">return</span></span><br><span class="line">            <span class="keyword">if</span> task_name <span class="keyword">in</span> <span class="variable language_">self</span>.dependencies:</span><br><span class="line">                <span class="keyword">for</span> dep <span class="keyword">in</span> <span class="variable language_">self</span>.dependencies[task_name]:</span><br><span class="line">                    <span class="keyword">await</span> execute(dep)</span><br><span class="line">            <span class="keyword">await</span> <span class="variable language_">self</span>._run_with_dependencies(task_name)</span><br><span class="line">            executed.add(task_name)</span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span> task_name <span class="keyword">in</span> <span class="variable language_">self</span>.tasks:</span><br><span class="line">            <span class="keyword">await</span> execute(task_name)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">run_scheduled</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;运行定时任务。&quot;&quot;&quot;</span></span><br><span class="line">        jobs = []</span><br><span class="line">        <span class="keyword">for</span> task_name, interval <span class="keyword">in</span> <span class="variable language_">self</span>.schedule.items():</span><br><span class="line">            <span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">periodic_task</span>():</span><br><span class="line">                <span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">                    <span class="keyword">await</span> <span class="variable language_">self</span>._run_with_dependencies(task_name)</span><br><span class="line">                    <span class="keyword">await</span> asyncio.sleep(interval)</span><br><span class="line">            jobs.append(periodic_task())</span><br><span class="line"></span><br><span class="line">        <span class="keyword">await</span> asyncio.gather(*jobs)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用示例</span></span><br><span class="line">scheduler = AsyncTaskScheduler()</span><br><span class="line"></span><br><span class="line"><span class="meta">@scheduler.register(<span class="params">name=<span class="string">&quot;fetch_data&quot;</span>, interval=<span class="number">5</span></span>)</span></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">fetch_data</span>():</span><br><span class="line">    <span class="string">&quot;&quot;&quot;每 5 秒获取数据。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;获取数据...&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="meta">@scheduler.register(<span class="params">name=<span class="string">&quot;process_data&quot;</span>, depends_on=[<span class="string">&quot;fetch_data&quot;</span>]</span>)</span></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">process_data</span>():</span><br><span class="line">    <span class="string">&quot;&quot;&quot;处理数据（依赖 fetch_data）。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;处理数据...&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="meta">@scheduler.register(<span class="params">name=<span class="string">&quot;save_data&quot;</span>, depends_on=[<span class="string">&quot;process_data&quot;</span>], interval=<span class="number">10</span></span>)</span></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">save_data</span>():</span><br><span class="line">    <span class="string">&quot;&quot;&quot;每 10 秒保存数据。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;保存数据...&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">main</span>():</span><br><span class="line">    <span class="comment"># 执行一次</span></span><br><span class="line">    <span class="keyword">await</span> scheduler.run_once()</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 启动定时任务</span></span><br><span class="line">    <span class="comment"># await scheduler.run_scheduled()</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># asyncio.run(main())</span></span><br></pre></td></tr></table></figure><h3 id="11-3-场景三：装饰器实现缓存与权限控制"><a href="#11-3-场景三：装饰器实现缓存与权限控制" class="headerlink" title="11.3 场景三：装饰器实现缓存与权限控制"></a>11.3 场景三：装饰器实现缓存与权限控制</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> functools</span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"><span class="keyword">from</span> typing <span class="keyword">import</span> <span class="type">Callable</span>, <span class="type">Any</span></span><br><span class="line"><span class="keyword">import</span> hashlib</span><br><span class="line"><span class="keyword">import</span> json</span><br><span class="line"></span><br><span class="line"><span class="comment"># 1. 异步缓存装饰器</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">async_cache</span>(<span class="params">expire_seconds=<span class="number">60</span></span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;异步函数缓存装饰器。&quot;&quot;&quot;</span></span><br><span class="line">    cache = &#123;&#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">decorator</span>(<span class="params">func</span>):</span><br><span class="line"><span class="meta">        @functools.wraps(<span class="params">func</span>)</span></span><br><span class="line">        <span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">wrapper</span>(<span class="params">*args, **kwargs</span>):</span><br><span class="line">            <span class="comment"># 生成缓存键</span></span><br><span class="line">            key_parts = (func.__name__, args, <span class="built_in">tuple</span>(<span class="built_in">sorted</span>(kwargs.items())))</span><br><span class="line">            cache_key = hashlib.md5(<span class="built_in">str</span>(key_parts).encode()).hexdigest()</span><br><span class="line"></span><br><span class="line">            <span class="comment"># 检查缓存</span></span><br><span class="line">            <span class="keyword">if</span> cache_key <span class="keyword">in</span> cache:</span><br><span class="line">                data, timestamp = cache[cache_key]</span><br><span class="line">                <span class="keyword">if</span> time.time() - timestamp &lt; expire_seconds:</span><br><span class="line">                    <span class="built_in">print</span>(<span class="string">f&quot;[缓存命中] <span class="subst">&#123;func.__name__&#125;</span>&quot;</span>)</span><br><span class="line">                    <span class="keyword">return</span> data</span><br><span class="line"></span><br><span class="line">            <span class="comment"># 执行函数</span></span><br><span class="line">            result = <span class="keyword">await</span> func(*args, **kwargs)</span><br><span class="line">            cache[cache_key] = (result, time.time())</span><br><span class="line">            <span class="keyword">return</span> result</span><br><span class="line"></span><br><span class="line">        wrapper.cache = cache</span><br><span class="line">        <span class="keyword">return</span> wrapper</span><br><span class="line">    <span class="keyword">return</span> decorator</span><br><span class="line"></span><br><span class="line"><span class="comment"># 2. 权限检查装饰器</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">require_permission</span>(<span class="params">permission: <span class="built_in">str</span></span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;权限检查装饰器。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">decorator</span>(<span class="params">func</span>):</span><br><span class="line"><span class="meta">        @functools.wraps(<span class="params">func</span>)</span></span><br><span class="line">        <span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">wrapper</span>(<span class="params">user, *args, **kwargs</span>):</span><br><span class="line">            <span class="keyword">if</span> permission <span class="keyword">not</span> <span class="keyword">in</span> user.get(<span class="string">&#x27;permissions&#x27;</span>, []):</span><br><span class="line">                <span class="keyword">raise</span> PermissionError(<span class="string">f&quot;用户缺少权限: <span class="subst">&#123;permission&#125;</span>&quot;</span>)</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">await</span> func(user, *args, **kwargs)</span><br><span class="line">        <span class="keyword">return</span> wrapper</span><br><span class="line">    <span class="keyword">return</span> decorator</span><br><span class="line"></span><br><span class="line"><span class="comment"># 3. 日志记录装饰器</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">log_execution</span>(<span class="params">logger=<span class="literal">None</span></span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;执行日志装饰器。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">decorator</span>(<span class="params">func</span>):</span><br><span class="line"><span class="meta">        @functools.wraps(<span class="params">func</span>)</span></span><br><span class="line">        <span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">wrapper</span>(<span class="params">*args, **kwargs</span>):</span><br><span class="line">            start_time = time.time()</span><br><span class="line">            func_name = func.__name__</span><br><span class="line"></span><br><span class="line">            <span class="built_in">print</span>(<span class="string">f&quot;[开始] <span class="subst">&#123;func_name&#125;</span>, 参数: args=<span class="subst">&#123;args&#125;</span>, kwargs=<span class="subst">&#123;kwargs&#125;</span>&quot;</span>)</span><br><span class="line">            <span class="keyword">try</span>:</span><br><span class="line">                result = <span class="keyword">await</span> func(*args, **kwargs)</span><br><span class="line">                elapsed = time.time() - start_time</span><br><span class="line">                <span class="built_in">print</span>(<span class="string">f&quot;[完成] <span class="subst">&#123;func_name&#125;</span>, 耗时: <span class="subst">&#123;elapsed:<span class="number">.3</span>f&#125;</span>s&quot;</span>)</span><br><span class="line">                <span class="keyword">return</span> result</span><br><span class="line">            <span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">                elapsed = time.time() - start_time</span><br><span class="line">                <span class="built_in">print</span>(<span class="string">f&quot;[错误] <span class="subst">&#123;func_name&#125;</span>, 耗时: <span class="subst">&#123;elapsed:<span class="number">.3</span>f&#125;</span>s, 错误: <span class="subst">&#123;e&#125;</span>&quot;</span>)</span><br><span class="line">                <span class="keyword">raise</span></span><br><span class="line">        <span class="keyword">return</span> wrapper</span><br><span class="line">    <span class="keyword">return</span> decorator</span><br><span class="line"></span><br><span class="line"><span class="comment"># 综合应用</span></span><br><span class="line"><span class="meta">@log_execution()</span></span><br><span class="line"><span class="meta">@require_permission(<span class="params"><span class="string">&#x27;admin&#x27;</span></span>)</span></span><br><span class="line"><span class="meta">@async_cache(<span class="params">expire_seconds=<span class="number">30</span></span>)</span></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">get_user_stats</span>(<span class="params">user, user_id</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;获取用户统计信息（需要 admin 权限，缓存 30 秒）。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">await</span> asyncio.sleep(<span class="number">1</span>)  <span class="comment"># 模拟数据库查询</span></span><br><span class="line">    <span class="keyword">return</span> &#123;</span><br><span class="line">        <span class="string">&#x27;user_id&#x27;</span>: user_id,</span><br><span class="line">        <span class="string">&#x27;posts&#x27;</span>: <span class="number">42</span>,</span><br><span class="line">        <span class="string">&#x27;followers&#x27;</span>: <span class="number">128</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">main</span>():</span><br><span class="line">    admin_user = &#123;<span class="string">&#x27;id&#x27;</span>: <span class="number">1</span>, <span class="string">&#x27;name&#x27;</span>: <span class="string">&#x27;Alice&#x27;</span>, <span class="string">&#x27;permissions&#x27;</span>: [<span class="string">&#x27;admin&#x27;</span>, <span class="string">&#x27;write&#x27;</span>]&#125;</span><br><span class="line">    normal_user = &#123;<span class="string">&#x27;id&#x27;</span>: <span class="number">2</span>, <span class="string">&#x27;name&#x27;</span>: <span class="string">&#x27;Bob&#x27;</span>, <span class="string">&#x27;permissions&#x27;</span>: [<span class="string">&#x27;read&#x27;</span>]&#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 有权限的用户</span></span><br><span class="line">    stats1 = <span class="keyword">await</span> get_user_stats(admin_user, <span class="number">1</span>)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;统计信息: <span class="subst">&#123;stats1&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 缓存命中</span></span><br><span class="line">    stats2 = <span class="keyword">await</span> get_user_stats(admin_user, <span class="number">1</span>)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;统计信息（缓存）: <span class="subst">&#123;stats2&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 无权限用户</span></span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        stats3 = <span class="keyword">await</span> get_user_stats(normal_user, <span class="number">1</span>)</span><br><span class="line">    <span class="keyword">except</span> PermissionError <span class="keyword">as</span> e:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;权限错误: <span class="subst">&#123;e&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># asyncio.run(main())</span></span><br></pre></td></tr></table></figure><hr><h2 id="12-常见面试题汇总"><a href="#12-常见面试题汇总" class="headerlink" title="12. 常见面试题汇总"></a>12. 常见面试题汇总</h2><h3 id="12-1-生成器与迭代器"><a href="#12-1-生成器与迭代器" class="headerlink" title="12.1 生成器与迭代器"></a>12.1 生成器与迭代器</h3><p><strong>Q1: 解释 <code>yield</code> 和 <code>yield from</code> 的区别。</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></pre></td><td class="code"><pre><span class="line"><span class="comment"># yield: 暂停函数并返回值</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">simple_gen</span>():</span><br><span class="line">    <span class="keyword">yield</span> <span class="number">1</span></span><br><span class="line">    <span class="keyword">yield</span> <span class="number">2</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># yield from: 委托给另一个生成器（Python 3.3+）</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">sub_gen</span>():</span><br><span class="line">    <span class="keyword">yield</span> <span class="number">1</span></span><br><span class="line">    <span class="keyword">yield</span> <span class="number">2</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">main_gen</span>():</span><br><span class="line">    <span class="keyword">yield</span> <span class="keyword">from</span> sub_gen()  <span class="comment"># 透传子生成器的所有值</span></span><br><span class="line">    <span class="keyword">yield</span> <span class="number">3</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 对比</span></span><br><span class="line"><span class="built_in">list</span>(simple_gen())   <span class="comment"># [1, 2]</span></span><br><span class="line"><span class="built_in">list</span>(main_gen())     <span class="comment"># [1, 2, 3]</span></span><br></pre></td></tr></table></figure><p><strong>关键点</strong>：</p><ul><li><code>yield</code> 直接返回值</li><li><code>yield from</code> 建立生成器之间的双向通道，支持 <code>send()</code>、<code>throw()</code>、<code>close()</code> 的透传</li><li>用于重构嵌套生成器，避免手动 <code>for</code> 循环</li></ul><hr><p><strong>Q2: 如何用生成器实现无限序列？</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><span class="line"><span class="keyword">def</span> <span class="title function_">fibonacci</span>():</span><br><span class="line">    <span class="string">&quot;&quot;&quot;无限斐波那契数列。&quot;&quot;&quot;</span></span><br><span class="line">    a, b = <span class="number">0</span>, <span class="number">1</span></span><br><span class="line">    <span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">        <span class="keyword">yield</span> a</span><br><span class="line">        a, b = b, a + b</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用 itertools.islice 取前 N 个</span></span><br><span class="line"><span class="keyword">from</span> itertools <span class="keyword">import</span> islice</span><br><span class="line">fib = fibonacci()</span><br><span class="line">first_10 = <span class="built_in">list</span>(islice(fib, <span class="number">10</span>))</span><br><span class="line"><span class="built_in">print</span>(first_10)  <span class="comment"># [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 优势：内存占用恒定，无论取多少个</span></span><br></pre></td></tr></table></figure><hr><p><strong>Q3: 迭代器和可迭代对象的区别？</strong></p><table><thead><tr><th>特性</th><th>可迭代对象 (Iterable)</th><th>迭代器 (Iterator)</th></tr></thead><tbody><tr><td>协议</td><td>实现 <code>__iter__()</code></td><td>实现 <code>__iter__()</code> + <code>__next__()</code></td></tr><tr><td>示例</td><td><code>list</code>, <code>str</code>, <code>dict</code></td><td><code>iter([1,2,3])</code> 的返回值</td></tr><tr><td>遍历次数</td><td>可多次遍历</td><td>只能遍历一次</td></tr><tr><td>内存</td><td>存储所有元素</td><td>惰性计算，不存储</td></tr></tbody></table><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><span class="line"><span class="comment"># 验证</span></span><br><span class="line"><span class="keyword">from</span> collections.abc <span class="keyword">import</span> Iterable, Iterator</span><br><span class="line"></span><br><span class="line">lst = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]</span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">isinstance</span>(lst, Iterable))    <span class="comment"># True</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">isinstance</span>(lst, Iterator))     <span class="comment"># False</span></span><br><span class="line"></span><br><span class="line">it = <span class="built_in">iter</span>(lst)</span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">isinstance</span>(it, Iterator))      <span class="comment"># True</span></span><br></pre></td></tr></table></figure><hr><h3 id="12-2-装饰器"><a href="#12-2-装饰器" class="headerlink" title="12.2 装饰器"></a>12.2 装饰器</h3><p><strong>Q4: 编写一个可以统计函数执行次数的装饰器。</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><span class="line"><span class="keyword">import</span> functools</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">count_calls</span>(<span class="params">func</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;统计函数调用次数。&quot;&quot;&quot;</span></span><br><span class="line"><span class="meta">    @functools.wraps(<span class="params">func</span>)</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">wrapper</span>(<span class="params">*args, **kwargs</span>):</span><br><span class="line">        wrapper.call_count += <span class="number">1</span></span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;调用 <span class="subst">&#123;func.__name__&#125;</span> 第 <span class="subst">&#123;wrapper.call_count&#125;</span> 次&quot;</span>)</span><br><span class="line">        <span class="keyword">return</span> func(*args, **kwargs)</span><br><span class="line"></span><br><span class="line">    wrapper.call_count = <span class="number">0</span></span><br><span class="line">    <span class="keyword">return</span> wrapper</span><br><span class="line"></span><br><span class="line"><span class="meta">@count_calls</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">say_hello</span>(<span class="params">name</span>):</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;Hello, <span class="subst">&#123;name&#125;</span>!&quot;</span>)</span><br><span class="line"></span><br><span class="line">say_hello(<span class="string">&quot;Alice&quot;</span>)  <span class="comment"># 调用 say_hello 第 1 次</span></span><br><span class="line">say_hello(<span class="string">&quot;Bob&quot;</span>)    <span class="comment"># 调用 say_hello 第 2 次</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;总调用次数: <span class="subst">&#123;say_hello.call_count&#125;</span>&quot;</span>)  <span class="comment"># 总调用次数: 2</span></span><br></pre></td></tr></table></figure><hr><p><strong>Q5: 装饰器的叠加顺序是什么？</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@decorator_a</span></span><br><span class="line"><span class="meta">@decorator_b</span></span><br><span class="line"><span class="meta">@decorator_c</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">my_func</span>():</span><br><span class="line">    <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 等价于：</span></span><br><span class="line">my_func = decorator_a(decorator_b(decorator_c(my_func)))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 定义阶段：从下到上（decorator_c → decorator_b → decorator_a）</span></span><br><span class="line"><span class="comment"># 调用阶段：从外到内（wrapper_a → wrapper_b → wrapper_c → 原函数）</span></span><br></pre></td></tr></table></figure><p><strong>记忆技巧</strong>：就像穿衣服，里面的先穿（定义时），外面的先脱（调用时）。</p><hr><p><strong>Q6: 如何实现带可选参数的装饰器？</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><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><span class="line"><span class="keyword">import</span> functools</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">timer</span>(<span class="params">_func=<span class="literal">None</span>, *, precision=<span class="number">4</span></span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;可以 @timer 或 @timer(precision=2) 使用。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">decorator</span>(<span class="params">func</span>):</span><br><span class="line"><span class="meta">        @functools.wraps(<span class="params">func</span>)</span></span><br><span class="line">        <span class="keyword">def</span> <span class="title function_">wrapper</span>(<span class="params">*args, **kwargs</span>):</span><br><span class="line">            start = time.time()</span><br><span class="line">            result = func(*args, **kwargs)</span><br><span class="line">            elapsed = time.time() - start</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">f&quot;<span class="subst">&#123;func.__name__&#125;</span> 耗时: <span class="subst">&#123;elapsed:.&#123;precision&#125;</span>f&#125;s&quot;</span>)</span><br><span class="line">            <span class="keyword">return</span> result</span><br><span class="line">        <span class="keyword">return</span> wrapper</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 如果直接 @timer，_func 是被装饰的函数</span></span><br><span class="line">    <span class="keyword">if</span> _func <span class="keyword">is</span> <span class="literal">None</span>:</span><br><span class="line">        <span class="keyword">return</span> decorator</span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        <span class="keyword">return</span> decorator(_func)</span><br><span class="line"></span><br><span class="line"><span class="meta">@timer</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">slow_function</span>():</span><br><span class="line">    time.sleep(<span class="number">0.1</span>)</span><br><span class="line"></span><br><span class="line"><span class="meta">@timer(<span class="params">precision=<span class="number">2</span></span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">fast_function</span>():</span><br><span class="line">    time.sleep(<span class="number">0.05</span>)</span><br></pre></td></tr></table></figure><hr><h3 id="12-3-上下文管理器"><a href="#12-3-上下文管理器" class="headerlink" title="12.3 上下文管理器"></a>12.3 上下文管理器</h3><p><strong>Q7: <code>@contextmanager</code> 的工作原理？</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><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> contextlib <span class="keyword">import</span> contextmanager</span><br><span class="line"></span><br><span class="line"><span class="meta">@contextmanager</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">managed_file</span>(<span class="params">name</span>):</span><br><span class="line">    f = <span class="built_in">open</span>(name, <span class="string">&#x27;w&#x27;</span>)</span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        <span class="keyword">yield</span> f</span><br><span class="line">    <span class="keyword">finally</span>:</span><br><span class="line">        f.close()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 等价于：</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">ManagedFile</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, name</span>):</span><br><span class="line">        <span class="variable language_">self</span>.name = name</span><br><span class="line">        <span class="variable language_">self</span>.file = <span class="literal">None</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__enter__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="variable language_">self</span>.file = <span class="built_in">open</span>(<span class="variable language_">self</span>.name, <span class="string">&#x27;w&#x27;</span>)</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>.file</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__exit__</span>(<span class="params">self, exc_type, exc_val, exc_tb</span>):</span><br><span class="line">        <span class="keyword">if</span> <span class="variable language_">self</span>.file:</span><br><span class="line">            <span class="variable language_">self</span>.file.close()</span><br></pre></td></tr></table></figure><p><strong>关键点</strong>：</p><ul><li><code>yield</code> 之前的代码对应 <code>__enter__</code></li><li><code>yield</code> 之后的代码对应 <code>__exit__</code></li><li><code>finally</code> 块确保清理代码一定执行</li></ul><hr><h3 id="12-4-元类与描述符"><a href="#12-4-元类与描述符" class="headerlink" title="12.4 元类与描述符"></a>12.4 元类与描述符</h3><p><strong>Q8: 元类的主要用途？</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><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><span class="line"><span class="comment"># 1. 自动注册子类</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">PluginBase</span>(<span class="title class_ inherited__">type</span>):</span><br><span class="line">    registry = &#123;&#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__new__</span>(<span class="params">mcs, name, bases, namespace</span>):</span><br><span class="line">        cls = <span class="built_in">super</span>().__new__(mcs, name, bases, namespace)</span><br><span class="line">        <span class="keyword">if</span> name != <span class="string">&#x27;BasePlugin&#x27;</span>:</span><br><span class="line">            mcs.registry[name] = cls</span><br><span class="line">        <span class="keyword">return</span> cls</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">BasePlugin</span>(metaclass=PluginBase):</span><br><span class="line">    <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">ImagePlugin</span>(<span class="title class_ inherited__">BasePlugin</span>):</span><br><span class="line">    <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(PluginBase.registry)  <span class="comment"># &#123;&#x27;ImagePlugin&#x27;: &lt;class &#x27;ImagePlugin&#x27;&gt;&#125;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 2. 自动添加方法</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">AddMethodMeta</span>(<span class="title class_ inherited__">type</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__new__</span>(<span class="params">mcs, name, bases, namespace</span>):</span><br><span class="line">        cls = <span class="built_in">super</span>().__new__(mcs, name, bases, namespace)</span><br><span class="line">        <span class="keyword">def</span> <span class="title function_">hello</span>(<span class="params">self</span>):</span><br><span class="line">            <span class="keyword">return</span> <span class="string">f&quot;Hello from <span class="subst">&#123;self.__class__.__name__&#125;</span>&quot;</span></span><br><span class="line">        cls.hello = hello</span><br><span class="line">        <span class="keyword">return</span> cls</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">MyClass</span>(metaclass=AddMethodMeta):</span><br><span class="line">    <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line">obj = MyClass()</span><br><span class="line"><span class="built_in">print</span>(obj.hello())  <span class="comment"># Hello from MyClass</span></span><br></pre></td></tr></table></figure><hr><p><strong>Q9: 描述符与 <code>@property</code> 的关系？</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><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><span class="line"><span class="comment"># @property 本质是描述符</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Person</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, name</span>):</span><br><span class="line">        <span class="variable language_">self</span>._name = name</span><br><span class="line"></span><br><span class="line"><span class="meta">    @property</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">name</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>._name</span><br><span class="line"></span><br><span class="line"><span class="meta">    @name.setter</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">name</span>(<span class="params">self, value</span>):</span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> <span class="built_in">isinstance</span>(value, <span class="built_in">str</span>):</span><br><span class="line">            <span class="keyword">raise</span> TypeError(<span class="string">&quot;name must be str&quot;</span>)</span><br><span class="line">        <span class="variable language_">self</span>._name = value</span><br><span class="line"></span><br><span class="line"><span class="comment"># 等价的描述符实现</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">NameDescriptor</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__get__</span>(<span class="params">self, instance, owner</span>):</span><br><span class="line">        <span class="keyword">if</span> instance <span class="keyword">is</span> <span class="literal">None</span>:</span><br><span class="line">            <span class="keyword">return</span> <span class="variable language_">self</span></span><br><span class="line">        <span class="keyword">return</span> instance._name</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__set__</span>(<span class="params">self, instance, value</span>):</span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> <span class="built_in">isinstance</span>(value, <span class="built_in">str</span>):</span><br><span class="line">            <span class="keyword">raise</span> TypeError(<span class="string">&quot;name must be str&quot;</span>)</span><br><span class="line">        instance._name = value</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Person</span>:</span><br><span class="line">    name = NameDescriptor()</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, name</span>):</span><br><span class="line">        <span class="variable language_">self</span>._name = name</span><br></pre></td></tr></table></figure><hr><h3 id="12-5-异步编程"><a href="#12-5-异步编程" class="headerlink" title="12.5 异步编程"></a>12.5 异步编程</h3><p><strong>Q10: <code>asyncio.sleep()</code> 与 <code>time.sleep()</code> 的区别？</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><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><span class="line"><span class="keyword">import</span> asyncio</span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">test_sync_sleep</span>():</span><br><span class="line">    <span class="string">&quot;&quot;&quot;同步睡眠：阻塞整个线程。&quot;&quot;&quot;</span></span><br><span class="line">    time.sleep(<span class="number">1</span>)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;同步睡眠完成&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">test_async_sleep</span>():</span><br><span class="line">    <span class="string">&quot;&quot;&quot;异步睡眠：不阻塞线程，允许其他协程运行。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">await</span> asyncio.sleep(<span class="number">1</span>)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;异步睡眠完成&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">main</span>():</span><br><span class="line">    <span class="comment"># 同步：串行执行，总耗时 2s</span></span><br><span class="line">    start = time.time()</span><br><span class="line">    <span class="keyword">await</span> test_sync_sleep()</span><br><span class="line">    <span class="keyword">await</span> test_sync_sleep()</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;同步总耗时: <span class="subst">&#123;time.time() - start:<span class="number">.2</span>f&#125;</span>s&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 异步：并发执行，总耗时约 1s</span></span><br><span class="line">    start = time.time()</span><br><span class="line">    <span class="keyword">await</span> asyncio.gather(test_async_sleep(), test_async_sleep())</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;异步总耗时: <span class="subst">&#123;time.time() - start:<span class="number">.2</span>f&#125;</span>s&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># asyncio.run(main())</span></span><br></pre></td></tr></table></figure><p><strong>关键区别</strong>：</p><ul><li><code>time.sleep()</code>: 阻塞整个线程，期间不能执行任何代码</li><li><code>asyncio.sleep()</code>: 非阻塞，让出控制权，其他协程可运行</li></ul><hr><p><strong>Q11: 何时使用异步编程？</strong></p><p><strong>适用场景</strong>：</p><ul><li>I&#x2F;O 密集型任务（网络请求、数据库查询、文件读写）</li><li>高并发 Web 服务器（FastAPI、Sanic）</li><li>网络爬虫、聊天应用</li></ul><p><strong>不适用场景</strong>：</p><ul><li>CPU 密集型任务（计算密集型算法）</li><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 判断依据：任务是否大部分时间在&quot;等待&quot;</span></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">io_task</span>():</span><br><span class="line">    <span class="keyword">await</span> asyncio.sleep(<span class="number">1</span>)  <span class="comment"># 等待 I/O，异步有优势</span></span><br><span class="line">    <span class="comment"># ...</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">cpu_task</span>():</span><br><span class="line">    <span class="comment"># 计算 1e8 次循环</span></span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">10</span>**<span class="number">8</span>):</span><br><span class="line">        <span class="keyword">pass</span></span><br><span class="line">    <span class="comment"># 异步无优势，应考虑多进程</span></span><br></pre></td></tr></table></figure><hr><h3 id="12-6-类型系统"><a href="#12-6-类型系统" class="headerlink" title="12.6 类型系统"></a>12.6 类型系统</h3><p><strong>Q12: <code>Protocol</code> 与抽象基类 (ABC) 的区别？</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><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><span class="line"><span class="keyword">from</span> abc <span class="keyword">import</span> ABC, abstractmethod</span><br><span class="line"><span class="keyword">from</span> typing <span class="keyword">import</span> Protocol</span><br><span class="line"></span><br><span class="line"><span class="comment"># 方式1: 抽象基类（需要显式继承）</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">DrawableABC</span>(<span class="title class_ inherited__">ABC</span>):</span><br><span class="line"><span class="meta">    @abstractmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">draw</span>(<span class="params">self</span>) -&gt; <span class="literal">None</span>:</span><br><span class="line">        <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">CircleABC</span>(<span class="title class_ inherited__">DrawableABC</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">draw</span>(<span class="params">self</span>) -&gt; <span class="literal">None</span>:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;画圆&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 方式2: Protocol（结构子类型，无需继承）</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">DrawableProtocol</span>(<span class="title class_ inherited__">Protocol</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">draw</span>(<span class="params">self</span>) -&gt; <span class="literal">None</span>:</span><br><span class="line">        ...</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">CircleProtocol</span>:</span><br><span class="line">    <span class="comment"># 没有继承，但满足协议</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">draw</span>(<span class="params">self</span>) -&gt; <span class="literal">None</span>:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;画圆&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Protocol 更灵活：第三方类无需修改即可适配</span></span><br><span class="line"><span class="comment"># ABC 更严格：强制子类关系，运行时可检查</span></span><br></pre></td></tr></table></figure><hr><h2 id="13-实战项目：异步-Web-爬虫-数据处理管道"><a href="#13-实战项目：异步-Web-爬虫-数据处理管道" class="headerlink" title="13. 实战项目：异步 Web 爬虫 + 数据处理管道"></a>13. 实战项目：异步 Web 爬虫 + 数据处理管道</h2><h3 id="13-1-项目概述"><a href="#13-1-项目概述" class="headerlink" title="13.1 项目概述"></a>13.1 项目概述</h3><p><strong>目标</strong>：构建一个异步爬虫系统，爬取多个网站，提取数据，并通过装饰器实现缓存、日志、重试等功能。</p><p><strong>技术栈</strong>：</p><ul><li><code>asyncio</code>: 异步协程</li><li><code>aiohttp</code>: 异步 HTTP 客户端</li><li><code>asyncpg</code>: 异步数据库驱动</li><li>装饰器：缓存、重试、日志</li><li>生成器：数据流管道</li></ul><h3 id="13-2-核心代码"><a href="#13-2-核心代码" class="headerlink" title="13.2 核心代码"></a>13.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><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><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> asyncio</span><br><span class="line"><span class="keyword">import</span> aiohttp</span><br><span class="line"><span class="keyword">import</span> functools</span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"><span class="keyword">import</span> hashlib</span><br><span class="line"><span class="keyword">import</span> json</span><br><span class="line"><span class="keyword">from</span> typing <span class="keyword">import</span> AsyncGenerator, <span class="type">Dict</span>, <span class="type">List</span></span><br><span class="line"><span class="keyword">from</span> dataclasses <span class="keyword">import</span> dataclass</span><br><span class="line"></span><br><span class="line"><span class="comment"># ============ 装饰器部分 ============</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">async_cache</span>(<span class="params">expire_seconds=<span class="number">60</span></span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;异步缓存装饰器。&quot;&quot;&quot;</span></span><br><span class="line">    cache = &#123;&#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">decorator</span>(<span class="params">func</span>):</span><br><span class="line"><span class="meta">        @functools.wraps(<span class="params">func</span>)</span></span><br><span class="line">        <span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">wrapper</span>(<span class="params">*args, **kwargs</span>):</span><br><span class="line">            key_parts = (func.__name__, args, <span class="built_in">tuple</span>(<span class="built_in">sorted</span>(kwargs.items())))</span><br><span class="line">            cache_key = hashlib.md5(<span class="built_in">str</span>(key_parts).encode()).hexdigest()</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> cache_key <span class="keyword">in</span> cache:</span><br><span class="line">                data, timestamp = cache[cache_key]</span><br><span class="line">                <span class="keyword">if</span> time.time() - timestamp &lt; expire_seconds:</span><br><span class="line">                    <span class="built_in">print</span>(<span class="string">f&quot;[缓存命中] <span class="subst">&#123;func.__name__&#125;</span>&quot;</span>)</span><br><span class="line">                    <span class="keyword">return</span> data</span><br><span class="line"></span><br><span class="line">            result = <span class="keyword">await</span> func(*args, **kwargs)</span><br><span class="line">            cache[cache_key] = (result, time.time())</span><br><span class="line">            <span class="keyword">return</span> result</span><br><span class="line"></span><br><span class="line">        wrapper.cache = cache</span><br><span class="line">        <span class="keyword">return</span> wrapper</span><br><span class="line">    <span class="keyword">return</span> decorator</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">async_retry</span>(<span class="params">max_attempts=<span class="number">3</span>, delay=<span class="number">1</span>, exceptions=(<span class="params">Exception,</span>)</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;异步重试装饰器。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">decorator</span>(<span class="params">func</span>):</span><br><span class="line"><span class="meta">        @functools.wraps(<span class="params">func</span>)</span></span><br><span class="line">        <span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">wrapper</span>(<span class="params">*args, **kwargs</span>):</span><br><span class="line">            last_exception = <span class="literal">None</span></span><br><span class="line">            <span class="keyword">for</span> attempt <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">1</span>, max_attempts + <span class="number">1</span>):</span><br><span class="line">                <span class="keyword">try</span>:</span><br><span class="line">                    <span class="keyword">return</span> <span class="keyword">await</span> func(*args, **kwargs)</span><br><span class="line">                <span class="keyword">except</span> exceptions <span class="keyword">as</span> e:</span><br><span class="line">                    last_exception = e</span><br><span class="line">                    <span class="keyword">if</span> attempt &lt; max_attempts:</span><br><span class="line">                        <span class="keyword">await</span> asyncio.sleep(delay)</span><br><span class="line">                        <span class="built_in">print</span>(<span class="string">f&quot;[重试 <span class="subst">&#123;attempt&#125;</span>/<span class="subst">&#123;max_attempts&#125;</span>] <span class="subst">&#123;func.__name__&#125;</span>: <span class="subst">&#123;e&#125;</span>&quot;</span>)</span><br><span class="line">            <span class="keyword">raise</span> last_exception</span><br><span class="line">        <span class="keyword">return</span> wrapper</span><br><span class="line">    <span class="keyword">return</span> decorator</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">async_timer</span>(<span class="params">func</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;异步计时装饰器。&quot;&quot;&quot;</span></span><br><span class="line"><span class="meta">    @functools.wraps(<span class="params">func</span>)</span></span><br><span class="line">    <span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">wrapper</span>(<span class="params">*args, **kwargs</span>):</span><br><span class="line">        start = time.time()</span><br><span class="line">        result = <span class="keyword">await</span> func(*args, **kwargs)</span><br><span class="line">        elapsed = time.time() - start</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;[计时] <span class="subst">&#123;func.__name__&#125;</span> 耗时: <span class="subst">&#123;elapsed:<span class="number">.3</span>f&#125;</span>s&quot;</span>)</span><br><span class="line">        <span class="keyword">return</span> result</span><br><span class="line">    <span class="keyword">return</span> wrapper</span><br><span class="line"></span><br><span class="line"><span class="comment"># ============ 数据模型 ============</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@dataclass</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Article</span>:</span><br><span class="line">    title: <span class="built_in">str</span></span><br><span class="line">    url: <span class="built_in">str</span></span><br><span class="line">    content: <span class="built_in">str</span></span><br><span class="line">    published_at: <span class="built_in">str</span></span><br><span class="line">    source: <span class="built_in">str</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ============ 核心爬虫类 ============</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">AsyncNewsCrawler</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;异步新闻爬虫。&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, max_concurrent=<span class="number">10</span></span>):</span><br><span class="line">        <span class="variable language_">self</span>.max_concurrent = max_concurrent</span><br><span class="line">        <span class="variable language_">self</span>.semaphore = asyncio.Semaphore(max_concurrent)</span><br><span class="line">        <span class="variable language_">self</span>.session = <span class="literal">None</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">__aenter__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="variable language_">self</span>.session = aiohttp.ClientSession()</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">__aexit__</span>(<span class="params">self, exc_type, exc_val, exc_tb</span>):</span><br><span class="line">        <span class="keyword">if</span> <span class="variable language_">self</span>.session:</span><br><span class="line">            <span class="keyword">await</span> <span class="variable language_">self</span>.session.close()</span><br><span class="line"></span><br><span class="line"><span class="meta">    @async_cache(<span class="params">expire_seconds=<span class="number">300</span></span>)</span></span><br><span class="line"><span class="meta">    @async_retry(<span class="params">max_attempts=<span class="number">3</span>, delay=<span class="number">1</span>, exceptions=(<span class="params">aiohttp.ClientError,</span>)</span>)</span></span><br><span class="line"><span class="meta">    @async_timer</span></span><br><span class="line">    <span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">fetch_page</span>(<span class="params">self, url: <span class="built_in">str</span></span>) -&gt; <span class="built_in">str</span>:</span><br><span class="line">        <span class="string">&quot;&quot;&quot;获取页面内容（带缓存、重试、计时）。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">async</span> <span class="keyword">with</span> <span class="variable language_">self</span>.semaphore:</span><br><span class="line">            <span class="keyword">async</span> <span class="keyword">with</span> <span class="variable language_">self</span>.session.get(url, timeout=aiohttp.ClientTimeout(total=<span class="number">10</span>)) <span class="keyword">as</span> resp:</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">await</span> resp.text()</span><br><span class="line"></span><br><span class="line">    <span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">parse_articles</span>(<span class="params">self, html: <span class="built_in">str</span>, source: <span class="built_in">str</span></span>) -&gt; <span class="type">List</span>[Article]:</span><br><span class="line">        <span class="string">&quot;&quot;&quot;解析文章列表（简化版，实际应使用 BeautifulSoup）。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">import</span> re</span><br><span class="line">        articles = []</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 模拟解析</span></span><br><span class="line">        title_pattern = re.<span class="built_in">compile</span>(<span class="string">r&#x27;&lt;h[12][^&gt;]*&gt;([^&lt;]+)&lt;/h[12]&gt;&#x27;</span>)</span><br><span class="line">        titles = title_pattern.findall(html)</span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span> i, title <span class="keyword">in</span> <span class="built_in">enumerate</span>(titles[:<span class="number">10</span>]):</span><br><span class="line">            articles.append(Article(</span><br><span class="line">                title=title.strip(),</span><br><span class="line">                url=<span class="string">f&quot;<span class="subst">&#123;source&#125;</span>/article/<span class="subst">&#123;i&#125;</span>&quot;</span>,</span><br><span class="line">                content=<span class="string">f&quot;内容 <span class="subst">&#123;i&#125;</span>&quot;</span>,</span><br><span class="line">                published_at=<span class="string">&quot;2026-01-01&quot;</span>,</span><br><span class="line">                source=source</span><br><span class="line">            ))</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> articles</span><br><span class="line"></span><br><span class="line">    <span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">crawl_source</span>(<span class="params">self, base_url: <span class="built_in">str</span></span>) -&gt; <span class="type">List</span>[Article]:</span><br><span class="line">        <span class="string">&quot;&quot;&quot;爬取单个来源。&quot;&quot;&quot;</span></span><br><span class="line">        html = <span class="keyword">await</span> <span class="variable language_">self</span>.fetch_page(base_url)</span><br><span class="line">        articles = <span class="keyword">await</span> <span class="variable language_">self</span>.parse_articles(html, base_url)</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;[<span class="subst">&#123;base_url&#125;</span>] 获取 <span class="subst">&#123;<span class="built_in">len</span>(articles)&#125;</span> 篇文章&quot;</span>)</span><br><span class="line">        <span class="keyword">return</span> articles</span><br><span class="line"></span><br><span class="line"><span class="comment"># ============ 数据处理管道（生成器） ============</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">article_pipeline</span>(<span class="params">articles: <span class="type">List</span>[Article]</span>) -&gt; AsyncGenerator[<span class="type">Dict</span>, <span class="literal">None</span>]:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;异步生成器：处理文章数据流。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">for</span> article <span class="keyword">in</span> articles:</span><br><span class="line">        <span class="comment"># 数据清洗</span></span><br><span class="line">        article.title = article.title.strip()</span><br><span class="line">        article.content = article.content.replace(<span class="string">&#x27;\n&#x27;</span>, <span class="string">&#x27; &#x27;</span>)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 转换为字典</span></span><br><span class="line">        <span class="keyword">yield</span> &#123;</span><br><span class="line">            <span class="string">&#x27;title&#x27;</span>: article.title,</span><br><span class="line">            <span class="string">&#x27;url&#x27;</span>: article.url,</span><br><span class="line">            <span class="string">&#x27;content&#x27;</span>: article.content[:<span class="number">200</span>],  <span class="comment"># 截取前 200 字符</span></span><br><span class="line">            <span class="string">&#x27;published_at&#x27;</span>: article.published_at,</span><br><span class="line">            <span class="string">&#x27;source&#x27;</span>: article.source</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">filter_keywords</span>(<span class="params">data_stream: AsyncGenerator, keywords: <span class="type">List</span>[<span class="built_in">str</span>]</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;过滤生成器：只保留包含关键词的文章。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">async</span> <span class="keyword">for</span> article <span class="keyword">in</span> data_stream:</span><br><span class="line">        <span class="keyword">if</span> <span class="built_in">any</span>(kw.lower() <span class="keyword">in</span> article[<span class="string">&#x27;title&#x27;</span>].lower() <span class="keyword">for</span> kw <span class="keyword">in</span> keywords):</span><br><span class="line">            <span class="keyword">yield</span> article</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">save_to_json</span>(<span class="params">data_stream: AsyncGenerator, output_file: <span class="built_in">str</span></span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;保存生成器：写入 JSON 文件。&quot;&quot;&quot;</span></span><br><span class="line">    articles = []</span><br><span class="line">    <span class="keyword">async</span> <span class="keyword">for</span> article <span class="keyword">in</span> data_stream:</span><br><span class="line">        articles.append(article)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">with</span> <span class="built_in">open</span>(output_file, <span class="string">&#x27;w&#x27;</span>, encoding=<span class="string">&#x27;utf-8&#x27;</span>) <span class="keyword">as</span> f:</span><br><span class="line">        json.dump(articles, f, ensure_ascii=<span class="literal">False</span>, indent=<span class="number">2</span>)</span><br><span class="line"></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;[保存] 写入 <span class="subst">&#123;<span class="built_in">len</span>(articles)&#125;</span> 篇文章到 <span class="subst">&#123;output_file&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># ============ 主流程 ============</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">main</span>():</span><br><span class="line">    sources = [</span><br><span class="line">        <span class="string">&quot;https://news.ycombinator.com&quot;</span>,</span><br><span class="line">        <span class="string">&quot;https://www.reddit.com/r/programming&quot;</span>,</span><br><span class="line">    ]</span><br><span class="line"></span><br><span class="line">    all_articles = []</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 爬取多个来源</span></span><br><span class="line">    <span class="keyword">async</span> <span class="keyword">with</span> AsyncNewsCrawler(max_concurrent=<span class="number">5</span>) <span class="keyword">as</span> crawler:</span><br><span class="line">        tasks = [crawler.crawl_source(url) <span class="keyword">for</span> url <span class="keyword">in</span> sources]</span><br><span class="line">        results = <span class="keyword">await</span> asyncio.gather(*tasks, return_exceptions=<span class="literal">True</span>)</span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span> result <span class="keyword">in</span> results:</span><br><span class="line">            <span class="keyword">if</span> <span class="built_in">isinstance</span>(result, Exception):</span><br><span class="line">                <span class="built_in">print</span>(<span class="string">f&quot;爬取失败: <span class="subst">&#123;result&#125;</span>&quot;</span>)</span><br><span class="line">            <span class="keyword">else</span>:</span><br><span class="line">                all_articles.extend(result)</span><br><span class="line"></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;\n总共获取 <span class="subst">&#123;<span class="built_in">len</span>(all_articles)&#125;</span> 篇文章&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 数据处理管道</span></span><br><span class="line">    pipeline = article_pipeline(all_articles)</span><br><span class="line">    filtered = filter_keywords(pipeline, keywords=[<span class="string">&#x27;Python&#x27;</span>, <span class="string">&#x27;AI&#x27;</span>, <span class="string">&#x27;Web&#x27;</span>])</span><br><span class="line">    <span class="keyword">await</span> save_to_json(filtered, <span class="string">&#x27;articles.json&#x27;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 统计耗时</span></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;\n=== 性能统计 ===&quot;</span>)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;爬虫缓存命中率:&quot;</span>, <span class="built_in">sum</span>(<span class="number">1</span> <span class="keyword">for</span> _ <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">0</span>)))</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;总耗时:&quot;</span>, time.time() - start_time)</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&#x27;__main__&#x27;</span>:</span><br><span class="line">    start_time = time.time()</span><br><span class="line">    asyncio.run(main())</span><br></pre></td></tr></table></figure><h3 id="13-3-项目亮点"><a href="#13-3-项目亮点" class="headerlink" title="13.3 项目亮点"></a>13.3 项目亮点</h3><ol><li><p><strong>综合运用高级特性</strong>：</p><ul><li>装饰器：缓存、重试、计时</li><li>异步协程：并发爬取、异步 I&#x2F;O</li><li>生成器：数据流管道</li><li>上下文管理器：<code>async with</code> 管理连接</li><li>类型注解：完整类型提示</li></ul></li><li><p><strong>工程化实践</strong>：</p><ul><li>并发控制（<code>Semaphore</code>）</li><li>错误处理（<code>return_exceptions=True</code>）</li><li>资源管理（<code>async with</code> 自动关闭）</li><li>模块化设计（爬虫、管道分离）</li></ul></li><li><p><strong>可扩展性</strong>：</p><ul><li>添加新数据源：只需添加 URL</li><li>添加新处理步骤：插入新的生成器</li><li>调整并发数：修改 <code>max_concurrent</code></li></ul></li></ol><hr><blockquote><p><strong>工程师寄语</strong>：Python 的高级特性不是炫技的工具，而是解决实际问题的利器。当你发现代码里有大量重复的模式时，停下来想一想：是不是可以用描述符、元类或装饰器来抽象？但记住——<strong>不要过度工程化</strong>。如果简单的代码能解决问题,就不要引入元类。</p></blockquote>]]>
    </content>
    <id>https://itdxb.cn/2026/07/23/python/document/%E3%80%90Python%E3%80%91%E9%AB%98%E7%BA%A7%E7%89%B9%E6%80%A7%EF%BC%88%E7%AC%AC%E4%B8%83%E9%98%B6%E6%AE%B5%EF%BC%89/</id>
    <link href="https://itdxb.cn/2026/07/23/python/document/%E3%80%90Python%E3%80%91%E9%AB%98%E7%BA%A7%E7%89%B9%E6%80%A7%EF%BC%88%E7%AC%AC%E4%B8%83%E9%98%B6%E6%AE%B5%EF%BC%89/"/>
    <published>2026-07-23T06:00:00.000Z</published>
    <summary>深入 Python 核心机制，掌握迭代器、生成器、装饰器、上下文管理器、描述符、元类、协程和类型系统。</summary>
    <title>【Python】高级特性（第七阶段）</title>
    <updated>2026-07-24T03:30:13.776Z</updated>
  </entry>
  <entry>
    <author>
      <name>Super Bing</name>
    </author>
    <category term="Python" scheme="https://itdxb.cn/categories/Python/"/>
    <category term="教程" scheme="https://itdxb.cn/tags/%E6%95%99%E7%A8%8B/"/>
    <category term="Python" scheme="https://itdxb.cn/tags/Python/"/>
    <category term="文件IO" scheme="https://itdxb.cn/tags/%E6%96%87%E4%BB%B6IO/"/>
    <category term="序列化" scheme="https://itdxb.cn/tags/%E5%BA%8F%E5%88%97%E5%8C%96/"/>
    <content>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><h1 id="六、文件-I-O-与序列化"><a href="#六、文件-I-O-与序列化" class="headerlink" title="六、文件 I&#x2F;O 与序列化"></a>六、文件 I&#x2F;O 与序列化</h1><blockquote><p><strong>阶段定位</strong>：文件操作和序列化是任何应用程序都无法回避的基础能力。Python 提供了丰富的工具链，从底层的 <code>os</code> 到现代的 <code>pathlib</code>，从 <code>json</code> 到 <code>pickle</code>。本阶段从工程实践出发，讲解如何安全、高效地处理文件和数据持久化。</p></blockquote><figure class="highlight plaintext"><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><span class="line">┌─────────────────────────────────────────────────────────────────┐</span><br><span class="line">│                   文件 I/O 与序列化知识图谱                       │</span><br><span class="line">├─────────────────────────────────────────────────────────────────┤</span><br><span class="line">│                                                                 │</span><br><span class="line">│  1. 文件读写 ──→ open() / with / 文本模式 / 二进制模式            │</span><br><span class="line">│       │                                                         │</span><br><span class="line">│  2. 上下文管理器 ─→ with / @contextmanager / ExitStack           │</span><br><span class="line">│       │                                                         │</span><br><span class="line">│  3. 文件系统 ───→ pathlib / os / os.path / shutil                │</span><br><span class="line">│       │                                                         │</span><br><span class="line">│  4. 序列化 ─────→ JSON / Pickle / CSV / XML                      │</span><br><span class="line">│       │                                                         │</span><br><span class="line">│  5. 压缩归档 ───→ zipfile / tarfile / gzip                       │</span><br><span class="line">│       │                                                         │</span><br><span class="line">│  6. 内存流 ────→ io.StringIO / io.BytesIO                        │</span><br><span class="line">│       │                                                         │</span><br><span class="line">│  7. 工程实践 ───→ 大文件 / 临时文件 / 文件锁 / 原子写入 / sqlite3  │</span><br><span class="line">│                                                                 │</span><br><span class="line">└─────────────────────────────────────────────────────────────────┘</span><br></pre></td></tr></table></figure><hr><h2 id="1-文件读写基础"><a href="#1-文件读写基础" class="headerlink" title="1. 文件读写基础"></a>1. 文件读写基础</h2><h3 id="1-1-基本文件操作"><a href="#1-1-基本文件操作" class="headerlink" title="1.1 基本文件操作"></a>1.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><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 基本读写模式</span></span><br><span class="line"><span class="comment"># &#x27;r&#x27;  读取（默认）</span></span><br><span class="line"><span class="comment"># &#x27;w&#x27;  写入（覆盖）</span></span><br><span class="line"><span class="comment"># &#x27;a&#x27;  追加</span></span><br><span class="line"><span class="comment"># &#x27;x&#x27;  独占创建（文件已存在则报错）</span></span><br><span class="line"><span class="comment"># &#x27;b&#x27;  二进制模式（如 &#x27;rb&#x27;, &#x27;wb&#x27;）</span></span><br><span class="line"><span class="comment"># &#x27;+&#x27;  读写模式（如 &#x27;r+&#x27;, &#x27;w+&#x27;）</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 推荐：使用 with 语句确保文件关闭</span></span><br><span class="line"><span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">&quot;data.txt&quot;</span>, <span class="string">&quot;r&quot;</span>, encoding=<span class="string">&quot;utf-8&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">    content = f.read()          <span class="comment"># 读取全部内容</span></span><br><span class="line">    <span class="built_in">print</span>(content)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 逐行读取（内存友好，适合大文件）</span></span><br><span class="line"><span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">&quot;data.txt&quot;</span>, <span class="string">&quot;r&quot;</span>, encoding=<span class="string">&quot;utf-8&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">    <span class="keyword">for</span> line <span class="keyword">in</span> f:              <span class="comment"># 惰性逐行读取</span></span><br><span class="line">        <span class="built_in">print</span>(line.strip())     <span class="comment"># strip() 去除换行符</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 写入文件</span></span><br><span class="line"><span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">&quot;output.txt&quot;</span>, <span class="string">&quot;w&quot;</span>, encoding=<span class="string">&quot;utf-8&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">    f.write(<span class="string">&quot;第一行\n&quot;</span>)</span><br><span class="line">    f.write(<span class="string">&quot;第二行\n&quot;</span>)</span><br><span class="line">    f.writelines([<span class="string">&quot;第三行\n&quot;</span>, <span class="string">&quot;第四行\n&quot;</span>])</span><br><span class="line"></span><br><span class="line"><span class="comment"># 追加模式</span></span><br><span class="line"><span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">&quot;output.txt&quot;</span>, <span class="string">&quot;a&quot;</span>, encoding=<span class="string">&quot;utf-8&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">    f.write(<span class="string">&quot;追加内容\n&quot;</span>)</span><br></pre></td></tr></table></figure><p><strong>文件打开模式组合</strong>：</p><figure class="highlight plaintext"><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><span class="line">┌─────────────────────────────────────────────────────────┐</span><br><span class="line">│                  文件打开模式组合                         │</span><br><span class="line">├──────────────┬──────────────┬──────────────────────────┤</span><br><span class="line">│ 模式         │ 含义          │ 特性                      │</span><br><span class="line">├──────────────┼──────────────┼──────────────────────────┤</span><br><span class="line">│ &#x27;r&#x27;          │ 只读          │ 默认模式，文件不存在报错    │</span><br><span class="line">│ &#x27;w&#x27;          │ 写入（覆盖）   │ 文件不存在创建，存在清空    │</span><br><span class="line">│ &#x27;a&#x27;          │ 追加          │ 文件不存在创建，指针在末尾  │</span><br><span class="line">│ &#x27;x&#x27;          │ 独占创建      │ 文件已存在则报错           │</span><br><span class="line">│ &#x27;b&#x27;          │ 二进制        │ 与其他模式组合使用         │</span><br><span class="line">│ &#x27;+&#x27;          │ 读写          │ 与其他模式组合使用         │</span><br><span class="line">│ &#x27;r+&#x27;         │ 读写（不清空） │ 文件必须存在，指针在开头    │</span><br><span class="line">│ &#x27;w+&#x27;         │ 读写（覆盖）   │ 文件不存在创建，存在清空    │</span><br><span class="line">│ &#x27;a+&#x27;         │ 读写（追加）   │ 文件不存在创建，指针在末尾  │</span><br><span class="line">└──────────────┴──────────────┴──────────────────────────┘</span><br></pre></td></tr></table></figure><h3 id="1-2-文件指针与随机访问"><a href="#1-2-文件指针与随机访问" class="headerlink" title="1.2 文件指针与随机访问"></a>1.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></pre></td><td class="code"><pre><span class="line"><span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">&quot;data.txt&quot;</span>, <span class="string">&quot;r+&quot;</span>, encoding=<span class="string">&quot;utf-8&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">    <span class="comment"># 读取前 10 字节</span></span><br><span class="line">    chunk = f.read(<span class="number">10</span>)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;当前位置: <span class="subst">&#123;f.tell()&#125;</span>&quot;</span>)    <span class="comment"># 返回当前文件指针位置</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 移动到文件开头</span></span><br><span class="line">    f.seek(<span class="number">0</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 移动到第 20 字节</span></span><br><span class="line">    f.seek(<span class="number">20</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 从当前位置偏移 5 字节</span></span><br><span class="line">    f.seek(<span class="number">5</span>, <span class="number">1</span>)    <span class="comment"># whence: 0=文件头, 1=当前位置, 2=文件尾</span></span><br></pre></td></tr></table></figure><p><strong><code>seek()</code> 的 <code>whence</code> 参数</strong>：</p><table><thead><tr><th>参数</th><th>含义</th></tr></thead><tbody><tr><td>0（默认）</td><td>从文件开头计算偏移量</td></tr><tr><td>1</td><td>从当前位置计算偏移量（仅二进制模式）</td></tr><tr><td>2</td><td>从文件末尾计算偏移量（仅二进制模式）</td></tr></tbody></table><h3 id="1-3-二进制文件读写"><a href="#1-3-二进制文件读写" class="headerlink" title="1.3 二进制文件读写"></a>1.3 二进制文件读写</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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 二进制模式（图片、音频、视频等）</span></span><br><span class="line"><span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">&quot;image.jpg&quot;</span>, <span class="string">&quot;rb&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">    header = f.read(<span class="number">4</span>)          <span class="comment"># 读取文件头</span></span><br><span class="line">    <span class="built_in">print</span>(header)               <span class="comment"># b&#x27;\xff\xd8\xff\xe0&#x27;（JPEG 文件头）</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 复制文件（二进制方式）</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">copy_file</span>(<span class="params">src, dst, chunk_size=<span class="number">8192</span></span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;高效复制文件，使用分块读取避免大文件占用过多内存。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">with</span> <span class="built_in">open</span>(src, <span class="string">&quot;rb&quot;</span>) <span class="keyword">as</span> fsrc, <span class="built_in">open</span>(dst, <span class="string">&quot;wb&quot;</span>) <span class="keyword">as</span> fdst:</span><br><span class="line">        <span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">            chunk = fsrc.read(chunk_size)</span><br><span class="line">            <span class="keyword">if</span> <span class="keyword">not</span> chunk:</span><br><span class="line">                <span class="keyword">break</span></span><br><span class="line">            fdst.write(chunk)</span><br></pre></td></tr></table></figure><blockquote><p><strong>工程建议</strong>：复制文件时使用 <code>shutil.copy2()</code> 替代手动实现，它更高效且保留元数据。</p></blockquote><hr><h2 id="2-with-语句与上下文管理器"><a href="#2-with-语句与上下文管理器" class="headerlink" title="2. with 语句与上下文管理器"></a>2. <code>with</code> 语句与上下文管理器</h2><h3 id="2-1-with-的本质"><a href="#2-1-with-的本质" class="headerlink" title="2.1 with 的本质"></a>2.1 <code>with</code> 的本质</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><span class="line"><span class="comment"># with 语句自动管理资源（文件、锁、数据库连接等）</span></span><br><span class="line"><span class="comment"># 等价于 try/finally，但语法更优雅</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">&quot;file.txt&quot;</span>, <span class="string">&quot;r&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">    data = f.read()</span><br><span class="line"><span class="comment"># 这里 f 已自动关闭，即使发生异常也会关闭</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 等价于：</span></span><br><span class="line">f = <span class="built_in">open</span>(<span class="string">&quot;file.txt&quot;</span>, <span class="string">&quot;r&quot;</span>)</span><br><span class="line"><span class="keyword">try</span>:</span><br><span class="line">    data = f.read()</span><br><span class="line"><span class="keyword">finally</span>:</span><br><span class="line">    f.close()</span><br></pre></td></tr></table></figure><p><strong>上下文管理器协议</strong>：</p><figure class="highlight plaintext"><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><span class="line">┌─────────────────────────────────────────────────────────┐</span><br><span class="line">│                  上下文管理器协议                         │</span><br><span class="line">├─────────────────────────────────────────────────────────┤</span><br><span class="line">│                                                         │</span><br><span class="line">│  一个对象只要实现了 __enter__ 和 __exit__ 方法，          │</span><br><span class="line">│  就可以在 with 语句中使用。                               │</span><br><span class="line">│                                                         │</span><br><span class="line">│  class Resource:                                        │</span><br><span class="line">│      def __enter__(self):                               │</span><br><span class="line">│          # 资源获取逻辑                                  │</span><br><span class="line">│          return self                                    │</span><br><span class="line">│                                                         │</span><br><span class="line">│      def __exit__(self, exc_type, exc_val, exc_tb):     │</span><br><span class="line">│          # 资源释放逻辑（无论是否发生异常都会执行）         │</span><br><span class="line">│          return False  # 返回 True 会抑制异常             │</span><br><span class="line">│                                                         │</span><br><span class="line">└─────────────────────────────────────────────────────────┘</span><br></pre></td></tr></table></figure><h3 id="2-2-同时打开多个文件"><a href="#2-2-同时打开多个文件" class="headerlink" title="2.2 同时打开多个文件"></a>2.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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 同时读写多个文件</span></span><br><span class="line"><span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">&quot;input.txt&quot;</span>, <span class="string">&quot;r&quot;</span>, encoding=<span class="string">&quot;utf-8&quot;</span>) <span class="keyword">as</span> fin, \</span><br><span class="line">     <span class="built_in">open</span>(<span class="string">&quot;output.txt&quot;</span>, <span class="string">&quot;w&quot;</span>, encoding=<span class="string">&quot;utf-8&quot;</span>) <span class="keyword">as</span> fout:</span><br><span class="line">    <span class="keyword">for</span> line <span class="keyword">in</span> fin:</span><br><span class="line">        processed = line.strip().upper()</span><br><span class="line">        fout.write(processed + <span class="string">&quot;\n&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Python 3.10+ 更简洁的语法</span></span><br><span class="line"><span class="keyword">with</span> (</span><br><span class="line">    <span class="built_in">open</span>(<span class="string">&quot;input.txt&quot;</span>, <span class="string">&quot;r&quot;</span>, encoding=<span class="string">&quot;utf-8&quot;</span>) <span class="keyword">as</span> fin,</span><br><span class="line">    <span class="built_in">open</span>(<span class="string">&quot;output.txt&quot;</span>, <span class="string">&quot;w&quot;</span>, encoding=<span class="string">&quot;utf-8&quot;</span>) <span class="keyword">as</span> fout,</span><br><span class="line">):</span><br><span class="line">    fout.write(fin.read())</span><br></pre></td></tr></table></figure><h3 id="2-3-自定义上下文管理器"><a href="#2-3-自定义上下文管理器" class="headerlink" title="2.3 自定义上下文管理器"></a>2.3 自定义上下文管理器</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><span class="line"><span class="keyword">from</span> contextlib <span class="keyword">import</span> contextmanager</span><br><span class="line"></span><br><span class="line"><span class="meta">@contextmanager</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">managed_file</span>(<span class="params">filename, mode=<span class="string">&quot;r&quot;</span></span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;自定义文件上下文管理器。&quot;&quot;&quot;</span></span><br><span class="line">    f = <span class="built_in">open</span>(filename, mode)</span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;打开文件: <span class="subst">&#123;filename&#125;</span>&quot;</span>)</span><br><span class="line">        <span class="keyword">yield</span> f</span><br><span class="line">    <span class="keyword">finally</span>:</span><br><span class="line">        f.close()</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;关闭文件: <span class="subst">&#123;filename&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">with</span> managed_file(<span class="string">&quot;test.txt&quot;</span>, <span class="string">&quot;w&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">    f.write(<span class="string">&quot;Hello&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 更多实用上下文管理器</span></span><br><span class="line"><span class="keyword">from</span> contextlib <span class="keyword">import</span> suppress, redirect_stdout</span><br><span class="line"></span><br><span class="line"><span class="comment"># suppress：忽略指定异常</span></span><br><span class="line"><span class="keyword">with</span> suppress(FileNotFoundError):</span><br><span class="line">    os.remove(<span class="string">&quot;nonexistent.txt&quot;</span>)   <span class="comment"># 不会报错</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># redirect_stdout：重定向标准输出</span></span><br><span class="line"><span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">&quot;log.txt&quot;</span>, <span class="string">&quot;w&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">    <span class="keyword">with</span> redirect_stdout(f):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;这行会写入 log.txt&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># ExitStack：动态管理多个上下文</span></span><br><span class="line"><span class="keyword">from</span> contextlib <span class="keyword">import</span> ExitStack</span><br><span class="line"></span><br><span class="line"><span class="keyword">with</span> ExitStack() <span class="keyword">as</span> stack:</span><br><span class="line">    files = [</span><br><span class="line">        stack.enter_context(<span class="built_in">open</span>(<span class="string">f&quot;file<span class="subst">&#123;i&#125;</span>.txt&quot;</span>))</span><br><span class="line">        <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">10</span>)</span><br><span class="line">    ]</span><br><span class="line">    <span class="comment"># 所有文件会在 with 结束时自动关闭</span></span><br></pre></td></tr></table></figure><hr><h2 id="3-文件系统操作：os、os-path、pathlib"><a href="#3-文件系统操作：os、os-path、pathlib" class="headerlink" title="3. 文件系统操作：os、os.path、pathlib"></a>3. 文件系统操作：<code>os</code>、<code>os.path</code>、<code>pathlib</code></h2><h3 id="3-1-pathlib（推荐，3-4-）"><a href="#3-1-pathlib（推荐，3-4-）" class="headerlink" title="3.1 pathlib（推荐，3.4+）"></a>3.1 <code>pathlib</code>（推荐，3.4+）</h3><p><code>pathlib</code> 是面向对象的文件路径操作库，取代了传统的 <code>os.path</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> pathlib <span class="keyword">import</span> Path</span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建路径对象</span></span><br><span class="line">p = Path(<span class="string">&quot;/home/user/documents/file.txt&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 路径属性</span></span><br><span class="line"><span class="built_in">print</span>(p.name)           <span class="comment"># file.txt</span></span><br><span class="line"><span class="built_in">print</span>(p.stem)           <span class="comment"># file（不含扩展名）</span></span><br><span class="line"><span class="built_in">print</span>(p.suffix)         <span class="comment"># .txt</span></span><br><span class="line"><span class="built_in">print</span>(p.suffixes)       <span class="comment"># [&#x27;.txt&#x27;]（多个扩展名）</span></span><br><span class="line"><span class="built_in">print</span>(p.parent)         <span class="comment"># /home/user/documents</span></span><br><span class="line"><span class="built_in">print</span>(p.parents[<span class="number">0</span>])     <span class="comment"># /home/user/documents</span></span><br><span class="line"><span class="built_in">print</span>(p.parents[<span class="number">1</span>])     <span class="comment"># /home/user</span></span><br><span class="line"><span class="built_in">print</span>(p.parts)          <span class="comment"># (&#x27;/&#x27;, &#x27;home&#x27;, &#x27;user&#x27;, &#x27;documents&#x27;, &#x27;file.txt&#x27;)</span></span><br><span class="line"><span class="built_in">print</span>(p.anchor)         <span class="comment"># /（Windows 为 C:\）</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 路径拼接（使用 / 运算符）</span></span><br><span class="line">base = Path(<span class="string">&quot;/home/user&quot;</span>)</span><br><span class="line">doc = base / <span class="string">&quot;documents&quot;</span> / <span class="string">&quot;file.txt&quot;</span></span><br><span class="line"><span class="built_in">print</span>(doc)              <span class="comment"># /home/user/documents/file.txt</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 相对/绝对路径</span></span><br><span class="line"><span class="built_in">print</span>(Path(<span class="string">&quot;relative.txt&quot;</span>).is_absolute())   <span class="comment"># False</span></span><br><span class="line"><span class="built_in">print</span>(Path(<span class="string">&quot;relative.txt&quot;</span>).resolve())        <span class="comment"># 转为绝对路径</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 路径存在性检查</span></span><br><span class="line"><span class="built_in">print</span>(p.exists())</span><br><span class="line"><span class="built_in">print</span>(p.is_file())</span><br><span class="line"><span class="built_in">print</span>(p.is_dir())</span><br></pre></td></tr></table></figure><h3 id="3-2-文件与目录操作"><a href="#3-2-文件与目录操作" class="headerlink" title="3.2 文件与目录操作"></a>3.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><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><span class="line"><span class="keyword">from</span> pathlib <span class="keyword">import</span> Path</span><br><span class="line"></span><br><span class="line">base = Path(<span class="string">&quot;my_project&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建目录（parents=True 创建父目录，exist_ok=True 不报错）</span></span><br><span class="line">base.mkdir(parents=<span class="literal">True</span>, exist_ok=<span class="literal">True</span>)</span><br><span class="line">(base / <span class="string">&quot;src&quot;</span>).mkdir(exist_ok=<span class="literal">True</span>)</span><br><span class="line">(base / <span class="string">&quot;tests&quot;</span>).mkdir(exist_ok=<span class="literal">True</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建文件</span></span><br><span class="line">config = base / <span class="string">&quot;config.json&quot;</span></span><br><span class="line">config.write_text(<span class="string">&#x27;&#123;&quot;debug&quot;: true&#125;&#x27;</span>, encoding=<span class="string">&quot;utf-8&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 读取文件</span></span><br><span class="line">text = config.read_text(encoding=<span class="string">&quot;utf-8&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(text)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 读取二进制</span></span><br><span class="line"><span class="comment"># binary_data = config.read_bytes()</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 文件操作</span></span><br><span class="line">target = base / <span class="string">&quot;config_backup.json&quot;</span></span><br><span class="line">config.rename(target)           <span class="comment"># 重命名</span></span><br><span class="line">target.replace(config)          <span class="comment"># 移动/替换</span></span><br><span class="line">config.copy(target)             <span class="comment"># 复制（3.8+）</span></span><br><span class="line">config.unlink()                 <span class="comment"># 删除文件</span></span><br><span class="line">target.unlink(missing_ok=<span class="literal">True</span>)  <span class="comment"># 删除，不存在不报错（3.8+）</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 遍历目录</span></span><br><span class="line"><span class="keyword">for</span> item <span class="keyword">in</span> base.iterdir():</span><br><span class="line">    <span class="built_in">print</span>(item.name)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 递归遍历</span></span><br><span class="line"><span class="keyword">for</span> py_file <span class="keyword">in</span> base.rglob(<span class="string">&quot;*.py&quot;</span>):   <span class="comment"># rglob = recursive glob</span></span><br><span class="line">    <span class="built_in">print</span>(py_file)</span><br><span class="line"></span><br><span class="line"><span class="comment"># glob 模式匹配</span></span><br><span class="line"><span class="keyword">for</span> txt <span class="keyword">in</span> base.glob(<span class="string">&quot;*.txt&quot;</span>):</span><br><span class="line">    <span class="built_in">print</span>(txt)</span><br></pre></td></tr></table></figure><h3 id="3-3-os-与-os-path-对照表"><a href="#3-3-os-与-os-path-对照表" class="headerlink" title="3.3 os 与 os.path 对照表"></a>3.3 <code>os</code> 与 <code>os.path</code> 对照表</h3><table><thead><tr><th>操作</th><th><code>os</code>&#x2F;<code>os.path</code></th><th><code>pathlib</code>（推荐）</th></tr></thead><tbody><tr><td>路径拼接</td><td><code>os.path.join(a, b)</code></td><td><code>Path(a) / b</code></td></tr><tr><td>绝对路径</td><td><code>os.path.abspath(p)</code></td><td><code>Path(p).resolve()</code></td></tr><tr><td>存在检查</td><td><code>os.path.exists(p)</code></td><td><code>Path(p).exists()</code></td></tr><tr><td>是否为文件</td><td><code>os.path.isfile(p)</code></td><td><code>Path(p).is_file()</code></td></tr><tr><td>是否为目录</td><td><code>os.path.isdir(p)</code></td><td><code>Path(p).is_dir()</code></td></tr><tr><td>文件名</td><td><code>os.path.basename(p)</code></td><td><code>Path(p).name</code></td></tr><tr><td>目录名</td><td><code>os.path.dirname(p)</code></td><td><code>Path(p).parent</code></td></tr><tr><td>扩展名</td><td><code>os.path.splitext(p)</code></td><td><code>Path(p).suffix</code></td></tr><tr><td>创建目录</td><td><code>os.makedirs(p, exist_ok=True)</code></td><td><code>Path(p).mkdir(parents=True, exist_ok=True)</code></td></tr><tr><td>删除文件</td><td><code>os.remove(p)</code></td><td><code>Path(p).unlink()</code></td></tr><tr><td>删除目录</td><td><code>os.rmdir(p)</code></td><td><code>Path(p).rmdir()</code></td></tr><tr><td>重命名</td><td><code>os.rename(src, dst)</code></td><td><code>Path(src).rename(dst)</code></td></tr></tbody></table><hr><h2 id="4-shutil：高级文件操作"><a href="#4-shutil：高级文件操作" class="headerlink" title="4. shutil：高级文件操作"></a>4. <code>shutil</code>：高级文件操作</h2><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><span class="line"><span class="keyword">import</span> shutil</span><br><span class="line"><span class="keyword">from</span> pathlib <span class="keyword">import</span> Path</span><br><span class="line"></span><br><span class="line">src = Path(<span class="string">&quot;source&quot;</span>)</span><br><span class="line">dst = Path(<span class="string">&quot;destination&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 复制文件</span></span><br><span class="line">shutil.copy2(<span class="string">&quot;file.txt&quot;</span>, <span class="string">&quot;backup/file.txt&quot;</span>)     <span class="comment"># 保留元数据（修改时间等）</span></span><br><span class="line">shutil.copy(<span class="string">&quot;file.txt&quot;</span>, <span class="string">&quot;backup/&quot;</span>)              <span class="comment"># 不保留元数据</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 复制目录树</span></span><br><span class="line">shutil.copytree(<span class="string">&quot;project&quot;</span>, <span class="string">&quot;project_backup&quot;</span>)</span><br><span class="line">shutil.copytree(<span class="string">&quot;project&quot;</span>, <span class="string">&quot;project_backup&quot;</span>, dirs_exist_ok=<span class="literal">True</span>)  <span class="comment"># 3.8+</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 移动文件/目录</span></span><br><span class="line">shutil.move(<span class="string">&quot;old_dir&quot;</span>, <span class="string">&quot;new_dir&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 删除目录树</span></span><br><span class="line">shutil.rmtree(<span class="string">&quot;temp_dir&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 磁盘使用统计</span></span><br><span class="line">total, used, free = shutil.disk_usage(<span class="string">&quot;/&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;总空间: <span class="subst">&#123;total // (<span class="number">1024</span>**<span class="number">3</span>)&#125;</span> GB&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;已用: <span class="subst">&#123;used // (<span class="number">1024</span>**<span class="number">3</span>)&#125;</span> GB&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;可用: <span class="subst">&#123;free // (<span class="number">1024</span>**<span class="number">3</span>)&#125;</span> GB&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查找文件</span></span><br><span class="line">shutil.which(<span class="string">&quot;python&quot;</span>)      <span class="comment"># &#x27;/usr/bin/python&#x27;（类 which 命令）</span></span><br></pre></td></tr></table></figure><hr><h2 id="5-序列化"><a href="#5-序列化" class="headerlink" title="5. 序列化"></a>5. 序列化</h2><h3 id="5-1-JSON"><a href="#5-1-JSON" class="headerlink" title="5.1 JSON"></a>5.1 JSON</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><span class="line"><span class="keyword">import</span> json</span><br><span class="line"><span class="keyword">from</span> pathlib <span class="keyword">import</span> Path</span><br><span class="line"></span><br><span class="line">data = &#123;</span><br><span class="line">    <span class="string">&quot;name&quot;</span>: <span class="string">&quot;Alice&quot;</span>,</span><br><span class="line">    <span class="string">&quot;age&quot;</span>: <span class="number">30</span>,</span><br><span class="line">    <span class="string">&quot;skills&quot;</span>: [<span class="string">&quot;Python&quot;</span>, <span class="string">&quot;Go&quot;</span>, <span class="string">&quot;Rust&quot;</span>],</span><br><span class="line">    <span class="string">&quot;address&quot;</span>: &#123;</span><br><span class="line">        <span class="string">&quot;city&quot;</span>: <span class="string">&quot;Beijing&quot;</span>,</span><br><span class="line">        <span class="string">&quot;zipcode&quot;</span>: <span class="string">&quot;100000&quot;</span></span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="string">&quot;is_active&quot;</span>: <span class="literal">True</span>,</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 序列化（Python 对象 → JSON 字符串）</span></span><br><span class="line">json_str = json.dumps(data, ensure_ascii=<span class="literal">False</span>, indent=<span class="number">2</span>)</span><br><span class="line"><span class="built_in">print</span>(json_str)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 写入文件</span></span><br><span class="line">Path(<span class="string">&quot;data.json&quot;</span>).write_text(</span><br><span class="line">    json.dumps(data, ensure_ascii=<span class="literal">False</span>, indent=<span class="number">2</span>),</span><br><span class="line">    encoding=<span class="string">&quot;utf-8&quot;</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 快捷方式（更推荐）</span></span><br><span class="line"><span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">&quot;data.json&quot;</span>, <span class="string">&quot;w&quot;</span>, encoding=<span class="string">&quot;utf-8&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">    json.dump(data, f, ensure_ascii=<span class="literal">False</span>, indent=<span class="number">2</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 反序列化（JSON 字符串 → Python 对象）</span></span><br><span class="line">loaded = json.loads(json_str)</span><br><span class="line"><span class="built_in">print</span>(loaded[<span class="string">&quot;name&quot;</span>])</span><br><span class="line"></span><br><span class="line"><span class="comment"># 从文件读取</span></span><br><span class="line"><span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">&quot;data.json&quot;</span>, <span class="string">&quot;r&quot;</span>, encoding=<span class="string">&quot;utf-8&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">    loaded = json.load(f)</span><br></pre></td></tr></table></figure><p><strong>JSON 参数速查</strong>：</p><table><thead><tr><th>参数</th><th>说明</th></tr></thead><tbody><tr><td><code>ensure_ascii=False</code></td><td>允许输出中文，不转义为 Unicode 编码</td></tr><tr><td><code>indent=2</code></td><td>格式化缩进</td></tr><tr><td><code>sort_keys=True</code></td><td>按键排序（提高可读性&#x2F;可比较性）</td></tr><tr><td><code>separators=(&#39;,&#39;, &#39;:&#39;)</code></td><td>紧凑输出（生产环境减小体积）</td></tr></tbody></table><p><strong>自定义 JSON 编码</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><span class="line"><span class="keyword">from</span> datetime <span class="keyword">import</span> datetime</span><br><span class="line"><span class="keyword">import</span> json</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">CustomEncoder</span>(json.JSONEncoder):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;自定义 JSON 编码器。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">default</span>(<span class="params">self, obj</span>):</span><br><span class="line">        <span class="keyword">if</span> <span class="built_in">isinstance</span>(obj, datetime):</span><br><span class="line">            <span class="keyword">return</span> obj.isoformat()</span><br><span class="line">        <span class="keyword">if</span> <span class="built_in">isinstance</span>(obj, <span class="built_in">set</span>):</span><br><span class="line">            <span class="keyword">return</span> <span class="built_in">list</span>(obj)</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">super</span>().default(obj)</span><br><span class="line"></span><br><span class="line">data = &#123;</span><br><span class="line">    <span class="string">&quot;created_at&quot;</span>: datetime.now(),</span><br><span class="line">    <span class="string">&quot;tags&quot;</span>: &#123;<span class="string">&quot;python&quot;</span>, <span class="string">&quot;json&quot;</span>&#125;,</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">json_str = json.dumps(data, cls=CustomEncoder, ensure_ascii=<span class="literal">False</span>)</span><br><span class="line"><span class="built_in">print</span>(json_str)</span><br><span class="line"><span class="comment"># &#123;&quot;created_at&quot;: &quot;2024-07-20T10:30:00.000000&quot;, &quot;tags&quot;: [&quot;python&quot;, &quot;json&quot;]&#125;</span></span><br></pre></td></tr></table></figure><h3 id="5-2-Pickle"><a href="#5-2-Pickle" class="headerlink" title="5.2 Pickle"></a>5.2 Pickle</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><span class="line"><span class="keyword">import</span> pickle</span><br><span class="line"></span><br><span class="line"><span class="comment"># Pickle：Python 专用的二进制序列化</span></span><br><span class="line"><span class="comment"># 可以序列化几乎所有 Python 对象（包括自定义类实例）</span></span><br><span class="line"><span class="comment"># 警告：不要反序列化不受信任的数据！存在安全漏洞</span></span><br><span class="line"></span><br><span class="line">data = &#123;</span><br><span class="line">    <span class="string">&quot;name&quot;</span>: <span class="string">&quot;Alice&quot;</span>,</span><br><span class="line">    <span class="string">&quot;func&quot;</span>: <span class="keyword">lambda</span> x: x * <span class="number">2</span>,   <span class="comment"># 甚至可以序列化 lambda！</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 序列化</span></span><br><span class="line"><span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">&quot;data.pkl&quot;</span>, <span class="string">&quot;wb&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">    pickle.dump(data, f)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 反序列化</span></span><br><span class="line"><span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">&quot;data.pkl&quot;</span>, <span class="string">&quot;rb&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">    loaded = pickle.load(f)</span><br><span class="line">    <span class="built_in">print</span>(loaded[<span class="string">&quot;func&quot;</span>](<span class="number">5</span>))   <span class="comment"># 10</span></span><br></pre></td></tr></table></figure><p><strong>JSON vs Pickle 对比</strong>：</p><table><thead><tr><th>特性</th><th>JSON</th><th>Pickle</th></tr></thead><tbody><tr><td>格式</td><td>文本，人类可读</td><td>二进制</td></tr><tr><td>跨语言</td><td>是</td><td>否（仅限 Python）</td></tr><tr><td>安全性</td><td>安全</td><td>不安全（不要加载不信任的数据）</td></tr><tr><td>对象支持</td><td>基础类型</td><td>几乎所有 Python 对象</td></tr><tr><td>速度</td><td>较慢</td><td>较快</td></tr><tr><td>适用场景</td><td>API 数据交换、配置文件</td><td>缓存、临时状态保存</td></tr></tbody></table><blockquote><p><strong>安全警告</strong>：<code>pickle.load()</code> 会执行任意 Python 代码，<strong>永远不要反序列化来自不可信来源的数据</strong>。</p></blockquote><h3 id="5-3-CSV"><a href="#5-3-CSV" class="headerlink" title="5.3 CSV"></a>5.3 CSV</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><span class="line"><span class="keyword">import</span> csv</span><br><span class="line"><span class="keyword">from</span> pathlib <span class="keyword">import</span> Path</span><br><span class="line"></span><br><span class="line"><span class="comment"># 写入 CSV</span></span><br><span class="line">users = [</span><br><span class="line">    [<span class="string">&quot;name&quot;</span>, <span class="string">&quot;age&quot;</span>, <span class="string">&quot;city&quot;</span>],</span><br><span class="line">    [<span class="string">&quot;Alice&quot;</span>, <span class="number">30</span>, <span class="string">&quot;Beijing&quot;</span>],</span><br><span class="line">    [<span class="string">&quot;Bob&quot;</span>, <span class="number">25</span>, <span class="string">&quot;Shanghai&quot;</span>],</span><br><span class="line">    [<span class="string">&quot;Carol&quot;</span>, <span class="number">35</span>, <span class="string">&quot;Shenzhen&quot;</span>],</span><br><span class="line">]</span><br><span class="line"></span><br><span class="line"><span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">&quot;users.csv&quot;</span>, <span class="string">&quot;w&quot;</span>, newline=<span class="string">&quot;&quot;</span>, encoding=<span class="string">&quot;utf-8&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">    writer = csv.writer(f)</span><br><span class="line">    writer.writerows(users)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 读取 CSV</span></span><br><span class="line"><span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">&quot;users.csv&quot;</span>, <span class="string">&quot;r&quot;</span>, encoding=<span class="string">&quot;utf-8&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">    reader = csv.reader(f)</span><br><span class="line">    <span class="keyword">for</span> row <span class="keyword">in</span> reader:</span><br><span class="line">        <span class="built_in">print</span>(row)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用 DictReader/DictWriter（推荐）</span></span><br><span class="line"><span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">&quot;users.csv&quot;</span>, <span class="string">&quot;r&quot;</span>, encoding=<span class="string">&quot;utf-8&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">    reader = csv.DictReader(f)</span><br><span class="line">    <span class="keyword">for</span> row <span class="keyword">in</span> reader:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;<span class="subst">&#123;row[<span class="string">&#x27;name&#x27;</span>]&#125;</span> 来自 <span class="subst">&#123;row[<span class="string">&#x27;city&#x27;</span>]&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># DictWriter</span></span><br><span class="line"><span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">&quot;users.csv&quot;</span>, <span class="string">&quot;w&quot;</span>, newline=<span class="string">&quot;&quot;</span>, encoding=<span class="string">&quot;utf-8&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">    fieldnames = [<span class="string">&quot;name&quot;</span>, <span class="string">&quot;age&quot;</span>, <span class="string">&quot;city&quot;</span>]</span><br><span class="line">    writer = csv.DictWriter(f, fieldnames=fieldnames)</span><br><span class="line">    writer.writeheader()</span><br><span class="line">    writer.writerow(&#123;<span class="string">&quot;name&quot;</span>: <span class="string">&quot;Alice&quot;</span>, <span class="string">&quot;age&quot;</span>: <span class="number">30</span>, <span class="string">&quot;city&quot;</span>: <span class="string">&quot;Beijing&quot;</span>&#125;)</span><br></pre></td></tr></table></figure><h3 id="5-4-XML"><a href="#5-4-XML" class="headerlink" title="5.4 XML"></a>5.4 XML</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><span class="line"><span class="keyword">import</span> xml.etree.ElementTree <span class="keyword">as</span> ET</span><br><span class="line"></span><br><span class="line"><span class="comment"># 解析 XML</span></span><br><span class="line">xml_data = <span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">&lt;library&gt;</span></span><br><span class="line"><span class="string">    &lt;book id=&quot;1&quot;&gt;</span></span><br><span class="line"><span class="string">        &lt;title&gt;Python编程&lt;/title&gt;</span></span><br><span class="line"><span class="string">        &lt;author&gt;张三&lt;/author&gt;</span></span><br><span class="line"><span class="string">        &lt;price&gt;59.00&lt;/price&gt;</span></span><br><span class="line"><span class="string">    &lt;/book&gt;</span></span><br><span class="line"><span class="string">    &lt;book id=&quot;2&quot;&gt;</span></span><br><span class="line"><span class="string">        &lt;title&gt;数据结构&lt;/title&gt;</span></span><br><span class="line"><span class="string">        &lt;author&gt;李四&lt;/author&gt;</span></span><br><span class="line"><span class="string">        &lt;price&gt;45.00&lt;/price&gt;</span></span><br><span class="line"><span class="string">    &lt;/book&gt;</span></span><br><span class="line"><span class="string">&lt;/library&gt;</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">root = ET.fromstring(xml_data)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 遍历</span></span><br><span class="line"><span class="keyword">for</span> book <span class="keyword">in</span> root.findall(<span class="string">&quot;book&quot;</span>):</span><br><span class="line">    book_id = book.get(<span class="string">&quot;id&quot;</span>)</span><br><span class="line">    title = book.find(<span class="string">&quot;title&quot;</span>).text</span><br><span class="line">    author = book.find(<span class="string">&quot;author&quot;</span>).text</span><br><span class="line">    price = <span class="built_in">float</span>(book.find(<span class="string">&quot;price&quot;</span>).text)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;[<span class="subst">&#123;book_id&#125;</span>] <span class="subst">&#123;title&#125;</span> - <span class="subst">&#123;author&#125;</span> (<span class="subst">&#123;price&#125;</span>元)&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 生成 XML</span></span><br><span class="line">new_book = ET.SubElement(root, <span class="string">&quot;book&quot;</span>, &#123;<span class="string">&quot;id&quot;</span>: <span class="string">&quot;3&quot;</span>&#125;)</span><br><span class="line">ET.SubElement(new_book, <span class="string">&quot;title&quot;</span>).text = <span class="string">&quot;算法导论&quot;</span></span><br><span class="line">ET.SubElement(new_book, <span class="string">&quot;author&quot;</span>).text = <span class="string">&quot;王五&quot;</span></span><br><span class="line">ET.SubElement(new_book, <span class="string">&quot;price&quot;</span>).text = <span class="string">&quot;89.00&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 保存</span></span><br><span class="line">ET.indent(root, space=<span class="string">&quot;  &quot;</span>)   <span class="comment"># 3.9+ 格式化</span></span><br><span class="line">tree = ET.ElementTree(root)</span><br><span class="line">tree.write(<span class="string">&quot;library.xml&quot;</span>, encoding=<span class="string">&quot;utf-8&quot;</span>, xml_declaration=<span class="literal">True</span>)</span><br></pre></td></tr></table></figure><hr><h2 id="6-压缩与归档"><a href="#6-压缩与归档" class="headerlink" title="6. 压缩与归档"></a>6. 压缩与归档</h2><h3 id="6-1-zipfile"><a href="#6-1-zipfile" class="headerlink" title="6.1 zipfile"></a>6.1 <code>zipfile</code></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><span class="line"><span class="keyword">import</span> zipfile</span><br><span class="line"><span class="keyword">from</span> pathlib <span class="keyword">import</span> Path</span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建 ZIP</span></span><br><span class="line"><span class="keyword">with</span> zipfile.ZipFile(<span class="string">&quot;archive.zip&quot;</span>, <span class="string">&quot;w&quot;</span>, zipfile.ZIP_DEFLATED) <span class="keyword">as</span> zf:</span><br><span class="line">    zf.write(<span class="string">&quot;file1.txt&quot;</span>)</span><br><span class="line">    zf.write(<span class="string">&quot;file2.txt&quot;</span>, <span class="string">&quot;renamed.txt&quot;</span>)   <span class="comment"># 写入时重命名</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 追加到 ZIP</span></span><br><span class="line"><span class="keyword">with</span> zipfile.ZipFile(<span class="string">&quot;archive.zip&quot;</span>, <span class="string">&quot;a&quot;</span>) <span class="keyword">as</span> zf:</span><br><span class="line">    zf.write(<span class="string">&quot;file3.txt&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 读取 ZIP</span></span><br><span class="line"><span class="keyword">with</span> zipfile.ZipFile(<span class="string">&quot;archive.zip&quot;</span>, <span class="string">&quot;r&quot;</span>) <span class="keyword">as</span> zf:</span><br><span class="line">    <span class="comment"># 列出内容</span></span><br><span class="line">    <span class="built_in">print</span>(zf.namelist())</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 读取文件内容</span></span><br><span class="line">    <span class="keyword">with</span> zf.<span class="built_in">open</span>(<span class="string">&quot;file1.txt&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">        <span class="built_in">print</span>(f.read().decode(<span class="string">&quot;utf-8&quot;</span>))</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 解压全部</span></span><br><span class="line">    zf.extractall(<span class="string">&quot;extracted/&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 解压单个文件</span></span><br><span class="line">    zf.extract(<span class="string">&quot;file1.txt&quot;</span>, <span class="string">&quot;extracted/&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 解压密码保护的 ZIP</span></span><br><span class="line"><span class="keyword">with</span> zipfile.ZipFile(<span class="string">&quot;secret.zip&quot;</span>, <span class="string">&quot;r&quot;</span>) <span class="keyword">as</span> zf:</span><br><span class="line">    zf.extractall(<span class="string">&quot;extracted/&quot;</span>, pwd=<span class="string">b&quot;password&quot;</span>)</span><br></pre></td></tr></table></figure><h3 id="6-2-tarfile"><a href="#6-2-tarfile" class="headerlink" title="6.2 tarfile"></a>6.2 <code>tarfile</code></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><span class="line"><span class="keyword">import</span> tarfile</span><br><span class="line"><span class="keyword">from</span> pathlib <span class="keyword">import</span> Path</span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建 tar.gz</span></span><br><span class="line"><span class="keyword">with</span> tarfile.<span class="built_in">open</span>(<span class="string">&quot;archive.tar.gz&quot;</span>, <span class="string">&quot;w:gz&quot;</span>) <span class="keyword">as</span> tf:</span><br><span class="line">    tf.add(<span class="string">&quot;file1.txt&quot;</span>)</span><br><span class="line">    tf.add(<span class="string">&quot;dir/&quot;</span>, arcname=<span class="string">&quot;data/&quot;</span>)   <span class="comment"># 写入时重命名目录</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 读取 tar</span></span><br><span class="line"><span class="keyword">with</span> tarfile.<span class="built_in">open</span>(<span class="string">&quot;archive.tar.gz&quot;</span>, <span class="string">&quot;r:gz&quot;</span>) <span class="keyword">as</span> tf:</span><br><span class="line">    <span class="built_in">print</span>(tf.getnames())   <span class="comment"># 列出内容</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 提取全部</span></span><br><span class="line">    tf.extractall(<span class="string">&quot;extracted/&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 提取单个</span></span><br><span class="line">    tf.extract(<span class="string">&quot;file1.txt&quot;</span>, <span class="string">&quot;extracted/&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 安全提取（3.12+）</span></span><br><span class="line">    <span class="comment"># tf.extractall(&quot;extracted/&quot;, filter=&quot;fully_trusted&quot;)</span></span><br></pre></td></tr></table></figure><h3 id="6-3-gzip"><a href="#6-3-gzip" class="headerlink" title="6.3 gzip"></a>6.3 <code>gzip</code></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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> gzip</span><br><span class="line"></span><br><span class="line"><span class="comment"># 压缩</span></span><br><span class="line"><span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">&quot;data.txt&quot;</span>, <span class="string">&quot;rb&quot;</span>) <span class="keyword">as</span> fsrc:</span><br><span class="line">    <span class="keyword">with</span> gzip.<span class="built_in">open</span>(<span class="string">&quot;data.txt.gz&quot;</span>, <span class="string">&quot;wb&quot;</span>) <span class="keyword">as</span> fdst:</span><br><span class="line">        fdst.write(fsrc.read())</span><br><span class="line"></span><br><span class="line"><span class="comment"># 更简洁（文本模式）</span></span><br><span class="line"><span class="keyword">with</span> gzip.<span class="built_in">open</span>(<span class="string">&quot;data.txt.gz&quot;</span>, <span class="string">&quot;wt&quot;</span>, encoding=<span class="string">&quot;utf-8&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">    f.write(<span class="string">&quot;Hello, World!&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 解压读取</span></span><br><span class="line"><span class="keyword">with</span> gzip.<span class="built_in">open</span>(<span class="string">&quot;data.txt.gz&quot;</span>, <span class="string">&quot;rt&quot;</span>, encoding=<span class="string">&quot;utf-8&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">    <span class="built_in">print</span>(f.read())</span><br></pre></td></tr></table></figure><hr><h2 id="7-内存流：io-StringIO-与-io-BytesIO"><a href="#7-内存流：io-StringIO-与-io-BytesIO" class="headerlink" title="7. 内存流：io.StringIO 与 io.BytesIO"></a>7. 内存流：<code>io.StringIO</code> 与 <code>io.BytesIO</code></h2><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><span class="line"><span class="keyword">from</span> io <span class="keyword">import</span> StringIO, BytesIO</span><br><span class="line"></span><br><span class="line"><span class="comment"># StringIO：内存中的文本流</span></span><br><span class="line">output = StringIO()</span><br><span class="line">output.write(<span class="string">&quot;第一行\n&quot;</span>)</span><br><span class="line">output.write(<span class="string">&quot;第二行\n&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;第三行&quot;</span>, file=output)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 获取内容</span></span><br><span class="line">content = output.getvalue()</span><br><span class="line"><span class="built_in">print</span>(content)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 重置指针，重新读取</span></span><br><span class="line">output.seek(<span class="number">0</span>)</span><br><span class="line"><span class="keyword">for</span> line <span class="keyword">in</span> output:</span><br><span class="line">    <span class="built_in">print</span>(line.strip())</span><br><span class="line"></span><br><span class="line">output.close()</span><br><span class="line"></span><br><span class="line"><span class="comment"># BytesIO：内存中的字节流</span></span><br><span class="line">binary = BytesIO()</span><br><span class="line">binary.write(<span class="string">b&quot;Hello&quot;</span>)</span><br><span class="line">binary.write(<span class="string">b&quot; World&quot;</span>)</span><br><span class="line"></span><br><span class="line">binary.seek(<span class="number">0</span>)</span><br><span class="line"><span class="built_in">print</span>(binary.read())    <span class="comment"># b&#x27;Hello World&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 实用场景：避免创建临时文件</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">process_csv_in_memory</span>(<span class="params">rows</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;在内存中生成 CSV，直接发送给 HTTP 响应。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">from</span> io <span class="keyword">import</span> StringIO</span><br><span class="line">    <span class="keyword">import</span> csv</span><br><span class="line"></span><br><span class="line">    output = StringIO()</span><br><span class="line">    writer = csv.writer(output)</span><br><span class="line">    writer.writerows(rows)</span><br><span class="line"></span><br><span class="line">    output.seek(<span class="number">0</span>)</span><br><span class="line">    <span class="keyword">return</span> output.getvalue()</span><br></pre></td></tr></table></figure><hr><h2 id="8-文件-I-O-工程实践"><a href="#8-文件-I-O-工程实践" class="headerlink" title="8. 文件 I&#x2F;O 工程实践"></a>8. 文件 I&#x2F;O 工程实践</h2><h3 id="8-1-大文件处理"><a href="#8-1-大文件处理" class="headerlink" title="8.1 大文件处理"></a>8.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><span class="line"><span class="keyword">from</span> pathlib <span class="keyword">import</span> Path</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">process_large_file</span>(<span class="params">filepath, chunk_size=<span class="number">8192</span></span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;分块读取大文件，避免内存溢出。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">with</span> <span class="built_in">open</span>(filepath, <span class="string">&quot;rb&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">        <span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">            chunk = f.read(chunk_size)</span><br><span class="line">            <span class="keyword">if</span> <span class="keyword">not</span> chunk:</span><br><span class="line">                <span class="keyword">break</span></span><br><span class="line">            <span class="keyword">yield</span> chunk</span><br><span class="line"></span><br><span class="line"><span class="comment"># 逐行处理（最省内存）</span></span><br><span class="line"><span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">&quot;huge.log&quot;</span>, <span class="string">&quot;r&quot;</span>, encoding=<span class="string">&quot;utf-8&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">    <span class="keyword">for</span> line <span class="keyword">in</span> f:          <span class="comment"># 内部使用缓冲区，每次只读一行</span></span><br><span class="line">        process_line(line)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用 mmap 处理超大文件（内存映射）</span></span><br><span class="line"><span class="keyword">import</span> mmap</span><br><span class="line"></span><br><span class="line"><span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">&quot;huge.bin&quot;</span>, <span class="string">&quot;r+b&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">    <span class="keyword">with</span> mmap.mmap(f.fileno(), <span class="number">0</span>) <span class="keyword">as</span> mm:</span><br><span class="line">        <span class="comment"># 像操作内存一样操作文件，操作系统自动处理分页</span></span><br><span class="line">        <span class="built_in">print</span>(mm[:<span class="number">100</span>])     <span class="comment"># 读取前 100 字节</span></span><br><span class="line">        mm.seek(<span class="number">1000000</span>)    <span class="comment"># 跳转到 1MB 处</span></span><br><span class="line">        <span class="built_in">print</span>(mm.read(<span class="number">100</span>))</span><br></pre></td></tr></table></figure><h3 id="8-2-临时文件"><a href="#8-2-临时文件" class="headerlink" title="8.2 临时文件"></a>8.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><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><span class="line"><span class="keyword">import</span> tempfile</span><br><span class="line"><span class="keyword">from</span> pathlib <span class="keyword">import</span> Path</span><br><span class="line"></span><br><span class="line"><span class="comment"># 临时文件（自动删除）</span></span><br><span class="line"><span class="keyword">with</span> tempfile.NamedTemporaryFile(mode=<span class="string">&quot;w&quot;</span>, suffix=<span class="string">&quot;.txt&quot;</span>, delete=<span class="literal">True</span>) <span class="keyword">as</span> tmp:</span><br><span class="line">    tmp.write(<span class="string">&quot;临时内容&quot;</span>)</span><br><span class="line">    tmp.flush()</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;临时文件路径: <span class="subst">&#123;tmp.name&#125;</span>&quot;</span>)</span><br><span class="line"><span class="comment"># with 结束时自动删除</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 临时目录</span></span><br><span class="line"><span class="keyword">with</span> tempfile.TemporaryDirectory() <span class="keyword">as</span> tmpdir:</span><br><span class="line">    path = Path(tmpdir) / <span class="string">&quot;data.json&quot;</span></span><br><span class="line">    path.write_text(<span class="string">&quot;&#123;&#125;&quot;</span>)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;临时目录: <span class="subst">&#123;tmpdir&#125;</span>&quot;</span>)</span><br><span class="line"><span class="comment"># with 结束时自动删除整个目录</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 不自动删除的临时文件（用于测试后检查）</span></span><br><span class="line">tmp = tempfile.NamedTemporaryFile(mode=<span class="string">&quot;w&quot;</span>, suffix=<span class="string">&quot;.txt&quot;</span>, delete=<span class="literal">False</span>)</span><br><span class="line">tmp.write(<span class="string">&quot;保留的内容&quot;</span>)</span><br><span class="line">tmp.close()</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;保留的临时文件: <span class="subst">&#123;tmp.name&#125;</span>&quot;</span>)</span><br><span class="line"><span class="comment"># 手动删除：os.unlink(tmp.name)</span></span><br></pre></td></tr></table></figure><h3 id="8-3-文件锁（跨进程同步）"><a href="#8-3-文件锁（跨进程同步）" class="headerlink" title="8.3 文件锁（跨进程同步）"></a>8.3 文件锁（跨进程同步）</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><span class="line"><span class="keyword">import</span> fcntl  <span class="comment"># Linux/macOS</span></span><br><span class="line"><span class="comment"># import msvcrt  # Windows</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">acquire_lock</span>(<span class="params">filepath</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;获取文件锁（Linux/macOS）。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">with</span> <span class="built_in">open</span>(filepath, <span class="string">&quot;w&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">        fcntl.flock(f, fcntl.LOCK_EX)   <span class="comment"># 排他锁</span></span><br><span class="line">        <span class="keyword">try</span>:</span><br><span class="line">            <span class="keyword">yield</span> f</span><br><span class="line">        <span class="keyword">finally</span>:</span><br><span class="line">            fcntl.flock(f, fcntl.LOCK_UN)  <span class="comment"># 释放锁</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Windows 使用 portalocker 或 filelock 库</span></span><br><span class="line"><span class="comment"># pip install filelock</span></span><br><span class="line"><span class="keyword">from</span> filelock <span class="keyword">import</span> FileLock</span><br><span class="line"></span><br><span class="line">lock = FileLock(<span class="string">&quot;data.lock&quot;</span>)</span><br><span class="line"><span class="keyword">with</span> lock:</span><br><span class="line">    <span class="comment"># 临界区：只有一个进程可以执行</span></span><br><span class="line">    <span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">&quot;data.json&quot;</span>, <span class="string">&quot;r+&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">        data = json.load(f)</span><br><span class="line">        data[<span class="string">&quot;count&quot;</span>] += <span class="number">1</span></span><br><span class="line">        f.seek(<span class="number">0</span>)</span><br><span class="line">        json.dump(data, f)</span><br><span class="line">        f.truncate()</span><br></pre></td></tr></table></figure><h3 id="8-4-原子写入"><a href="#8-4-原子写入" class="headerlink" title="8.4 原子写入"></a>8.4 原子写入</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><span class="line"><span class="keyword">from</span> pathlib <span class="keyword">import</span> Path</span><br><span class="line"><span class="keyword">import</span> tempfile</span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">atomic_write</span>(<span class="params">filepath, content</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;原子写入：要么完全成功，要么完全不写（避免写入中断导致文件损坏）。&quot;&quot;&quot;</span></span><br><span class="line">    filepath = Path(filepath)</span><br><span class="line">    tmp = tempfile.NamedTemporaryFile(</span><br><span class="line">        mode=<span class="string">&quot;w&quot;</span>,</span><br><span class="line">        <span class="built_in">dir</span>=filepath.parent,</span><br><span class="line">        suffix=<span class="string">&quot;.tmp&quot;</span>,</span><br><span class="line">        delete=<span class="literal">False</span>,</span><br><span class="line">        encoding=<span class="string">&quot;utf-8&quot;</span></span><br><span class="line">    )</span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        tmp.write(content)</span><br><span class="line">        tmp.close()</span><br><span class="line">        os.replace(tmp.name, filepath)   <span class="comment"># 原子替换</span></span><br><span class="line">    <span class="keyword">except</span> Exception:</span><br><span class="line">        os.unlink(tmp.name)</span><br><span class="line">        <span class="keyword">raise</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用</span></span><br><span class="line">atomic_write(<span class="string">&quot;config.json&quot;</span>, <span class="string">&#x27;&#123;&quot;key&quot;: &quot;value&quot;&#125;&#x27;</span>)</span><br></pre></td></tr></table></figure><h3 id="8-5-sqlite3：内置数据库"><a href="#8-5-sqlite3：内置数据库" class="headerlink" title="8.5 sqlite3：内置数据库"></a>8.5 <code>sqlite3</code>：内置数据库</h3><p>Python 自带 SQLite 数据库引擎，无需安装额外服务，轻量且功能完整。</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> sqlite3</span><br><span class="line"></span><br><span class="line"><span class="comment"># 连接数据库（自动创建文件）</span></span><br><span class="line">conn = sqlite3.connect(<span class="string">&quot;app.db&quot;</span>)</span><br><span class="line">cur = conn.cursor()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建表</span></span><br><span class="line">cur.execute(<span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">    CREATE TABLE IF NOT EXISTS users (</span></span><br><span class="line"><span class="string">        id INTEGER PRIMARY KEY AUTOINCREMENT,</span></span><br><span class="line"><span class="string">        name TEXT NOT NULL,</span></span><br><span class="line"><span class="string">        age INTEGER,</span></span><br><span class="line"><span class="string">        email TEXT UNIQUE</span></span><br><span class="line"><span class="string">    )</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 插入数据（参数化查询，防止 SQL 注入）</span></span><br><span class="line">cur.execute(</span><br><span class="line">    <span class="string">&quot;INSERT INTO users (name, age, email) VALUES (?, ?, ?)&quot;</span>,</span><br><span class="line">    (<span class="string">&quot;Alice&quot;</span>, <span class="number">30</span>, <span class="string">&quot;alice@example.com&quot;</span>)</span><br><span class="line">)</span><br><span class="line">conn.commit()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查询</span></span><br><span class="line">cur.execute(<span class="string">&quot;SELECT * FROM users WHERE age &gt; ?&quot;</span>, (<span class="number">25</span>,))</span><br><span class="line"><span class="keyword">for</span> row <span class="keyword">in</span> cur.fetchall():</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;ID: <span class="subst">&#123;row[<span class="number">0</span>]&#125;</span>, Name: <span class="subst">&#123;row[<span class="number">1</span>]&#125;</span>, Age: <span class="subst">&#123;row[<span class="number">2</span>]&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用 Row 作为字典式访问</span></span><br><span class="line">conn.row_factory = sqlite3.Row</span><br><span class="line">cur = conn.cursor()</span><br><span class="line">cur.execute(<span class="string">&quot;SELECT * FROM users LIMIT 1&quot;</span>)</span><br><span class="line">row = cur.fetchone()</span><br><span class="line"><span class="built_in">print</span>(row[<span class="string">&quot;name&quot;</span>], row[<span class="string">&quot;email&quot;</span>])   <span class="comment"># 按列名访问</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 批量操作</span></span><br><span class="line">users = [</span><br><span class="line">    (<span class="string">&quot;Bob&quot;</span>, <span class="number">25</span>, <span class="string">&quot;bob@example.com&quot;</span>),</span><br><span class="line">    (<span class="string">&quot;Carol&quot;</span>, <span class="number">35</span>, <span class="string">&quot;carol@example.com&quot;</span>),</span><br><span class="line">]</span><br><span class="line">cur.executemany(<span class="string">&quot;INSERT INTO users (name, age, email) VALUES (?, ?, ?)&quot;</span>, users)</span><br><span class="line">conn.commit()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 事务管理</span></span><br><span class="line"><span class="keyword">try</span>:</span><br><span class="line">    cur.execute(<span class="string">&quot;UPDATE users SET age = ? WHERE name = ?&quot;</span>, (<span class="number">31</span>, <span class="string">&quot;Alice&quot;</span>))</span><br><span class="line">    conn.commit()</span><br><span class="line"><span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">    conn.rollback()</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;事务回滚: <span class="subst">&#123;e&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line">conn.close()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 工程场景：原型开发、本地缓存、嵌入式应用、数据分析</span></span><br><span class="line"><span class="comment"># 优势：零配置、单文件、标准库自带、支持 SQL</span></span><br></pre></td></tr></table></figure><hr><h2 id="9-工作实战场景"><a href="#9-工作实战场景" class="headerlink" title="9. 工作实战场景"></a>9. 工作实战场景</h2><h3 id="9-1-日志轮转系统"><a href="#9-1-日志轮转系统" class="headerlink" title="9.1 日志轮转系统"></a>9.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><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><span class="line"><span class="keyword">import</span> logging</span><br><span class="line"><span class="keyword">import</span> logging.handlers</span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"><span class="keyword">from</span> datetime <span class="keyword">import</span> datetime</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">setup_logging</span>(<span class="params">app_name=<span class="string">&quot;app&quot;</span>, log_dir=<span class="string">&quot;logs&quot;</span>, max_size=<span class="number">10</span>*<span class="number">1024</span>*<span class="number">1024</span>, backup_count=<span class="number">5</span></span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;设置日志轮转系统。&quot;&quot;&quot;</span></span><br><span class="line">    os.makedirs(log_dir, exist_ok=<span class="literal">True</span>)</span><br><span class="line">    </span><br><span class="line">    formatter = logging.Formatter(</span><br><span class="line">        <span class="string">&quot;%(asctime)s [%(levelname)s] %(name)s:%(lineno)d: %(message)s&quot;</span>,</span><br><span class="line">        datefmt=<span class="string">&quot;%Y-%m-%d %H:%M:%S&quot;</span></span><br><span class="line">    )</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 控制台输出</span></span><br><span class="line">    console_handler = logging.StreamHandler()</span><br><span class="line">    console_handler.setLevel(logging.INFO)</span><br><span class="line">    console_handler.setFormatter(formatter)</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 文件输出（按大小轮转）</span></span><br><span class="line">    file_handler = logging.handlers.RotatingFileHandler(</span><br><span class="line">        os.path.join(log_dir, <span class="string">f&quot;<span class="subst">&#123;app_name&#125;</span>.log&quot;</span>),</span><br><span class="line">        maxBytes=max_size,</span><br><span class="line">        backupCount=backup_count,</span><br><span class="line">        encoding=<span class="string">&quot;utf-8&quot;</span></span><br><span class="line">    )</span><br><span class="line">    file_handler.setLevel(logging.INFO)</span><br><span class="line">    file_handler.setFormatter(formatter)</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 错误日志单独文件</span></span><br><span class="line">    error_handler = logging.handlers.RotatingFileHandler(</span><br><span class="line">        os.path.join(log_dir, <span class="string">f&quot;<span class="subst">&#123;app_name&#125;</span>_error.log&quot;</span>),</span><br><span class="line">        maxBytes=max_size,</span><br><span class="line">        backupCount=backup_count,</span><br><span class="line">        encoding=<span class="string">&quot;utf-8&quot;</span></span><br><span class="line">    )</span><br><span class="line">    error_handler.setLevel(logging.ERROR)</span><br><span class="line">    error_handler.setFormatter(formatter)</span><br><span class="line">    </span><br><span class="line">    logger = logging.getLogger(app_name)</span><br><span class="line">    logger.setLevel(logging.DEBUG)</span><br><span class="line">    logger.addHandler(console_handler)</span><br><span class="line">    logger.addHandler(file_handler)</span><br><span class="line">    logger.addHandler(error_handler)</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> logger</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用示例</span></span><br><span class="line">logger = setup_logging(<span class="string">&quot;myapp&quot;</span>)</span><br><span class="line">logger.info(<span class="string">&quot;应用启动&quot;</span>)</span><br><span class="line">logger.error(<span class="string">&quot;数据库连接失败&quot;</span>)</span><br></pre></td></tr></table></figure><h3 id="9-2-配置文件热更新"><a href="#9-2-配置文件热更新" class="headerlink" title="9.2 配置文件热更新"></a>9.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><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><span class="line"><span class="keyword">import</span> json</span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"><span class="keyword">from</span> pathlib <span class="keyword">import</span> Path</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">ConfigManager</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;配置管理：支持热更新。&quot;&quot;&quot;</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, config_path</span>):</span><br><span class="line">        <span class="variable language_">self</span>.config_path = Path(config_path)</span><br><span class="line">        <span class="variable language_">self</span>._config = &#123;&#125;</span><br><span class="line">        <span class="variable language_">self</span>._last_modified = <span class="number">0</span></span><br><span class="line">        <span class="variable language_">self</span>.load()</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">load</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;加载配置文件。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">if</span> <span class="variable language_">self</span>.config_path.exists():</span><br><span class="line">            <span class="keyword">with</span> <span class="built_in">open</span>(<span class="variable language_">self</span>.config_path, <span class="string">&quot;r&quot;</span>, encoding=<span class="string">&quot;utf-8&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">                <span class="variable language_">self</span>._config = json.load(f)</span><br><span class="line">            <span class="variable language_">self</span>._last_modified = <span class="variable language_">self</span>.config_path.stat().st_mtime</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get</span>(<span class="params">self, key, default=<span class="literal">None</span></span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;获取配置值（自动检测文件变化）。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">if</span> <span class="variable language_">self</span>.config_path.exists():</span><br><span class="line">            current_modified = <span class="variable language_">self</span>.config_path.stat().st_mtime</span><br><span class="line">            <span class="keyword">if</span> current_modified &gt; <span class="variable language_">self</span>._last_modified:</span><br><span class="line">                <span class="variable language_">self</span>.load()</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>._config.get(key, default)</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">watch</span>(<span class="params">self, callback</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;监听配置文件变化。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">            <span class="keyword">if</span> <span class="variable language_">self</span>.config_path.exists():</span><br><span class="line">                current_modified = <span class="variable language_">self</span>.config_path.stat().st_mtime</span><br><span class="line">                <span class="keyword">if</span> current_modified &gt; <span class="variable language_">self</span>._last_modified:</span><br><span class="line">                    <span class="variable language_">self</span>.load()</span><br><span class="line">                    callback(<span class="variable language_">self</span>._config)</span><br><span class="line">            time.sleep(<span class="number">1</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用示例</span></span><br><span class="line">config = ConfigManager(<span class="string">&quot;config.json&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">on_config_change</span>(<span class="params">new_config</span>):</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;配置已更新: <span class="subst">&#123;new_config&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 后台线程监听配置变化</span></span><br><span class="line"><span class="keyword">import</span> threading</span><br><span class="line">threading.Thread(target=config.watch, args=(on_config_change,), daemon=<span class="literal">True</span>).start()</span><br></pre></td></tr></table></figure><h3 id="9-3-文件上传与处理"><a href="#9-3-文件上传与处理" class="headerlink" title="9.3 文件上传与处理"></a>9.3 文件上传与处理</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> pathlib <span class="keyword">import</span> Path</span><br><span class="line"><span class="keyword">import</span> hashlib</span><br><span class="line"><span class="keyword">import</span> magic</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">FileUploader</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;文件上传处理器：验证、存储、去重。&quot;&quot;&quot;</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, upload_dir=<span class="string">&quot;uploads&quot;</span>, max_size=<span class="number">10</span>*<span class="number">1024</span>*<span class="number">1024</span></span>):</span><br><span class="line">        <span class="variable language_">self</span>.upload_dir = Path(upload_dir)</span><br><span class="line">        <span class="variable language_">self</span>.max_size = max_size</span><br><span class="line">        <span class="variable language_">self</span>.allowed_types = &#123;<span class="string">&quot;image/jpeg&quot;</span>, <span class="string">&quot;image/png&quot;</span>, <span class="string">&quot;application/pdf&quot;</span>&#125;</span><br><span class="line">        <span class="variable language_">self</span>.upload_dir.mkdir(parents=<span class="literal">True</span>, exist_ok=<span class="literal">True</span>)</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">_calculate_hash</span>(<span class="params">self, file_path</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;计算文件 MD5 哈希值。&quot;&quot;&quot;</span></span><br><span class="line">        hash_md5 = hashlib.md5()</span><br><span class="line">        <span class="keyword">with</span> <span class="built_in">open</span>(file_path, <span class="string">&quot;rb&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">            <span class="keyword">for</span> chunk <span class="keyword">in</span> <span class="built_in">iter</span>(<span class="keyword">lambda</span>: f.read(<span class="number">8192</span>), <span class="string">b&quot;&quot;</span>):</span><br><span class="line">                hash_md5.update(chunk)</span><br><span class="line">        <span class="keyword">return</span> hash_md5.hexdigest()</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">_validate_file</span>(<span class="params">self, file_path</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;验证文件类型和大小。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="comment"># 检查大小</span></span><br><span class="line">        <span class="keyword">if</span> file_path.stat().st_size &gt; <span class="variable language_">self</span>.max_size:</span><br><span class="line">            <span class="keyword">raise</span> ValueError(<span class="string">&quot;文件过大&quot;</span>)</span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 检查类型</span></span><br><span class="line">        file_type = magic.from_file(<span class="built_in">str</span>(file_path), mime=<span class="literal">True</span>)</span><br><span class="line">        <span class="keyword">if</span> file_type <span class="keyword">not</span> <span class="keyword">in</span> <span class="variable language_">self</span>.allowed_types:</span><br><span class="line">            <span class="keyword">raise</span> ValueError(<span class="string">f&quot;不支持的文件类型: <span class="subst">&#123;file_type&#125;</span>&quot;</span>)</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> file_type</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">upload</span>(<span class="params">self, source_path</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;上传文件：验证、哈希去重、存储。&quot;&quot;&quot;</span></span><br><span class="line">        source = Path(source_path)</span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 验证</span></span><br><span class="line">        file_type = <span class="variable language_">self</span>._validate_file(source)</span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 计算哈希（去重）</span></span><br><span class="line">        file_hash = <span class="variable language_">self</span>._calculate_hash(source)</span><br><span class="line">        extension = source.suffix.lower()</span><br><span class="line">        target_path = <span class="variable language_">self</span>.upload_dir / <span class="string">f&quot;<span class="subst">&#123;file_hash&#125;</span><span class="subst">&#123;extension&#125;</span>&quot;</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 如果已存在，直接返回路径</span></span><br><span class="line">        <span class="keyword">if</span> target_path.exists():</span><br><span class="line">            <span class="keyword">return</span> <span class="built_in">str</span>(target_path)</span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 复制文件</span></span><br><span class="line">        <span class="keyword">import</span> shutil</span><br><span class="line">        shutil.copy2(source, target_path)</span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 设置权限（可选）</span></span><br><span class="line">        target_path.chmod(<span class="number">0o644</span>)</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">str</span>(target_path)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用示例</span></span><br><span class="line">uploader = FileUploader()</span><br><span class="line">result = uploader.upload(<span class="string">&quot;photo.jpg&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;文件已上传到: <span class="subst">&#123;result&#125;</span>&quot;</span>)</span><br></pre></td></tr></table></figure><h3 id="9-4-数据导入导出工具"><a href="#9-4-数据导入导出工具" class="headerlink" title="9.4 数据导入导出工具"></a>9.4 数据导入导出工具</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> json</span><br><span class="line"><span class="keyword">import</span> csv</span><br><span class="line"><span class="keyword">from</span> pathlib <span class="keyword">import</span> Path</span><br><span class="line"><span class="keyword">from</span> datetime <span class="keyword">import</span> datetime</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">DataExporter</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;数据导入导出工具：支持 JSON/CSV 格式。&quot;&quot;&quot;</span></span><br><span class="line">    </span><br><span class="line"><span class="meta">    @staticmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">export_json</span>(<span class="params">data, filepath, **kwargs</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;导出为 JSON。&quot;&quot;&quot;</span></span><br><span class="line">        kwargs.setdefault(<span class="string">&quot;ensure_ascii&quot;</span>, <span class="literal">False</span>)</span><br><span class="line">        kwargs.setdefault(<span class="string">&quot;indent&quot;</span>, <span class="number">2</span>)</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">with</span> <span class="built_in">open</span>(filepath, <span class="string">&quot;w&quot;</span>, encoding=<span class="string">&quot;utf-8&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">            json.dump(data, f, **kwargs)</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;数据已导出到: <span class="subst">&#123;filepath&#125;</span>&quot;</span>)</span><br><span class="line">    </span><br><span class="line"><span class="meta">    @staticmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">import_json</span>(<span class="params">filepath</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;从 JSON 导入。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">with</span> <span class="built_in">open</span>(filepath, <span class="string">&quot;r&quot;</span>, encoding=<span class="string">&quot;utf-8&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">            <span class="keyword">return</span> json.load(f)</span><br><span class="line">    </span><br><span class="line"><span class="meta">    @staticmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">export_csv</span>(<span class="params">data, filepath, headers=<span class="literal">None</span></span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;导出为 CSV。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">with</span> <span class="built_in">open</span>(filepath, <span class="string">&quot;w&quot;</span>, newline=<span class="string">&quot;&quot;</span>, encoding=<span class="string">&quot;utf-8&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">            writer = csv.writer(f)</span><br><span class="line">            </span><br><span class="line">            <span class="comment"># 如果提供了表头，先写表头</span></span><br><span class="line">            <span class="keyword">if</span> headers:</span><br><span class="line">                writer.writerow(headers)</span><br><span class="line">            </span><br><span class="line">            <span class="comment"># 写入数据</span></span><br><span class="line">            <span class="keyword">if</span> <span class="built_in">isinstance</span>(data, <span class="built_in">list</span>) <span class="keyword">and</span> <span class="built_in">isinstance</span>(data[<span class="number">0</span>], <span class="built_in">dict</span>):</span><br><span class="line">                <span class="comment"># 字典列表，使用表头</span></span><br><span class="line">                <span class="keyword">if</span> <span class="keyword">not</span> headers:</span><br><span class="line">                    headers = data[<span class="number">0</span>].keys()</span><br><span class="line">                    writer.writerow(headers)</span><br><span class="line">                <span class="keyword">for</span> row <span class="keyword">in</span> data:</span><br><span class="line">                    writer.writerow([row.get(h) <span class="keyword">for</span> h <span class="keyword">in</span> headers])</span><br><span class="line">            <span class="keyword">else</span>:</span><br><span class="line">                <span class="comment"># 普通列表</span></span><br><span class="line">                writer.writerows(data)</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;数据已导出到: <span class="subst">&#123;filepath&#125;</span>&quot;</span>)</span><br><span class="line">    </span><br><span class="line"><span class="meta">    @staticmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">import_csv</span>(<span class="params">filepath</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;从 CSV 导入（返回字典列表）。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">with</span> <span class="built_in">open</span>(filepath, <span class="string">&quot;r&quot;</span>, encoding=<span class="string">&quot;utf-8&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">            reader = csv.DictReader(f)</span><br><span class="line">            <span class="keyword">return</span> <span class="built_in">list</span>(reader)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用示例</span></span><br><span class="line">data = [</span><br><span class="line">    &#123;<span class="string">&quot;name&quot;</span>: <span class="string">&quot;Alice&quot;</span>, <span class="string">&quot;age&quot;</span>: <span class="number">30</span>, <span class="string">&quot;city&quot;</span>: <span class="string">&quot;Beijing&quot;</span>&#125;,</span><br><span class="line">    &#123;<span class="string">&quot;name&quot;</span>: <span class="string">&quot;Bob&quot;</span>, <span class="string">&quot;age&quot;</span>: <span class="number">25</span>, <span class="string">&quot;city&quot;</span>: <span class="string">&quot;Shanghai&quot;</span>&#125;,</span><br><span class="line">]</span><br><span class="line"></span><br><span class="line">exporter = DataExporter()</span><br><span class="line">exporter.export_json(data, <span class="string">&quot;users.json&quot;</span>)</span><br><span class="line">exporter.export_csv(data, <span class="string">&quot;users.csv&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 导入</span></span><br><span class="line">loaded_data = exporter.import_json(<span class="string">&quot;users.json&quot;</span>)</span><br><span class="line">csv_data = exporter.import_csv(<span class="string">&quot;users.csv&quot;</span>)</span><br></pre></td></tr></table></figure><hr><h2 id="10-常见面试题汇总"><a href="#10-常见面试题汇总" class="headerlink" title="10. 常见面试题汇总"></a>10. 常见面试题汇总</h2><h3 id="10-1-基础概念题"><a href="#10-1-基础概念题" class="headerlink" title="10.1 基础概念题"></a>10.1 基础概念题</h3><p><strong>Q1：<code>with</code> 语句的作用是什么？它的原理是什么？</strong></p><p><strong>A</strong>：<code>with</code> 语句用于自动管理资源（文件、锁、数据库连接等），确保资源在使用完毕后被正确释放，即使发生异常也会释放。</p><p>原理：实现了上下文管理器协议，即对象必须实现 <code>__enter__</code> 和 <code>__exit__</code> 方法：</p><ul><li><code>__enter__</code>：进入 <code>with</code> 块时调用，返回资源对象</li><li><code>__exit__</code>：退出 <code>with</code> 块时调用，负责资源清理</li></ul><hr><p><strong>Q2：文件打开模式 <code>r</code>、<code>w</code>、<code>a</code>、<code>x</code> 的区别？</strong></p><p><strong>A</strong>：</p><ul><li><code>r</code>：只读（默认），文件不存在报错</li><li><code>w</code>：写入（覆盖），文件不存在创建，存在清空</li><li><code>a</code>：追加，文件不存在创建，指针在末尾</li><li><code>x</code>：独占创建，文件已存在则报错</li></ul><hr><p><strong>Q3：<code>pathlib</code> 相比 <code>os.path</code> 有什么优势？</strong></p><p><strong>A</strong>：</p><ol><li><strong>面向对象</strong>：路径是对象而非字符串，支持方法调用</li><li><strong>操作符重载</strong>：使用 <code>/</code> 进行路径拼接，更直观</li><li><strong>链式调用</strong>：<code>Path(&quot;a&quot;) / &quot;b&quot; / &quot;c&quot;</code> 可以连续操作</li><li><strong>统一接口</strong>：文件操作（read_text&#x2F;write_text）直接在路径对象上调用</li><li><strong>跨平台</strong>：自动处理不同操作系统的路径分隔符</li></ol><hr><p><strong>Q4：JSON 和 Pickle 的区别？</strong></p><p><strong>A</strong>：</p><table><thead><tr><th>特性</th><th>JSON</th><th>Pickle</th></tr></thead><tbody><tr><td>格式</td><td>文本，人类可读</td><td>二进制</td></tr><tr><td>跨语言</td><td>是</td><td>否（仅限 Python）</td></tr><tr><td>安全性</td><td>安全</td><td>不安全（不要加载不信任的数据）</td></tr><tr><td>对象支持</td><td>基础类型</td><td>几乎所有 Python 对象</td></tr></tbody></table><hr><p><strong>Q5：什么是原子写入？为什么需要原子写入？</strong></p><p><strong>A</strong>：原子写入是指文件写入操作要么完全成功，要么完全不写。使用临时文件写入后再原子替换的方式实现。</p><p>原因：避免写入过程中发生异常（如断电、磁盘满）导致文件损坏或内容不完整。</p><hr><h3 id="10-2-实战应用题"><a href="#10-2-实战应用题" class="headerlink" title="10.2 实战应用题"></a>10.2 实战应用题</h3><p><strong>Q6：如何处理大文件（GB 级别）？</strong></p><p><strong>A</strong>：</p><ol><li><strong>逐行读取</strong>：<code>for line in f:</code>（最省内存）</li><li><strong>分块读取</strong>：<code>f.read(chunk_size)</code>，每次读取固定大小</li><li><strong>内存映射</strong>：<code>mmap</code> 模块，像操作内存一样操作文件</li><li><strong>生成器</strong>：使用 <code>yield</code> 分块处理，避免一次性加载</li></ol><hr><p><strong>Q7：如何实现文件上传的去重功能？</strong></p><p><strong>A</strong>：</p><ol><li>计算文件的哈希值（MD5&#x2F;SHA256）</li><li>使用哈希值作为文件名存储</li><li>上传前检查哈希值是否已存在</li><li>如果已存在，直接返回已有路径</li></ol><hr><p><strong>Q8：如何安全地处理用户上传的文件？</strong></p><p><strong>A</strong>：</p><ol><li><strong>验证文件类型</strong>：检查 MIME 类型和文件头</li><li><strong>限制文件大小</strong>：防止大文件攻击</li><li><strong>重命名文件</strong>：使用哈希值或随机字符串，避免路径遍历攻击</li><li><strong>存储在非 Web 可访问目录</strong>：或使用安全的文件服务</li><li><strong>扫描病毒</strong>：使用 ClamAV 等工具</li><li><strong>设置合理的文件权限</strong>：避免执行权限</li></ol><hr><p><strong>Q9：如何实现配置文件的热更新？</strong></p><p><strong>A</strong>：</p><ol><li>定期检查配置文件的修改时间</li><li>如果文件被修改，重新加载配置</li><li>使用线程或定时器后台监听</li><li>提供回调函数处理配置变化</li></ol><hr><p><strong>Q10：<code>sqlite3</code> 的适用场景是什么？</strong></p><p><strong>A</strong>：</p><ul><li>原型开发、本地缓存、嵌入式应用、数据分析</li><li>优势：零配置、单文件、标准库自带、支持 SQL</li><li>不适用：高并发、大规模数据、多进程写入</li></ul><hr><h3 id="10-3-代码分析题"><a href="#10-3-代码分析题" class="headerlink" title="10.3 代码分析题"></a>10.3 代码分析题</h3><p><strong>Q11：分析以下代码的问题：</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></pre></td><td class="code"><pre><span class="line">f = <span class="built_in">open</span>(<span class="string">&quot;data.txt&quot;</span>, <span class="string">&quot;w&quot;</span>)</span><br><span class="line">f.write(<span class="string">&quot;hello&quot;</span>)</span><br><span class="line"><span class="comment"># 没有调用 f.close()</span></span><br></pre></td></tr></table></figure><p><strong>A</strong>：没有关闭文件，可能导致：</p><ol><li>数据未立即写入磁盘（缓冲问题）</li><li>文件句柄泄漏</li><li>程序退出时才能释放资源</li></ol><p>应使用 <code>with</code> 语句：</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><span class="line"><span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">&quot;data.txt&quot;</span>, <span class="string">&quot;w&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">    f.write(<span class="string">&quot;hello&quot;</span>)</span><br></pre></td></tr></table></figure><hr><p><strong>Q12：分析以下代码的问题：</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> json</span><br><span class="line"></span><br><span class="line">data = &#123;<span class="string">&quot;name&quot;</span>: <span class="string">&quot;张三&quot;</span>, <span class="string">&quot;age&quot;</span>: <span class="number">30</span>&#125;</span><br><span class="line"><span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">&quot;data.json&quot;</span>, <span class="string">&quot;w&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">    json.dump(data, f)</span><br></pre></td></tr></table></figure><p><strong>A</strong>：中文会被转义为 Unicode 编码（如 <code>\u5f20\u4e09</code>），可读性差。应添加 <code>ensure_ascii=False</code>：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">json.dump(data, f, ensure_ascii=<span class="literal">False</span>, indent=<span class="number">2</span>)</span><br></pre></td></tr></table></figure><hr><p><strong>Q13：以下代码能否正确读取大文件？为什么？</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">&quot;huge_file.txt&quot;</span>, <span class="string">&quot;r&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">    content = f.read()</span><br><span class="line"><span class="built_in">print</span>(content)</span><br></pre></td></tr></table></figure><p><strong>A</strong>：不能。<code>f.read()</code> 会一次性读取整个文件到内存，如果文件过大（如 GB 级别），会导致内存溢出。应使用逐行读取或分块读取：</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">&quot;huge_file.txt&quot;</span>, <span class="string">&quot;r&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">    <span class="keyword">for</span> line <span class="keyword">in</span> f:</span><br><span class="line">        process(line)</span><br></pre></td></tr></table></figure><hr><h2 id="11-实战项目：文件管理工具"><a href="#11-实战项目：文件管理工具" class="headerlink" title="11. 实战项目：文件管理工具"></a>11. 实战项目：文件管理工具</h2><h3 id="11-1-项目概述"><a href="#11-1-项目概述" class="headerlink" title="11.1 项目概述"></a>11.1 项目概述</h3><p><strong>项目目标</strong>：开发一个命令行文件管理工具 <code>filetool</code>，支持文件压缩、解压、搜索、批量重命名等功能。</p><p><strong>功能需求</strong>：</p><ul><li><code>filetool compress &lt;dir&gt;</code> - 压缩目录</li><li><code>filetool extract &lt;archive&gt;</code> - 解压归档</li><li><code>filetool search &lt;pattern&gt;</code> - 搜索文件</li><li><code>filetool rename &lt;pattern&gt; &lt;replace&gt;</code> - 批量重命名</li><li><code>filetool hash &lt;file&gt;</code> - 计算文件哈希</li></ul><h3 id="11-2-项目结构"><a href="#11-2-项目结构" class="headerlink" title="11.2 项目结构"></a>11.2 项目结构</h3><figure class="highlight plaintext"><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><span class="line">filetool/</span><br><span class="line">├── README.md</span><br><span class="line">├── pyproject.toml</span><br><span class="line">├── src/</span><br><span class="line">│   └── filetool/</span><br><span class="line">│       ├── __init__.py</span><br><span class="line">│       ├── __main__.py</span><br><span class="line">│       ├── cli.py</span><br><span class="line">│       ├── commands/</span><br><span class="line">│       │   ├── __init__.py</span><br><span class="line">│       │   ├── compress.py</span><br><span class="line">│       │   ├── extract.py</span><br><span class="line">│       │   ├── search.py</span><br><span class="line">│       │   ├── rename.py</span><br><span class="line">│       │   └── hash.py</span><br><span class="line">│       └── utils/</span><br><span class="line">│           ├── __init__.py</span><br><span class="line">│           └── helpers.py</span><br><span class="line">└── tests/</span><br><span class="line">    ├── __init__.py</span><br><span class="line">    └── test_filetool.py</span><br></pre></td></tr></table></figure><h3 id="11-3-核心代码"><a href="#11-3-核心代码" class="headerlink" title="11.3 核心代码"></a>11.3 核心代码</h3><p><strong>pyproject.toml</strong>：</p><figure class="highlight toml"><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><span class="line"><span class="section">[build-system]</span></span><br><span class="line"><span class="attr">requires</span> = [<span class="string">&quot;setuptools&gt;=61.0&quot;</span>, <span class="string">&quot;wheel&quot;</span>]</span><br><span class="line"><span class="attr">build-backend</span> = <span class="string">&quot;setuptools.build_meta&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="section">[project]</span></span><br><span class="line"><span class="attr">name</span> = <span class="string">&quot;filetool&quot;</span></span><br><span class="line"><span class="attr">version</span> = <span class="string">&quot;1.0.0&quot;</span></span><br><span class="line"><span class="attr">description</span> = <span class="string">&quot;A command-line file management tool&quot;</span></span><br><span class="line"><span class="attr">readme</span> = <span class="string">&quot;README.md&quot;</span></span><br><span class="line"><span class="attr">license</span> = &#123;text = <span class="string">&quot;MIT&quot;</span>&#125;</span><br><span class="line"><span class="attr">authors</span> = [</span><br><span class="line">    &#123;name = <span class="string">&quot;Your Name&quot;</span>, email = <span class="string">&quot;you@example.com&quot;</span>&#125;</span><br><span class="line">]</span><br><span class="line"><span class="attr">requires-python</span> = <span class="string">&quot;&gt;=3.9&quot;</span></span><br><span class="line"><span class="attr">dependencies</span> = [</span><br><span class="line">    <span class="string">&quot;click&gt;=8.0&quot;</span>,</span><br><span class="line">]</span><br><span class="line"></span><br><span class="line"><span class="section">[project.scripts]</span></span><br><span class="line"><span class="attr">filetool</span> = <span class="string">&quot;filetool.cli:main&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="section">[tool.setuptools.packages.find]</span></span><br><span class="line"><span class="attr">where</span> = [<span class="string">&quot;src&quot;</span>]</span><br></pre></td></tr></table></figure><p><strong>cli.py</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> click</span><br><span class="line"><span class="keyword">import</span> importlib</span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"></span><br><span class="line"><span class="meta">@click.group()</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">main</span>():</span><br><span class="line">    <span class="string">&quot;&quot;&quot;FileTool - 文件管理工具&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">_load_commands</span>():</span><br><span class="line">    commands_dir = os.path.join(os.path.dirname(__file__), <span class="string">&quot;commands&quot;</span>)</span><br><span class="line">    <span class="keyword">for</span> filename <span class="keyword">in</span> os.listdir(commands_dir):</span><br><span class="line">        <span class="keyword">if</span> filename.endswith(<span class="string">&quot;.py&quot;</span>) <span class="keyword">and</span> <span class="keyword">not</span> filename.startswith(<span class="string">&quot;_&quot;</span>):</span><br><span class="line">            module_name = filename[:-<span class="number">3</span>]</span><br><span class="line">            module = importlib.import_module(<span class="string">f&quot;filetool.commands.<span class="subst">&#123;module_name&#125;</span>&quot;</span>)</span><br><span class="line">            <span class="keyword">if</span> <span class="built_in">hasattr</span>(module, <span class="string">&quot;command&quot;</span>):</span><br><span class="line">                main.add_command(module.command)</span><br><span class="line"></span><br><span class="line">_load_commands()</span><br></pre></td></tr></table></figure><p><strong>commands&#x2F;compress.py</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> click</span><br><span class="line"><span class="keyword">import</span> tarfile</span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"></span><br><span class="line"><span class="meta">@click.command(<span class="params"><span class="string">&quot;compress&quot;</span></span>)</span></span><br><span class="line"><span class="meta">@click.argument(<span class="params"><span class="string">&quot;source&quot;</span></span>)</span></span><br><span class="line"><span class="meta">@click.option(<span class="params"><span class="string">&quot;-o&quot;</span>, <span class="string">&quot;--output&quot;</span>, <span class="built_in">help</span>=<span class="string">&quot;输出文件名&quot;</span></span>)</span></span><br><span class="line"><span class="meta">@click.option(<span class="params"><span class="string">&quot;-f&quot;</span>, <span class="string">&quot;--format&quot;</span>, default=<span class="string">&quot;gz&quot;</span>, <span class="built_in">type</span>=click.Choice(<span class="params">[<span class="string">&quot;gz&quot;</span>, <span class="string">&quot;bz2&quot;</span>, <span class="string">&quot;xz&quot;</span>]</span>)</span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">command</span>(<span class="params">source, output, <span class="built_in">format</span></span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;压缩目录或文件。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">if</span> output <span class="keyword">is</span> <span class="literal">None</span>:</span><br><span class="line">        output = <span class="string">f&quot;<span class="subst">&#123;os.path.basename(source)&#125;</span>.tar.<span class="subst">&#123;<span class="built_in">format</span>&#125;</span>&quot;</span></span><br><span class="line">    </span><br><span class="line">    mode = <span class="string">f&quot;w:<span class="subst">&#123;<span class="built_in">format</span>&#125;</span>&quot;</span></span><br><span class="line">    <span class="keyword">with</span> tarfile.<span class="built_in">open</span>(output, mode) <span class="keyword">as</span> tf:</span><br><span class="line">        tf.add(source, arcname=os.path.basename(source))</span><br><span class="line">    </span><br><span class="line">    click.echo(<span class="string">f&quot;压缩完成: <span class="subst">&#123;output&#125;</span>&quot;</span>)</span><br></pre></td></tr></table></figure><p><strong>commands&#x2F;search.py</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><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> click</span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"><span class="keyword">import</span> fnmatch</span><br><span class="line"></span><br><span class="line"><span class="meta">@click.command(<span class="params"><span class="string">&quot;search&quot;</span></span>)</span></span><br><span class="line"><span class="meta">@click.argument(<span class="params"><span class="string">&quot;pattern&quot;</span></span>)</span></span><br><span class="line"><span class="meta">@click.option(<span class="params"><span class="string">&quot;-d&quot;</span>, <span class="string">&quot;--directory&quot;</span>, default=<span class="string">&quot;.&quot;</span>, <span class="built_in">help</span>=<span class="string">&quot;搜索目录&quot;</span></span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">command</span>(<span class="params">pattern, directory</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;搜索匹配模式的文件。&quot;&quot;&quot;</span></span><br><span class="line">    matches = []</span><br><span class="line">    <span class="keyword">for</span> root, dirs, files <span class="keyword">in</span> os.walk(directory):</span><br><span class="line">        <span class="keyword">for</span> filename <span class="keyword">in</span> files:</span><br><span class="line">            <span class="keyword">if</span> fnmatch.fnmatch(filename, pattern):</span><br><span class="line">                matches.append(os.path.join(root, filename))</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">if</span> matches:</span><br><span class="line">        click.echo(<span class="string">f&quot;找到 <span class="subst">&#123;<span class="built_in">len</span>(matches)&#125;</span> 个文件:&quot;</span>)</span><br><span class="line">        <span class="keyword">for</span> filepath <span class="keyword">in</span> matches:</span><br><span class="line">            click.echo(<span class="string">f&quot;  <span class="subst">&#123;filepath&#125;</span>&quot;</span>)</span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        click.echo(<span class="string">&quot;未找到匹配的文件&quot;</span>)</span><br></pre></td></tr></table></figure><p><strong>commands&#x2F;rename.py</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><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><span class="line"><span class="keyword">import</span> click</span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"><span class="keyword">import</span> re</span><br><span class="line"></span><br><span class="line"><span class="meta">@click.command(<span class="params"><span class="string">&quot;rename&quot;</span></span>)</span></span><br><span class="line"><span class="meta">@click.argument(<span class="params"><span class="string">&quot;pattern&quot;</span></span>)</span></span><br><span class="line"><span class="meta">@click.argument(<span class="params"><span class="string">&quot;replace&quot;</span></span>)</span></span><br><span class="line"><span class="meta">@click.option(<span class="params"><span class="string">&quot;-d&quot;</span>, <span class="string">&quot;--directory&quot;</span>, default=<span class="string">&quot;.&quot;</span>, <span class="built_in">help</span>=<span class="string">&quot;操作目录&quot;</span></span>)</span></span><br><span class="line"><span class="meta">@click.option(<span class="params"><span class="string">&quot;-n&quot;</span>, <span class="string">&quot;--dry-run&quot;</span>, is_flag=<span class="literal">True</span>, <span class="built_in">help</span>=<span class="string">&quot;模拟运行，不实际修改&quot;</span></span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">command</span>(<span class="params">pattern, replace, directory, dry_run</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;批量重命名文件。&quot;&quot;&quot;</span></span><br><span class="line">    count = <span class="number">0</span></span><br><span class="line">    <span class="keyword">for</span> filename <span class="keyword">in</span> os.listdir(directory):</span><br><span class="line">        new_name = re.sub(pattern, replace, filename)</span><br><span class="line">        <span class="keyword">if</span> new_name != filename:</span><br><span class="line">            old_path = os.path.join(directory, filename)</span><br><span class="line">            new_path = os.path.join(directory, new_name)</span><br><span class="line">            </span><br><span class="line">            <span class="keyword">if</span> dry_run:</span><br><span class="line">                click.echo(<span class="string">f&quot;[模拟] <span class="subst">&#123;filename&#125;</span> -&gt; <span class="subst">&#123;new_name&#125;</span>&quot;</span>)</span><br><span class="line">            <span class="keyword">else</span>:</span><br><span class="line">                os.rename(old_path, new_path)</span><br><span class="line">                click.echo(<span class="string">f&quot;<span class="subst">&#123;filename&#125;</span> -&gt; <span class="subst">&#123;new_name&#125;</span>&quot;</span>)</span><br><span class="line">            count += <span class="number">1</span></span><br><span class="line">    </span><br><span class="line">    click.echo(<span class="string">f&quot;共重命名 <span class="subst">&#123;count&#125;</span> 个文件&quot;</span>)</span><br></pre></td></tr></table></figure><p><strong>commands&#x2F;hash.py</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> click</span><br><span class="line"><span class="keyword">import</span> hashlib</span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"></span><br><span class="line"><span class="meta">@click.command(<span class="params"><span class="string">&quot;hash&quot;</span></span>)</span></span><br><span class="line"><span class="meta">@click.argument(<span class="params"><span class="string">&quot;file&quot;</span></span>)</span></span><br><span class="line"><span class="meta">@click.option(<span class="params"><span class="string">&quot;-a&quot;</span>, <span class="string">&quot;--algorithm&quot;</span>, default=<span class="string">&quot;md5&quot;</span>, <span class="built_in">type</span>=click.Choice(<span class="params">[<span class="string">&quot;md5&quot;</span>, <span class="string">&quot;sha256&quot;</span>]</span>)</span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">command</span>(<span class="params">file, algorithm</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;计算文件哈希值。&quot;&quot;&quot;</span></span><br><span class="line">    hash_obj = hashlib.new(algorithm)</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">with</span> <span class="built_in">open</span>(file, <span class="string">&quot;rb&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">        <span class="keyword">for</span> chunk <span class="keyword">in</span> <span class="built_in">iter</span>(<span class="keyword">lambda</span>: f.read(<span class="number">8192</span>), <span class="string">b&quot;&quot;</span>):</span><br><span class="line">            hash_obj.update(chunk)</span><br><span class="line">    </span><br><span class="line">    click.echo(<span class="string">f&quot;<span class="subst">&#123;algorithm.upper()&#125;</span>: <span class="subst">&#123;hash_obj.hexdigest()&#125;</span>&quot;</span>)</span><br></pre></td></tr></table></figure><p><strong>utils&#x2F;helpers.py</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> os</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">validate_path</span>(<span class="params">path</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;验证路径是否存在。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> os.path.exists(path):</span><br><span class="line">        <span class="keyword">raise</span> FileNotFoundError(<span class="string">f&quot;路径不存在: <span class="subst">&#123;path&#125;</span>&quot;</span>)</span><br><span class="line">    <span class="keyword">return</span> path</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">get_file_size</span>(<span class="params">path</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;获取文件大小（人类可读格式）。&quot;&quot;&quot;</span></span><br><span class="line">    size = os.path.getsize(path)</span><br><span class="line">    <span class="keyword">for</span> unit <span class="keyword">in</span> [<span class="string">&quot;B&quot;</span>, <span class="string">&quot;KB&quot;</span>, <span class="string">&quot;MB&quot;</span>, <span class="string">&quot;GB&quot;</span>]:</span><br><span class="line">        <span class="keyword">if</span> size &lt; <span class="number">1024</span>:</span><br><span class="line">            <span class="keyword">return</span> <span class="string">f&quot;<span class="subst">&#123;size:<span class="number">.2</span>f&#125;</span> <span class="subst">&#123;unit&#125;</span>&quot;</span></span><br><span class="line">        size /= <span class="number">1024</span></span><br><span class="line">    <span class="keyword">return</span> <span class="string">f&quot;<span class="subst">&#123;size:<span class="number">.2</span>f&#125;</span> TB&quot;</span></span><br></pre></td></tr></table></figure><h3 id="11-4-项目运行"><a href="#11-4-项目运行" class="headerlink" title="11.4 项目运行"></a>11.4 项目运行</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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 开发模式安装</span></span><br><span class="line">pip install -e .</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用命令</span></span><br><span class="line">filetool compress project/          <span class="comment"># 压缩目录</span></span><br><span class="line">filetool extract archive.tar.gz    <span class="comment"># 解压归档</span></span><br><span class="line">filetool search <span class="string">&quot;*.py&quot;</span>             <span class="comment"># 搜索 Python 文件</span></span><br><span class="line">filetool rename <span class="string">&quot;(\d+)-&quot;</span> <span class="string">&quot;&quot;</span>        <span class="comment"># 批量重命名（去掉数字前缀）</span></span><br><span class="line">filetool <span class="built_in">hash</span> data.zip             <span class="comment"># 计算文件哈希</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 或者使用 python -m</span></span><br><span class="line">python -m filetool <span class="built_in">hash</span> data.zip</span><br></pre></td></tr></table></figure><h3 id="11-5-项目亮点"><a href="#11-5-项目亮点" class="headerlink" title="11.5 项目亮点"></a>11.5 项目亮点</h3><ol><li><strong>功能丰富</strong>：覆盖文件压缩、解压、搜索、重命名、哈希计算等常用操作</li><li><strong>模块化设计</strong>：每个命令独立模块，易于扩展</li><li><strong>安全可靠</strong>：批量操作支持 dry-run 模式，避免误操作</li><li><strong>用户友好</strong>：使用 click 库提供丰富的命令行参数和帮助信息</li></ol><hr><h2 id="附录：第六阶段自检清单"><a href="#附录：第六阶段自检清单" class="headerlink" title="附录：第六阶段自检清单"></a>附录：第六阶段自检清单</h2><p>在继续学习第七阶段之前，请确保你能：</p><ul><li><input disabled="" type="checkbox"> 使用 <code>with</code> 语句正确打开和关闭文件，并解释其原理</li><li><input disabled="" type="checkbox"> 区分文本模式和二进制模式，知道何时使用哪种</li><li><input disabled="" type="checkbox"> 使用 <code>pathlib</code> 完成文件路径的拼接、解析、遍历等操作</li><li><input disabled="" type="checkbox"> 用 <code>json</code> 序列化&#x2F;反序列化 Python 对象，处理中文和自定义类型</li><li><input disabled="" type="checkbox"> 解释 JSON 和 Pickle 的区别，以及各自的安全注意事项</li><li><input disabled="" type="checkbox"> 使用 <code>csv.DictReader</code> 和 <code>csv.DictWriter</code> 读写 CSV</li><li><input disabled="" type="checkbox"> 使用 <code>zipfile</code>、<code>tarfile</code>、<code>gzip</code> 进行压缩和解压</li><li><input disabled="" type="checkbox"> 使用 <code>StringIO</code> &#x2F; <code>BytesIO</code> 在内存中处理流数据</li><li><input disabled="" type="checkbox"> 实现大文件的分块读取，避免内存溢出</li><li><input disabled="" type="checkbox"> 使用临时文件和临时目录，并了解自动清理机制</li><li><input disabled="" type="checkbox"> 实现文件的原子写入操作</li><li><input disabled="" type="checkbox"> 使用 <code>sqlite3</code> 进行基本的数据库操作（增删改查）</li></ul><hr><blockquote><p><strong>工程师寄语</strong>：文件 I&#x2F;O 是最容易出问题的领域——编码错误、路径分隔符、并发写入、磁盘满……永远假设文件操作会失败，并做好错误处理。<code>pathlib</code> 让路径操作从”字符串拼接”变成了”对象操作”，这是 Python 3 带来的最大生产力提升之一。</p></blockquote>]]>
    </content>
    <id>https://itdxb.cn/2026/07/23/python/document/%E3%80%90Python%E3%80%91%E6%96%87%E4%BB%B6IO%E4%B8%8E%E5%BA%8F%E5%88%97%E5%8C%96%EF%BC%88%E7%AC%AC%E5%85%AD%E9%98%B6%E6%AE%B5%EF%BC%89/</id>
    <link href="https://itdxb.cn/2026/07/23/python/document/%E3%80%90Python%E3%80%91%E6%96%87%E4%BB%B6IO%E4%B8%8E%E5%BA%8F%E5%88%97%E5%8C%96%EF%BC%88%E7%AC%AC%E5%85%AD%E9%98%B6%E6%AE%B5%EF%BC%89/"/>
    <published>2026-07-23T02:00:00.000Z</published>
    <summary>系统掌握 Python 文件读写、pathlib、上下文管理器、JSON/Pickle/CSV/XML 序列化、压缩归档和工程实践。</summary>
    <title>【Python】文件I/O与序列化（第六阶段）</title>
    <updated>2026-07-24T05:42:50.239Z</updated>
  </entry>
  <entry>
    <author>
      <name>Super Bing</name>
    </author>
    <category term="Python" scheme="https://itdxb.cn/categories/Python/"/>
    <category term="教程" scheme="https://itdxb.cn/tags/%E6%95%99%E7%A8%8B/"/>
    <category term="Python" scheme="https://itdxb.cn/tags/Python/"/>
    <category term="模块" scheme="https://itdxb.cn/tags/%E6%A8%A1%E5%9D%97/"/>
    <category term="项目结构" scheme="https://itdxb.cn/tags/%E9%A1%B9%E7%9B%AE%E7%BB%93%E6%9E%84/"/>
    <content>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><h1 id="五、模块、包与项目结构"><a href="#五、模块、包与项目结构" class="headerlink" title="五、模块、包与项目结构"></a>五、模块、包与项目结构</h1><blockquote><p><strong>阶段定位</strong>：模块和包是 Python 代码的组织单元。写好模块不仅是技术问题，更是工程素养的体现。本阶段从 <code>import</code> 机制出发，讲解如何组织出可维护、可复用的项目结构。</p></blockquote><figure class="highlight plaintext"><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><span class="line">┌─────────────────────────────────────────────────────────────────┐</span><br><span class="line">│                   模块、包与项目结构知识图谱                       │</span><br><span class="line">├─────────────────────────────────────────────────────────────────┤</span><br><span class="line">│                                                                 │</span><br><span class="line">│  1. 模块 ──────→ import / sys.path / __name__ / 重载             │</span><br><span class="line">│       │                                                         │</span><br><span class="line">│  2. 包 ────────→ __init__.py / 绝对导入 / 相对导入 / 命名空间包     │</span><br><span class="line">│       │                                                         │</span><br><span class="line">│  3. 项目结构 ──→ src layout / pyproject.toml / README / tests     │</span><br><span class="line">│       │                                                         │</span><br><span class="line">│  4. 循环导入 ──→ 重构 / 延迟导入 / 接口抽象                        │</span><br><span class="line">│       │                                                         │</span><br><span class="line">│  5. 工程实践 ──→ import * / 副作用 / 版本管理 / zipapp             │</span><br><span class="line">│                                                                 │</span><br><span class="line">└─────────────────────────────────────────────────────────────────┘</span><br></pre></td></tr></table></figure><hr><h2 id="1-模块（Module）"><a href="#1-模块（Module）" class="headerlink" title="1. 模块（Module）"></a>1. 模块（Module）</h2><h3 id="1-1-什么是模块"><a href="#1-1-什么是模块" class="headerlink" title="1.1 什么是模块"></a>1.1 什么是模块</h3><p>一个 <code>.py</code> 文件就是一个模块。模块是 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><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><span class="line"><span class="comment"># math_utils.py</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;数学工具模块。&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">PI = <span class="number">3.14159265359</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Circle</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, radius</span>):</span><br><span class="line">        <span class="variable language_">self</span>.radius = radius</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">area</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> PI * <span class="variable language_">self</span>.radius ** <span class="number">2</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">add</span>(<span class="params">a, b</span>):</span><br><span class="line">    <span class="keyword">return</span> a + b</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">multiply</span>(<span class="params">a, b</span>):</span><br><span class="line">    <span class="keyword">return</span> a * b</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用</span></span><br><span class="line"><span class="keyword">import</span> math_utils</span><br><span class="line"><span class="built_in">print</span>(math_utils.add(<span class="number">1</span>, <span class="number">2</span>))</span><br><span class="line"><span class="built_in">print</span>(math_utils.PI)</span><br><span class="line">c = math_utils.Circle(<span class="number">5</span>)</span><br></pre></td></tr></table></figure><p><strong>模块的本质</strong>：模块是一个包含 Python 定义和语句的文件，文件名就是模块名（去掉 <code>.py</code>）。导入模块时，Python 会执行文件中的所有顶级语句，并将结果存储在模块对象中。</p><h3 id="1-2-导入机制"><a href="#1-2-导入机制" class="headerlink" title="1.2 导入机制"></a>1.2 导入机制</h3><h4 id="导入方式对比"><a href="#导入方式对比" class="headerlink" title="导入方式对比"></a>导入方式对比</h4><table><thead><tr><th>方式</th><th>示例</th><th>适用场景</th><th>缺点</th></tr></thead><tbody><tr><td><code>import</code></td><td><code>import os</code></td><td>标准库、长模块名</td><td>每次使用需写前缀</td></tr><tr><td><code>from ... import</code></td><td><code>from os import path</code></td><td>特定函数&#x2F;类</td><td>命名空间污染</td></tr><tr><td><code>from ... import *</code></td><td><code>from math import *</code></td><td>交互式&#x2F;小脚本</td><td>严重污染命名空间</td></tr><tr><td><code>as</code> 别名</td><td><code>import numpy as np</code></td><td>长模块名简化</td><td>团队需统一别名</td></tr></tbody></table><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><span class="line"><span class="comment"># 推荐写法</span></span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"><span class="keyword">from</span> pathlib <span class="keyword">import</span> Path</span><br><span class="line"><span class="keyword">import</span> requests</span><br><span class="line"><span class="keyword">import</span> pandas <span class="keyword">as</span> pd</span><br><span class="line"></span><br><span class="line"><span class="comment"># 不推荐</span></span><br><span class="line"><span class="keyword">from</span> os <span class="keyword">import</span> *           <span class="comment"># 你不知道导入了什么</span></span><br><span class="line"><span class="keyword">from</span> module <span class="keyword">import</span> a, b, c, d, e, f  <span class="comment"># 太多，用 import module 更清楚</span></span><br></pre></td></tr></table></figure><p><strong>工程建议</strong>：</p><ul><li>标准库：<code>import os</code>（不简写）</li><li>第三方库：<code>import numpy as np</code>（遵循社区约定）</li><li>自定义模块：<code>from myproject.utils import helper</code>（明确具体对象）</li></ul><h3 id="1-3-模块搜索路径-sys-path"><a href="#1-3-模块搜索路径-sys-path" class="headerlink" title="1.3 模块搜索路径 sys.path"></a>1.3 模块搜索路径 <code>sys.path</code></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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> sys</span><br><span class="line"><span class="built_in">print</span>(sys.path)</span><br><span class="line"><span class="comment"># [&#x27;&#x27;, &#x27;/usr/lib/python311.zip&#x27;, &#x27;/usr/lib/python3.11&#x27;, ...]</span></span><br><span class="line"><span class="comment"># 空字符串 &#x27;&#x27; 表示当前目录</span></span><br></pre></td></tr></table></figure><p><strong>搜索顺序</strong>：</p><ol><li>当前目录（运行脚本所在目录）</li><li><code>PYTHONPATH</code> 环境变量中的目录</li><li>标准库目录</li><li><code>.pth</code> 文件指定的目录</li></ol><p><strong>工程建议</strong>：不要直接修改 <code>sys.path</code>。正确做法：</p><ul><li>开发时：在虚拟环境中 <code>pip install -e .</code>（可编辑安装）</li><li>运行时：使用 <code>PYTHONPATH</code> 环境变量</li></ul><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 正确设置 PYTHONPATH</span></span><br><span class="line"><span class="built_in">export</span> PYTHONPATH=<span class="string">&quot;/path/to/your/project:<span class="variable">$PYTHONPATH</span>&quot;</span></span><br><span class="line">python -m mypackage.module</span><br></pre></td></tr></table></figure><blockquote><p><strong>易错点</strong>：<code>sys.path</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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 在 /home/user/scripts 目录下运行</span></span><br><span class="line">python /home/user/app/main.py</span><br><span class="line"><span class="comment"># sys.path[0] 是 /home/user/app（main.py 所在目录），不是 /home/user/scripts</span></span><br></pre></td></tr></table></figure></blockquote><h3 id="1-4-if-name-main"><a href="#1-4-if-name-main" class="headerlink" title="1.4 if __name__ == &quot;__main__&quot;"></a>1.4 <code>if __name__ == &quot;__main__&quot;</code></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><span class="line"><span class="comment"># utils.py</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">helper</span>():</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;I&#x27;m a helper&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 测试/演示代码，仅在直接运行文件时执行</span></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;直接运行 utils.py&quot;</span>)</span><br><span class="line">    <span class="built_in">print</span>(helper())</span><br><span class="line">    <span class="comment"># 可以写单元测试、性能测试、CLI 入口等</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># import utils 时，__name__ == &quot;utils&quot;，上面的代码不会执行</span></span><br></pre></td></tr></table></figure><p><strong><code>__name__</code> 的值</strong>：</p><table><thead><tr><th>运行方式</th><th><code>__name__</code></th></tr></thead><tbody><tr><td>直接运行文件</td><td><code>&quot;__main__&quot;</code></td></tr><tr><td>被导入时</td><td>模块名（如 <code>&quot;utils&quot;</code>）</td></tr></tbody></table><p><strong>工程建议</strong>：每个模块都应该有 <code>if __name__ == &quot;__main__&quot;:</code> 块，用于：</p><ul><li>快速测试模块功能</li><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 更工程化的做法：使用 argparse 提供 CLI</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">main</span>():</span><br><span class="line">    <span class="keyword">import</span> argparse</span><br><span class="line">    parser = argparse.ArgumentParser()</span><br><span class="line">    parser.add_argument(<span class="string">&quot;--input&quot;</span>, required=<span class="literal">True</span>)</span><br><span class="line">    args = parser.parse_args()</span><br><span class="line">    process(args.<span class="built_in">input</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    main()</span><br></pre></td></tr></table></figure><h3 id="1-5-模块重载"><a href="#1-5-模块重载" class="headerlink" title="1.5 模块重载"></a>1.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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> importlib</span><br><span class="line"><span class="keyword">import</span> my_module</span><br><span class="line"></span><br><span class="line"><span class="comment"># 修改 my_module.py 后重新加载</span></span><br><span class="line">importlib.reload(my_module)</span><br></pre></td></tr></table></figure><p><strong>工程建议</strong>：生产环境不要依赖模块重载，它可能导致状态不一致。仅在开发&#x2F;调试时使用。</p><hr><h2 id="2-包（Package）"><a href="#2-包（Package）" class="headerlink" title="2. 包（Package）"></a>2. 包（Package）</h2><h3 id="2-1-包的基本结构"><a href="#2-1-包的基本结构" class="headerlink" title="2.1 包的基本结构"></a>2.1 包的基本结构</h3><p>包是包含 <code>__init__.py</code> 的目录（Python 3.3+ 也支持隐式命名空间包，但显式更好）。</p><figure class="highlight plaintext"><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><span class="line">myproject/</span><br><span class="line">├── __init__.py          # 包初始化，控制导出内容</span><br><span class="line">├── core/</span><br><span class="line">│   ├── __init__.py</span><br><span class="line">│   ├── models.py        # 数据模型</span><br><span class="line">│   └── services.py      # 业务逻辑</span><br><span class="line">├── utils/</span><br><span class="line">│   ├── __init__.py</span><br><span class="line">│   ├── helpers.py</span><br><span class="line">│   └── validators.py</span><br><span class="line">└── cli.py               # 命令行入口</span><br></pre></td></tr></table></figure><h3 id="2-2-init-py-的作用"><a href="#2-2-init-py-的作用" class="headerlink" title="2.2 __init__.py 的作用"></a>2.2 <code>__init__.py</code> 的作用</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><span class="line"><span class="comment"># myproject/__init__.py</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;MyProject - 一个示例项目。&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">__version__ = <span class="string">&quot;1.0.0&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 控制 from myproject import * 时导出的内容</span></span><br><span class="line">__all__ = [<span class="string">&quot;CoreService&quot;</span>, <span class="string">&quot;setup_logging&quot;</span>]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 简化导入路径</span></span><br><span class="line"><span class="keyword">from</span> myproject.core.services <span class="keyword">import</span> CoreService</span><br><span class="line"><span class="keyword">from</span> myproject.utils.helpers <span class="keyword">import</span> setup_logging</span><br><span class="line"></span><br><span class="line"><span class="comment"># 现在可以：</span></span><br><span class="line"><span class="comment"># from myproject import CoreService</span></span><br><span class="line"><span class="comment"># 而不用：</span></span><br><span class="line"><span class="comment"># from myproject.core.services import CoreService</span></span><br></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></pre></td><td class="code"><pre><span class="line"><span class="comment"># myproject/utils/__init__.py</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;工具包。&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> .helpers <span class="keyword">import</span> format_date, parse_json</span><br><span class="line"><span class="keyword">from</span> .validators <span class="keyword">import</span> validate_email, validate_phone</span><br><span class="line"></span><br><span class="line">__all__ = [<span class="string">&quot;format_date&quot;</span>, <span class="string">&quot;parse_json&quot;</span>, <span class="string">&quot;validate_email&quot;</span>, <span class="string">&quot;validate_phone&quot;</span>]</span><br></pre></td></tr></table></figure><p><strong><code>__init__.py</code> 的多重角色</strong>：</p><figure class="highlight plaintext"><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><span class="line">┌─────────────────────────────────────────────────────────┐</span><br><span class="line">│                  __init__.py 的多重角色                  │</span><br><span class="line">├─────────────────────────────────────────────────────────┤</span><br><span class="line">│                                                         │</span><br><span class="line">│  1. 标记目录为 Python 包                                 │</span><br><span class="line">│                                                         │</span><br><span class="line">│  2. 包级初始化代码（只执行一次）                          │</span><br><span class="line">│     如：注册信号、初始化数据库连接池                       │</span><br><span class="line">│                                                         │</span><br><span class="line">│  3. 控制 `from package import *` 的导出内容              │</span><br><span class="line">│     __all__ = [&#x27;func1&#x27;, &#x27;Class2&#x27;]                       │</span><br><span class="line">│                                                         │</span><br><span class="line">│  4. 简化导入路径                                         │</span><br><span class="line">│     from .module import func  →  from package import func│</span><br><span class="line">│                                                         │</span><br><span class="line">│  5. 定义包级变量                                         │</span><br><span class="line">│     __version__ = &quot;1.0.0&quot;                               │</span><br><span class="line">│                                                         │</span><br><span class="line">└─────────────────────────────────────────────────────────┘</span><br></pre></td></tr></table></figure><h3 id="2-3-绝对导入与相对导入"><a href="#2-3-绝对导入与相对导入" class="headerlink" title="2.3 绝对导入与相对导入"></a>2.3 绝对导入与相对导入</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></pre></td><td class="code"><pre><span class="line"><span class="comment"># myproject/core/services.py</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 绝对导入（推荐，清晰明确）</span></span><br><span class="line"><span class="keyword">from</span> myproject.utils.helpers <span class="keyword">import</span> format_date</span><br><span class="line"><span class="keyword">from</span> myproject.core.models <span class="keyword">import</span> User</span><br><span class="line"></span><br><span class="line"><span class="comment"># 相对导入（适用于包内部，重构时更灵活）</span></span><br><span class="line"><span class="keyword">from</span> ..utils.helpers <span class="keyword">import</span> format_date   <span class="comment"># 上级目录的 utils</span></span><br><span class="line"><span class="keyword">from</span> .models <span class="keyword">import</span> User                   <span class="comment"># 同级目录的 models</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 混合使用（不推荐）</span></span><br><span class="line"><span class="keyword">import</span> sys</span><br><span class="line">sys.path.insert(<span class="number">0</span>, <span class="string">&quot;..&quot;</span>)   <span class="comment"># 不要这样做！</span></span><br><span class="line"><span class="keyword">import</span> helpers              <span class="comment"># 难以追踪依赖</span></span><br></pre></td></tr></table></figure><p><strong>工程建议</strong>：</p><ul><li>项目顶层用<strong>绝对导入</strong></li><li>包内部可用<strong>相对导入</strong>（<code>.module</code>），但深度不要超过两级</li><li>永远不要使用 <code>sys.path.insert</code> 解决导入问题</li></ul><blockquote><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><span class="line"><span class="comment"># services.py 中有：from .models import User</span></span><br><span class="line"><span class="comment"># 直接运行 python services.py 会报错：</span></span><br><span class="line"><span class="comment"># ImportError: attempted relative import with no known parent package</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 正确运行方式：</span></span><br><span class="line"><span class="comment"># cd myproject 上级目录</span></span><br><span class="line"><span class="comment"># python -m myproject.core.services</span></span><br></pre></td></tr></table></figure></blockquote><h3 id="2-4-命名空间包（Namespace-Package）"><a href="#2-4-命名空间包（Namespace-Package）" class="headerlink" title="2.4 命名空间包（Namespace Package）"></a>2.4 命名空间包（Namespace Package）</h3><p>Python 3.3+ 支持隐式命名空间包（没有 <code>__init__.py</code>），用于将一个大包拆分到多个目录。</p><figure class="highlight plaintext"><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><span class="line"># 场景：公司多个团队维护不同插件</span><br><span class="line"># team-a 的包</span><br><span class="line">team_plugins/</span><br><span class="line">├── feature_a/</span><br><span class="line">│   └── plugin.py</span><br><span class="line"></span><br><span class="line"># team-b 的包</span><br><span class="line">team_plugins/</span><br><span class="line">├── feature_b/</span><br><span class="line">│   └── plugin.py</span><br><span class="line"></span><br><span class="line"># 两个目录都可以导入</span><br><span class="line">from team_plugins.feature_a import plugin</span><br><span class="line">from team_plugins.feature_b import plugin</span><br></pre></td></tr></table></figure><p><strong>工程建议</strong>：除非有特殊需求（如插件系统），否则始终使用显式 <code>__init__.py</code>。隐式命名空间包在 IDE 支持、类型检查等方面有局限。</p><hr><h2 id="3-项目结构规范"><a href="#3-项目结构规范" class="headerlink" title="3. 项目结构规范"></a>3. 项目结构规范</h2><h3 id="3-1-推荐的项目目录结构"><a href="#3-1-推荐的项目目录结构" class="headerlink" title="3.1 推荐的项目目录结构"></a>3.1 推荐的项目目录结构</h3><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">myproject/                       # 项目根目录</span><br><span class="line">├── README.md                    # 项目说明</span><br><span class="line">├── LICENSE                      # 许可证</span><br><span class="line">├── pyproject.toml               # 项目配置（推荐）</span><br><span class="line">├── setup.py / setup.cfg         # 打包配置（旧方式）</span><br><span class="line">├── requirements.txt             # 运行依赖</span><br><span class="line">├── requirements-dev.txt         # 开发依赖</span><br><span class="line">├── .gitignore                   # Git 忽略规则</span><br><span class="line">├── .editorconfig                # 编辑器配置</span><br><span class="line">├── .pre-commit-config.yaml      # 提交前检查</span><br><span class="line">├── docs/                        # 文档</span><br><span class="line">│   ├── index.md</span><br><span class="line">│   └── api.md</span><br><span class="line">├── src/                         # 源代码（推荐用 src 布局）</span><br><span class="line">│   └── myproject/               # 主包</span><br><span class="line">│       ├── __init__.py</span><br><span class="line">│       ├── __main__.py          # python -m myproject 入口</span><br><span class="line">│       ├── core/</span><br><span class="line">│       │   ├── __init__.py</span><br><span class="line">│       │   ├── models.py</span><br><span class="line">│       │   └── services.py</span><br><span class="line">│       ├── api/</span><br><span class="line">│       │   ├── __init__.py</span><br><span class="line">│       │   └── routes.py</span><br><span class="line">│       ├── db/</span><br><span class="line">│       │   ├── __init__.py</span><br><span class="line">│       │   └── connection.py</span><br><span class="line">│       ├── utils/</span><br><span class="line">│       │   ├── __init__.py</span><br><span class="line">│       │   ├── helpers.py</span><br><span class="line">│       │   └── validators.py</span><br><span class="line">│       └── config.py            # 配置管理</span><br><span class="line">├── tests/                       # 测试</span><br><span class="line">│   ├── __init__.py</span><br><span class="line">│   ├── conftest.py              # pytest 共享 fixture</span><br><span class="line">│   ├── unit/                    # 单元测试</span><br><span class="line">│   │   ├── test_models.py</span><br><span class="line">│   │   └── test_services.py</span><br><span class="line">│   ├── integration/             # 集成测试</span><br><span class="line">│   │   └── test_api.py</span><br><span class="line">│   └── fixtures/                # 测试数据</span><br><span class="line">│       └── users.json</span><br><span class="line">├── scripts/                     # 工具脚本</span><br><span class="line">│   ├── setup_db.py</span><br><span class="line">│   └── seed_data.py</span><br><span class="line">└── .venv/                       # 虚拟环境（.gitignore）</span><br></pre></td></tr></table></figure><p><strong>Flat Layout vs Src Layout</strong>：</p><table><thead><tr><th>布局</th><th>结构</th><th>优点</th><th>缺点</th></tr></thead><tbody><tr><td>Flat</td><td><code>myproject/</code> 直接在根目录</td><td>简单直观</td><td>容易意外导入本地代码而非安装包</td></tr><tr><td>Src</td><td><code>src/myproject/</code></td><td>强制安装后测试，避免路径混乱</td><td>路径稍深</td></tr></tbody></table><p><strong>推荐</strong>：新项目一律使用 <strong>src layout</strong>。</p><blockquote><p><strong>面试真题</strong>：为什么推荐 <code>src</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></pre></td><td class="code"><pre><span class="line"><span class="comment"># Flat 布局的问题：</span></span><br><span class="line"><span class="comment"># 在项目根目录运行 pytest 时，Python 会导入根目录下的 myproject</span></span><br><span class="line"><span class="comment"># 而不是 pip install -e . 安装到 site-packages 的版本</span></span><br><span class="line"><span class="comment"># 这可能导致&quot;安装后测试&quot;和&quot;开发时测试&quot;结果不一致</span></span><br></pre></td></tr></table></figure><p><strong>答案</strong>：<code>src</code> 布局确保测试的是安装后的包，而不是开发目录下的代码，避免路径混乱。</p></blockquote><h3 id="3-2-main-py-的作用"><a href="#3-2-main-py-的作用" class="headerlink" title="3.2 __main__.py 的作用"></a>3.2 <code>__main__.py</code> 的作用</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><span class="line"><span class="comment"># src/myproject/__main__.py</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;python -m myproject 的入口。&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> myproject.cli <span class="keyword">import</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    main()</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 这样运行项目</span></span><br><span class="line">python -m myproject</span><br><span class="line"></span><br><span class="line"><span class="comment"># 而不是</span></span><br><span class="line">python src/myproject/cli.py   <span class="comment"># 不推荐，sys.path 可能有问题</span></span><br></pre></td></tr></table></figure><h3 id="3-3-配置文件-pyproject-toml"><a href="#3-3-配置文件-pyproject-toml" class="headerlink" title="3.3 配置文件 pyproject.toml"></a>3.3 配置文件 <code>pyproject.toml</code></h3><figure class="highlight toml"><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><span class="line"><span class="section">[build-system]</span></span><br><span class="line"><span class="attr">requires</span> = [<span class="string">&quot;setuptools&gt;=61.0&quot;</span>, <span class="string">&quot;wheel&quot;</span>]</span><br><span class="line"><span class="attr">build-backend</span> = <span class="string">&quot;setuptools.build_meta&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="section">[project]</span></span><br><span class="line"><span class="attr">name</span> = <span class="string">&quot;myproject&quot;</span></span><br><span class="line"><span class="attr">version</span> = <span class="string">&quot;1.0.0&quot;</span></span><br><span class="line"><span class="attr">description</span> = <span class="string">&quot;一个示例项目&quot;</span></span><br><span class="line"><span class="attr">readme</span> = <span class="string">&quot;README.md&quot;</span></span><br><span class="line"><span class="attr">license</span> = &#123;text = <span class="string">&quot;MIT&quot;</span>&#125;</span><br><span class="line"><span class="attr">authors</span> = [</span><br><span class="line">    &#123;name = <span class="string">&quot;Your Name&quot;</span>, email = <span class="string">&quot;you@example.com&quot;</span>&#125;</span><br><span class="line">]</span><br><span class="line"><span class="attr">requires-python</span> = <span class="string">&quot;&gt;=3.9&quot;</span></span><br><span class="line"><span class="attr">dependencies</span> = [</span><br><span class="line">    <span class="string">&quot;requests&gt;=2.28.0&quot;</span>,</span><br><span class="line">    <span class="string">&quot;pydantic&gt;=2.0&quot;</span>,</span><br><span class="line">]</span><br><span class="line"></span><br><span class="line"><span class="section">[project.optional-dependencies]</span></span><br><span class="line"><span class="attr">dev</span> = [</span><br><span class="line">    <span class="string">&quot;pytest&gt;=7.0&quot;</span>,</span><br><span class="line">    <span class="string">&quot;pytest-cov&quot;</span>,</span><br><span class="line">    <span class="string">&quot;black&quot;</span>,</span><br><span class="line">    <span class="string">&quot;flake8&quot;</span>,</span><br><span class="line">    <span class="string">&quot;mypy&quot;</span>,</span><br><span class="line">]</span><br><span class="line"></span><br><span class="line"><span class="section">[project.scripts]</span></span><br><span class="line"><span class="attr">myproject</span> = <span class="string">&quot;myproject.cli:main&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="section">[tool.setuptools.packages.find]</span></span><br><span class="line"><span class="attr">where</span> = [<span class="string">&quot;src&quot;</span>]</span><br><span class="line"></span><br><span class="line"><span class="section">[tool.black]</span></span><br><span class="line"><span class="attr">line-length</span> = <span class="number">88</span></span><br><span class="line"><span class="attr">target-version</span> = [<span class="string">&#x27;py39&#x27;</span>]</span><br><span class="line"></span><br><span class="line"><span class="section">[tool.mypy]</span></span><br><span class="line"><span class="attr">python_version</span> = <span class="string">&quot;3.9&quot;</span></span><br><span class="line"><span class="attr">warn_return_any</span> = <span class="literal">true</span></span><br><span class="line"><span class="attr">warn_unused_configs</span> = <span class="literal">true</span></span><br></pre></td></tr></table></figure><p><strong>pyproject.toml 的优势</strong>：</p><figure class="highlight plaintext"><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><span class="line">┌─────────────────────────────────────────────────────────┐</span><br><span class="line">│                  pyproject.toml 优势                     │</span><br><span class="line">├─────────────────────────────────────────────────────────┤</span><br><span class="line">│                                                         │</span><br><span class="line">│  1. 统一配置（PEP 621）                                  │</span><br><span class="line">│     项目元数据 + 依赖 + 工具配置 都在一个文件              │</span><br><span class="line">│                                                         │</span><br><span class="line">│  2. 取代 setup.py / setup.cfg / requirements.txt         │</span><br><span class="line">│     减少配置文件数量                                     │</span><br><span class="line">│                                                         │</span><br><span class="line">│  3. 工具独立配置区                                       │</span><br><span class="line">│     [tool.black] / [tool.mypy] / [tool.pytest.ini_options]│</span><br><span class="line">│     每个工具有自己的命名空间                             │</span><br><span class="line">│                                                         │</span><br><span class="line">│  4. 可编辑安装                                           │</span><br><span class="line">│     pip install -e .  # 开发模式安装                     │</span><br><span class="line">│                                                         │</span><br><span class="line">│  5. 脚本入口                                             │</span><br><span class="line">│     [project.scripts] 定义命令行入口                     │</span><br><span class="line">│                                                         │</span><br><span class="line">└─────────────────────────────────────────────────────────┘</span><br></pre></td></tr></table></figure><hr><h2 id="4-循环导入问题"><a href="#4-循环导入问题" class="headerlink" title="4. 循环导入问题"></a>4. 循环导入问题</h2><h3 id="4-1-问题场景"><a href="#4-1-问题场景" class="headerlink" title="4.1 问题场景"></a>4.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></pre></td><td class="code"><pre><span class="line"><span class="comment"># a.py</span></span><br><span class="line"><span class="keyword">from</span> b <span class="keyword">import</span> func_b</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">func_a</span>():</span><br><span class="line">    <span class="keyword">return</span> func_b()</span><br><span class="line"></span><br><span class="line"><span class="comment"># b.py</span></span><br><span class="line"><span class="keyword">from</span> a <span class="keyword">import</span> func_a   <span class="comment"># 循环导入！</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">func_b</span>():</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;b&quot;</span></span><br></pre></td></tr></table></figure><p>运行 <code>a.py</code> 时：</p><ol><li>开始加载 <code>a.py</code></li><li>执行 <code>from b import func_b</code></li><li>开始加载 <code>b.py</code></li><li>执行 <code>from a import func_a</code></li><li><code>a.py</code> 还没执行完，<code>func_a</code> 还不存在</li><li><code>ImportError: cannot import name &#39;func_a&#39;</code></li></ol><h3 id="4-2-解决方案"><a href="#4-2-解决方案" class="headerlink" title="4.2 解决方案"></a>4.2 解决方案</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></pre></td><td class="code"><pre><span class="line"><span class="comment"># common.py</span></span><br><span class="line"><span class="comment"># 将共享的接口/常量提取到独立模块</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> common <span class="keyword">import</span> SharedInterface</span><br><span class="line"></span><br><span class="line"><span class="comment"># a.py</span></span><br><span class="line"><span class="keyword">from</span> common <span class="keyword">import</span> SharedInterface</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">func_a</span>(<span class="params">interface: SharedInterface</span>):</span><br><span class="line">    <span class="keyword">return</span> interface.func_b()</span><br><span class="line"></span><br><span class="line"><span class="comment"># b.py</span></span><br><span class="line"><span class="keyword">from</span> common <span class="keyword">import</span> SharedInterface</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">func_b</span>():</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;b&quot;</span></span><br></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></pre></td><td class="code"><pre><span class="line"><span class="comment"># a.py</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">func_a</span>():</span><br><span class="line">    <span class="keyword">from</span> b <span class="keyword">import</span> func_b   <span class="comment"># 在函数内部导入</span></span><br><span class="line">    <span class="keyword">return</span> func_b()</span><br></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><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><span class="line"><span class="comment"># interfaces.py</span></span><br><span class="line"><span class="keyword">from</span> abc <span class="keyword">import</span> ABC, abstractmethod</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">ServiceInterface</span>(<span class="title class_ inherited__">ABC</span>):</span><br><span class="line"><span class="meta">    @abstractmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">process</span>(<span class="params">self</span>): <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># a.py</span></span><br><span class="line"><span class="keyword">from</span> interfaces <span class="keyword">import</span> ServiceInterface</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">func_a</span>(<span class="params">service: ServiceInterface</span>):</span><br><span class="line">    <span class="keyword">return</span> service.process()</span><br><span class="line"></span><br><span class="line"><span class="comment"># b.py</span></span><br><span class="line"><span class="keyword">from</span> interfaces <span class="keyword">import</span> ServiceInterface</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">ServiceB</span>(<span class="title class_ inherited__">ServiceInterface</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">process</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;b&quot;</span></span><br></pre></td></tr></table></figure><p><strong>工程建议</strong>：循环导入是设计问题的信号。优先重构，延迟导入只是权宜之计。</p><blockquote><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># a.py（顶层导入）</span></span><br><span class="line"><span class="keyword">from</span> b <span class="keyword">import</span> func_b   <span class="comment"># 报错</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># a.py（函数内导入）</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">func_a</span>():</span><br><span class="line">    <span class="keyword">from</span> b <span class="keyword">import</span> func_b   <span class="comment"># 不报错</span></span><br></pre></td></tr></table></figure><p><strong>答案</strong>：顶层导入在模块加载时立即执行，此时被导入模块可能还未完全加载。函数内导入在执行函数时才导入，此时所有模块都已加载完毕。</p></blockquote><hr><h2 id="5-模块与包的工程实践"><a href="#5-模块与包的工程实践" class="headerlink" title="5. 模块与包的工程实践"></a>5. 模块与包的工程实践</h2><h3 id="5-1-避免-import"><a href="#5-1-避免-import" class="headerlink" title="5.1 避免 import *"></a>5.1 避免 <code>import *</code></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><span class="line"><span class="comment"># 不好的做法：你不知道导入了什么</span></span><br><span class="line"><span class="keyword">from</span> os <span class="keyword">import</span> *</span><br><span class="line"><span class="built_in">print</span>(path)   <span class="comment"># 哪个 path？os.path？还是别的？</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 好的做法：明确导入</span></span><br><span class="line"><span class="keyword">from</span> os.path <span class="keyword">import</span> join, exists</span><br><span class="line"><span class="keyword">from</span> pathlib <span class="keyword">import</span> Path</span><br></pre></td></tr></table></figure><h3 id="5-2-模块级别的副作用"><a href="#5-2-模块级别的副作用" class="headerlink" title="5.2 模块级别的副作用"></a>5.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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 不好的做法：模块导入时产生副作用</span></span><br><span class="line"><span class="keyword">import</span> requests</span><br><span class="line"></span><br><span class="line"><span class="comment"># 模块加载时发送请求！这很糟糕</span></span><br><span class="line">response = requests.get(<span class="string">&quot;`https://api.example.com/config`&quot;</span>)</span><br><span class="line">DEFAULT_CONFIG = response.json()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 好的做法：延迟初始化</span></span><br><span class="line">DEFAULT_CONFIG = <span class="literal">None</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">get_config</span>():</span><br><span class="line">    <span class="keyword">global</span> DEFAULT_CONFIG</span><br><span class="line">    <span class="keyword">if</span> DEFAULT_CONFIG <span class="keyword">is</span> <span class="literal">None</span>:</span><br><span class="line">        response = requests.get(<span class="string">&quot;`https://api.example.com/config`&quot;</span>)</span><br><span class="line">        DEFAULT_CONFIG = response.json()</span><br><span class="line">    <span class="keyword">return</span> DEFAULT_CONFIG</span><br></pre></td></tr></table></figure><h3 id="5-3-包的版本管理"><a href="#5-3-包的版本管理" class="headerlink" title="5.3 包的版本管理"></a>5.3 包的版本管理</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><span class="line"><span class="comment"># myproject/__init__.py</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">try</span>:</span><br><span class="line">    <span class="keyword">from</span> importlib.metadata <span class="keyword">import</span> version</span><br><span class="line">    __version__ = version(<span class="string">&quot;myproject&quot;</span>)</span><br><span class="line"><span class="keyword">except</span> ImportError:</span><br><span class="line">    <span class="comment"># Python &lt; 3.8</span></span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        <span class="keyword">import</span> pkg_resources</span><br><span class="line">        __version__ = pkg_resources.get_distribution(<span class="string">&quot;myproject&quot;</span>).version</span><br><span class="line">    <span class="keyword">except</span> Exception:</span><br><span class="line">        __version__ = <span class="string">&quot;unknown&quot;</span></span><br></pre></td></tr></table></figure><h3 id="5-4-类型提示与模块"><a href="#5-4-类型提示与模块" class="headerlink" title="5.4 类型提示与模块"></a>5.4 类型提示与模块</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><span class="line"><span class="comment"># myproject/core/services.py</span></span><br><span class="line"><span class="keyword">from</span> __future__ <span class="keyword">import</span> annotations  <span class="comment"># 3.7+，允许在类定义内引用自身</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> typing <span class="keyword">import</span> <span class="type">Optional</span>, <span class="type">List</span></span><br><span class="line"><span class="keyword">from</span> myproject.core.models <span class="keyword">import</span> User</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">UserService</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>) -&gt; <span class="literal">None</span>:</span><br><span class="line">        <span class="variable language_">self</span>._users: <span class="type">List</span>[User] = []</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_user</span>(<span class="params">self, user_id: <span class="built_in">int</span></span>) -&gt; <span class="type">Optional</span>[User]:</span><br><span class="line">        <span class="keyword">for</span> user <span class="keyword">in</span> <span class="variable language_">self</span>._users:</span><br><span class="line">            <span class="keyword">if</span> user.<span class="built_in">id</span> == user_id:</span><br><span class="line">                <span class="keyword">return</span> user</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">None</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">add_user</span>(<span class="params">self, user: User</span>) -&gt; <span class="literal">None</span>:</span><br><span class="line">        <span class="variable language_">self</span>._users.append(user)</span><br></pre></td></tr></table></figure><h3 id="5-5-zipapp：打包为可执行-zip"><a href="#5-5-zipapp：打包为可执行-zip" class="headerlink" title="5.5 zipapp：打包为可执行 zip"></a>5.5 <code>zipapp</code>：打包为可执行 zip</h3><p>Python 标准库支持将 Python 应用打包为单个 <code>.pyz</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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 命令行使用</span></span><br><span class="line"><span class="comment"># python -m zipapp myproject -m &quot;myproject:main&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建可执行 zip 包</span></span><br><span class="line"><span class="comment"># 目录结构：</span></span><br><span class="line"><span class="comment"># myproject/</span></span><br><span class="line"><span class="comment"># ├── __main__.py     # 入口文件</span></span><br><span class="line"><span class="comment"># └── utils.py</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 打包后可以这样运行：</span></span><br><span class="line"><span class="comment"># python myproject.pyz</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 添加 shebang 创建可执行文件</span></span><br><span class="line"><span class="comment"># python -m zipapp myproject -p &quot;/usr/bin/env python3&quot; -m &quot;myproject:main&quot;</span></span><br><span class="line"><span class="comment"># chmod +x myproject.pyz</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 程序内部使用</span></span><br><span class="line"><span class="keyword">import</span> zipapp</span><br><span class="line">zipapp.create_archive(<span class="string">&quot;myproject&quot;</span>, <span class="string">&quot;myproject.pyz&quot;</span>, interpreter=<span class="string">&quot;/usr/bin/env python3&quot;</span>)</span><br></pre></td></tr></table></figure><p><strong>工程场景</strong>：快速分发工具脚本、演示项目、无依赖的 CLI 工具（不适用于需要大量第三方依赖的复杂项目）。</p><hr><h2 id="6-工作实战场景"><a href="#6-工作实战场景" class="headerlink" title="6. 工作实战场景"></a>6. 工作实战场景</h2><h3 id="6-1-大型项目的模块分层"><a href="#6-1-大型项目的模块分层" class="headerlink" title="6.1 大型项目的模块分层"></a>6.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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 典型的分层架构模块组织</span></span><br><span class="line"><span class="comment"># src/myapp/</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 第一层：接口层（对外暴露）</span></span><br><span class="line"><span class="comment"># api/routes.py - HTTP API 路由</span></span><br><span class="line"><span class="comment"># cli/main.py   - 命令行入口</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 第二层：应用层（业务逻辑）</span></span><br><span class="line"><span class="comment"># services/user_service.py - 用户服务</span></span><br><span class="line"><span class="comment"># services/order_service.py - 订单服务</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 第三层：领域层（核心业务对象）</span></span><br><span class="line"><span class="comment"># domain/models.py - 领域模型</span></span><br><span class="line"><span class="comment"># domain/repositories.py - 仓储接口</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 第四层：基础设施层（技术实现）</span></span><br><span class="line"><span class="comment"># infrastructure/db.py - 数据库连接</span></span><br><span class="line"><span class="comment"># infrastructure/cache.py - 缓存实现</span></span><br><span class="line"><span class="comment"># infrastructure/external_api.py - 外部 API 调用</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 第五层：工具层（通用工具）</span></span><br><span class="line"><span class="comment"># utils/helpers.py - 通用辅助函数</span></span><br><span class="line"><span class="comment"># utils/validators.py - 验证器</span></span><br></pre></td></tr></table></figure><p><strong>模块依赖规则</strong>：</p><figure class="highlight plaintext"><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><span class="line">api/cli → services → domain → infrastructure → utils</span><br><span class="line">         ↑                    ↑</span><br><span class="line">         └────────────────────┘</span><br></pre></td></tr></table></figure><ul><li>上层可以依赖下层</li><li>同层之间尽量避免依赖</li><li>绝对不能循环依赖</li></ul><h3 id="6-2-插件化架构设计"><a href="#6-2-插件化架构设计" class="headerlink" title="6.2 插件化架构设计"></a>6.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><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 插件系统：通过动态导入实现扩展</span></span><br><span class="line"><span class="keyword">import</span> importlib</span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">PluginManager</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;插件管理器：自动发现和加载插件。&quot;&quot;&quot;</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, plugins_dir</span>):</span><br><span class="line">        <span class="variable language_">self</span>.plugins_dir = plugins_dir</span><br><span class="line">        <span class="variable language_">self</span>.plugins = &#123;&#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">load_plugins</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;扫描插件目录并加载所有插件。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> os.path.exists(<span class="variable language_">self</span>.plugins_dir):</span><br><span class="line">            <span class="keyword">return</span></span><br><span class="line">        </span><br><span class="line">        <span class="keyword">for</span> filename <span class="keyword">in</span> os.listdir(<span class="variable language_">self</span>.plugins_dir):</span><br><span class="line">            <span class="keyword">if</span> filename.endswith(<span class="string">&quot;.py&quot;</span>) <span class="keyword">and</span> <span class="keyword">not</span> filename.startswith(<span class="string">&quot;_&quot;</span>):</span><br><span class="line">                module_name = filename[:-<span class="number">3</span>]</span><br><span class="line">                <span class="keyword">try</span>:</span><br><span class="line">                    module = importlib.import_module(<span class="string">f&quot;plugins.<span class="subst">&#123;module_name&#125;</span>&quot;</span>)</span><br><span class="line">                    <span class="keyword">if</span> <span class="built_in">hasattr</span>(module, <span class="string">&quot;register&quot;</span>):</span><br><span class="line">                        plugin = module.register()</span><br><span class="line">                        <span class="variable language_">self</span>.plugins[plugin.name] = plugin</span><br><span class="line">                        <span class="built_in">print</span>(<span class="string">f&quot;Loaded plugin: <span class="subst">&#123;plugin.name&#125;</span>&quot;</span>)</span><br><span class="line">                <span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">                    <span class="built_in">print</span>(<span class="string">f&quot;Failed to load plugin <span class="subst">&#123;module_name&#125;</span>: <span class="subst">&#123;e&#125;</span>&quot;</span>)</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_plugin</span>(<span class="params">self, name</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>.plugins.get(name)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用示例</span></span><br><span class="line"><span class="comment"># plugins/</span></span><br><span class="line"><span class="comment"># ├── __init__.py</span></span><br><span class="line"><span class="comment"># ├── email_notifier.py</span></span><br><span class="line"><span class="comment"># └── sms_notifier.py</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># email_notifier.py</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">EmailNotifier</span>:</span><br><span class="line">    name = <span class="string">&quot;email&quot;</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">send</span>(<span class="params">self, message</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;Sending email: <span class="subst">&#123;message&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">register</span>():</span><br><span class="line">    <span class="keyword">return</span> EmailNotifier()</span><br></pre></td></tr></table></figure><h3 id="6-3-配置模块的封装"><a href="#6-3-配置模块的封装" class="headerlink" title="6.3 配置模块的封装"></a>6.3 配置模块的封装</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><span class="line"><span class="keyword">import</span> os</span><br><span class="line"><span class="keyword">from</span> pathlib <span class="keyword">import</span> Path</span><br><span class="line"><span class="keyword">from</span> typing <span class="keyword">import</span> <span class="type">Optional</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Config</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;配置管理：支持多环境配置。&quot;&quot;&quot;</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="variable language_">self</span>._config = &#123;&#125;</span><br><span class="line">        <span class="variable language_">self</span>._load()</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">_load</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;加载配置：环境变量优先，然后是配置文件。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="comment"># 从环境变量加载</span></span><br><span class="line">        <span class="variable language_">self</span>._config[<span class="string">&quot;DEBUG&quot;</span>] = os.getenv(<span class="string">&quot;DEBUG&quot;</span>, <span class="string">&quot;false&quot;</span>).lower() == <span class="string">&quot;true&quot;</span></span><br><span class="line">        <span class="variable language_">self</span>._config[<span class="string">&quot;DATABASE_URL&quot;</span>] = os.getenv(<span class="string">&quot;DATABASE_URL&quot;</span>, <span class="string">&quot;sqlite:///app.db&quot;</span>)</span><br><span class="line">        <span class="variable language_">self</span>._config[<span class="string">&quot;SECRET_KEY&quot;</span>] = os.getenv(<span class="string">&quot;SECRET_KEY&quot;</span>)</span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 从配置文件加载（如果存在）</span></span><br><span class="line">        config_file = Path(<span class="string">&quot;config.yaml&quot;</span>)</span><br><span class="line">        <span class="keyword">if</span> config_file.exists():</span><br><span class="line">            <span class="keyword">import</span> yaml</span><br><span class="line">            <span class="keyword">with</span> <span class="built_in">open</span>(config_file) <span class="keyword">as</span> f:</span><br><span class="line">                file_config = yaml.safe_load(f)</span><br><span class="line">                <span class="comment"># 文件配置不覆盖环境变量</span></span><br><span class="line">                <span class="keyword">for</span> key, value <span class="keyword">in</span> file_config.items():</span><br><span class="line">                    <span class="keyword">if</span> key <span class="keyword">not</span> <span class="keyword">in</span> <span class="variable language_">self</span>._config <span class="keyword">or</span> <span class="variable language_">self</span>._config[key] <span class="keyword">is</span> <span class="literal">None</span>:</span><br><span class="line">                        <span class="variable language_">self</span>._config[key] = value</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get</span>(<span class="params">self, key: <span class="built_in">str</span>, default=<span class="literal">None</span></span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>._config.get(key, default)</span><br><span class="line">    </span><br><span class="line"><span class="meta">    @property</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">debug</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>.get(<span class="string">&quot;DEBUG&quot;</span>, <span class="literal">False</span>)</span><br><span class="line">    </span><br><span class="line"><span class="meta">    @property</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">database_url</span>(<span class="params">self</span>):</span><br><span class="line">        url = <span class="variable language_">self</span>.get(<span class="string">&quot;DATABASE_URL&quot;</span>)</span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> url:</span><br><span class="line">            <span class="keyword">raise</span> ValueError(<span class="string">&quot;DATABASE_URL is not set&quot;</span>)</span><br><span class="line">        <span class="keyword">return</span> url</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用</span></span><br><span class="line">config = Config()</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;Debug mode: <span class="subst">&#123;config.debug&#125;</span>&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;Database: <span class="subst">&#123;config.database_url&#125;</span>&quot;</span>)</span><br></pre></td></tr></table></figure><h3 id="6-4-跨包共享资源"><a href="#6-4-跨包共享资源" class="headerlink" title="6.4 跨包共享资源"></a>6.4 跨包共享资源</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><span class="line"><span class="comment"># myproject/resources/__init__.py</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;共享资源管理。&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> json</span><br><span class="line"><span class="keyword">from</span> pathlib <span class="keyword">import</span> Path</span><br><span class="line"></span><br><span class="line">_RESOURCES_DIR = Path(__file__).parent</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">load_resource</span>(<span class="params">name: <span class="built_in">str</span></span>) -&gt; <span class="built_in">dict</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;加载 JSON 资源文件。&quot;&quot;&quot;</span></span><br><span class="line">    filepath = _RESOURCES_DIR / <span class="string">f&quot;<span class="subst">&#123;name&#125;</span>.json&quot;</span></span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> filepath.exists():</span><br><span class="line">        <span class="keyword">raise</span> FileNotFoundError(<span class="string">f&quot;Resource not found: <span class="subst">&#123;name&#125;</span>&quot;</span>)</span><br><span class="line">    <span class="keyword">with</span> <span class="built_in">open</span>(filepath, encoding=<span class="string">&quot;utf-8&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">        <span class="keyword">return</span> json.load(f)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 预加载常用资源</span></span><br><span class="line">_COUNTRIES = <span class="literal">None</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">get_countries</span>():</span><br><span class="line">    <span class="string">&quot;&quot;&quot;获取国家列表（缓存）。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">global</span> _COUNTRIES</span><br><span class="line">    <span class="keyword">if</span> _COUNTRIES <span class="keyword">is</span> <span class="literal">None</span>:</span><br><span class="line">        _COUNTRIES = load_resource(<span class="string">&quot;countries&quot;</span>)</span><br><span class="line">    <span class="keyword">return</span> _COUNTRIES</span><br></pre></td></tr></table></figure><hr><h2 id="7-常见面试题汇总"><a href="#7-常见面试题汇总" class="headerlink" title="7. 常见面试题汇总"></a>7. 常见面试题汇总</h2><h3 id="7-1-基础概念题"><a href="#7-1-基础概念题" class="headerlink" title="7.1 基础概念题"></a>7.1 基础概念题</h3><p><strong>Q1：什么是模块？什么是包？它们的区别是什么？</strong></p><p><strong>A</strong>：</p><ul><li><strong>模块</strong>：一个 <code>.py</code> 文件就是一个模块，是代码复用的基本单元</li><li><strong>包</strong>：包含 <code>__init__.py</code> 的目录，是多个相关模块的集合</li><li><strong>区别</strong>：模块是单个文件，包是多个文件的目录结构；包可以包含子包和子模块</li></ul><hr><p><strong>Q2：<code>sys.path</code> 的搜索顺序是什么？如何正确添加自定义模块路径？</strong></p><p><strong>A</strong>：搜索顺序：</p><ol><li>当前目录（运行脚本所在目录）</li><li><code>PYTHONPATH</code> 环境变量中的目录</li><li>标准库目录</li><li><code>.pth</code> 文件指定的目录</li></ol><p>正确添加方式：</p><ul><li>开发时：<code>pip install -e .</code>（可编辑安装）</li><li>运行时：设置 <code>PYTHONPATH</code> 环境变量</li><li>不推荐：直接修改 <code>sys.path</code></li></ul><hr><p><strong>Q3：<code>if __name__ == &quot;__main__&quot;</code> 的作用是什么？</strong></p><p><strong>A</strong>：当模块被直接运行时，<code>__name__</code> 的值为 <code>&quot;__main__&quot;</code>；当模块被导入时，<code>__name__</code> 的值为模块名。这个条件判断用于：</p><ul><li>区分模块是被导入还是直接运行</li><li>放置测试代码、演示代码或 CLI 入口</li><li>确保导入模块时不会执行这些代码</li></ul><hr><p><strong>Q4：<code>__init__.py</code> 的作用是什么？</strong></p><p><strong>A</strong>：</p><ol><li>标记目录为 Python 包</li><li>包级初始化代码（只执行一次）</li><li>控制 <code>from package import *</code> 的导出内容（通过 <code>__all__</code>）</li><li>简化导入路径（将子模块的内容提升到包级别）</li><li>定义包级变量（如 <code>__version__</code>）</li></ol><hr><p><strong>Q5：绝对导入和相对导入的区别？</strong></p><p><strong>A</strong>：</p><ul><li><strong>绝对导入</strong>：<code>from myproject.core.models import User</code>，从项目根目录开始定位，清晰明确</li><li><strong>相对导入</strong>：<code>from .models import User</code> 或 <code>from ..utils import helper</code>，从当前模块位置开始定位</li></ul><p>推荐：项目顶层用绝对导入，包内部可用相对导入。</p><hr><h3 id="7-2-实战应用题"><a href="#7-2-实战应用题" class="headerlink" title="7.2 实战应用题"></a>7.2 实战应用题</h3><p><strong>Q6：为什么推荐 <code>src</code> 布局而不是 flat 布局？</strong></p><p><strong>A</strong>：<code>src</code> 布局确保测试的是安装后的包，而不是开发目录下的代码。flat 布局可能导致测试时导入本地代码而非安装包，造成”安装后测试”和”开发时测试”结果不一致。</p><hr><p><strong>Q7：如何解决循环导入问题？</strong></p><p><strong>A</strong>：</p><ol><li><strong>重构代码</strong>（推荐）：将共享的接口&#x2F;常量提取到独立模块，消除循环依赖</li><li><strong>延迟导入</strong>：在函数内部导入，避免模块加载时的循环</li><li><strong>使用接口抽象</strong>：通过 ABC 定义接口，依赖接口而非具体实现</li></ol><hr><p><strong>Q8：模块导入时产生副作用有什么问题？如何避免？</strong></p><p><strong>A</strong>：问题：导入模块时执行耗时操作（如网络请求、数据库连接）会减慢导入速度，难以测试，可能导致意外行为。</p><p>避免方式：</p><ol><li>将耗时操作封装到函数中，延迟执行</li><li>使用懒加载模式（首次调用时初始化）</li><li>模块级别只定义数据结构和函数，不执行实际操作</li></ol><hr><p><strong>Q9：<code>pyproject.toml</code> 的作用是什么？</strong></p><p><strong>A</strong>：<code>pyproject.toml</code> 是 PEP 621 推荐的项目配置文件，统一管理：</p><ol><li>项目元数据（名称、版本、作者）</li><li>依赖声明（运行依赖、开发依赖）</li><li>工具配置（black、mypy、pytest 等）</li><li>脚本入口定义</li></ol><hr><p><strong>Q10：如何实现插件化架构？</strong></p><p><strong>A</strong>：</p><ol><li>定义插件接口（抽象基类）</li><li>扫描插件目录</li><li>动态导入插件模块</li><li>注册插件到管理器</li><li>在运行时按需加载和使用插件</li></ol><hr><h3 id="7-3-代码分析题"><a href="#7-3-代码分析题" class="headerlink" title="7.3 代码分析题"></a>7.3 代码分析题</h3><p><strong>Q11：分析以下代码的问题：</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></pre></td><td class="code"><pre><span class="line"><span class="comment"># module_a.py</span></span><br><span class="line"><span class="keyword">from</span> module_b <span class="keyword">import</span> func_b</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">func_a</span>():</span><br><span class="line">    <span class="keyword">return</span> func_b()</span><br><span class="line"></span><br><span class="line"><span class="comment"># module_b.py</span></span><br><span class="line"><span class="keyword">from</span> module_a <span class="keyword">import</span> func_a</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">func_b</span>():</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;b&quot;</span></span><br></pre></td></tr></table></figure><p><strong>A</strong>：循环导入问题。运行时会报 <code>ImportError: cannot import name &#39;func_a&#39;</code>。解决方案：重构代码提取共享部分，或使用延迟导入。</p><hr><p><strong>Q12：分析以下代码的问题：</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></pre></td><td class="code"><pre><span class="line"><span class="comment"># config.py</span></span><br><span class="line"><span class="keyword">import</span> requests</span><br><span class="line"></span><br><span class="line">response = requests.get(<span class="string">&quot;https://api.example.com/config&quot;</span>)</span><br><span class="line">DEFAULT_CONFIG = response.json()</span><br></pre></td></tr></table></figure><p><strong>A</strong>：模块导入时产生副作用（发送网络请求）。问题：导入速度慢、难以测试、可能因网络问题导致导入失败。应改为延迟初始化。</p><hr><p><strong>Q13：如何正确运行以下包结构中的 <code>main.py</code>？</strong></p><figure class="highlight plaintext"><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><span class="line">myproject/</span><br><span class="line">├── __init__.py</span><br><span class="line">├── core/</span><br><span class="line">│   ├── __init__.py</span><br><span class="line">│   └── main.py</span><br></pre></td></tr></table></figure><p><strong>A</strong>：使用 <code>python -m myproject.core.main</code>。不要直接运行 <code>python myproject/core/main.py</code>，因为这样会导致 <code>sys.path</code> 问题，无法正确导入同包内的其他模块。</p><hr><h2 id="8-实战项目：模块化-CLI-工具开发"><a href="#8-实战项目：模块化-CLI-工具开发" class="headerlink" title="8. 实战项目：模块化 CLI 工具开发"></a>8. 实战项目：模块化 CLI 工具开发</h2><h3 id="8-1-项目概述"><a href="#8-1-项目概述" class="headerlink" title="8.1 项目概述"></a>8.1 项目概述</h3><p><strong>项目目标</strong>：开发一个命令行工具 <code>pytool</code>，包含多个子命令，支持模块化扩展。</p><p><strong>功能需求</strong>：</p><ul><li><code>pytool init &lt;project&gt;</code> - 初始化项目</li><li><code>pytool lint</code> - 代码检查</li><li><code>pytool format</code> - 代码格式化</li><li><code>pytool test</code> - 运行测试</li><li><code>pytool version</code> - 显示版本</li></ul><h3 id="8-2-项目结构"><a href="#8-2-项目结构" class="headerlink" title="8.2 项目结构"></a>8.2 项目结构</h3><figure class="highlight plaintext"><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><span class="line">pytool/</span><br><span class="line">├── README.md</span><br><span class="line">├── pyproject.toml</span><br><span class="line">├── src/</span><br><span class="line">│   └── pytool/</span><br><span class="line">│       ├── __init__.py</span><br><span class="line">│       ├── __main__.py</span><br><span class="line">│       ├── cli.py           # CLI 入口</span><br><span class="line">│       ├── commands/        # 命令模块</span><br><span class="line">│       │   ├── __init__.py</span><br><span class="line">│       │   ├── init.py</span><br><span class="line">│       │   ├── lint.py</span><br><span class="line">│       │   ├── format.py</span><br><span class="line">│       │   ├── test.py</span><br><span class="line">│       │   └── version.py</span><br><span class="line">│       └── utils/           # 工具模块</span><br><span class="line">│           ├── __init__.py</span><br><span class="line">│           └── helpers.py</span><br><span class="line">└── tests/</span><br><span class="line">    ├── __init__.py</span><br><span class="line">    └── test_cli.py</span><br></pre></td></tr></table></figure><h3 id="8-3-核心代码"><a href="#8-3-核心代码" class="headerlink" title="8.3 核心代码"></a>8.3 核心代码</h3><p><strong>pyproject.toml</strong>：</p><figure class="highlight toml"><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><span class="line"><span class="section">[build-system]</span></span><br><span class="line"><span class="attr">requires</span> = [<span class="string">&quot;setuptools&gt;=61.0&quot;</span>, <span class="string">&quot;wheel&quot;</span>]</span><br><span class="line"><span class="attr">build-backend</span> = <span class="string">&quot;setuptools.build_meta&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="section">[project]</span></span><br><span class="line"><span class="attr">name</span> = <span class="string">&quot;pytool&quot;</span></span><br><span class="line"><span class="attr">version</span> = <span class="string">&quot;1.0.0&quot;</span></span><br><span class="line"><span class="attr">description</span> = <span class="string">&quot;A modular CLI tool&quot;</span></span><br><span class="line"><span class="attr">readme</span> = <span class="string">&quot;README.md&quot;</span></span><br><span class="line"><span class="attr">license</span> = &#123;text = <span class="string">&quot;MIT&quot;</span>&#125;</span><br><span class="line"><span class="attr">authors</span> = [</span><br><span class="line">    &#123;name = <span class="string">&quot;Your Name&quot;</span>, email = <span class="string">&quot;you@example.com&quot;</span>&#125;</span><br><span class="line">]</span><br><span class="line"><span class="attr">requires-python</span> = <span class="string">&quot;&gt;=3.9&quot;</span></span><br><span class="line"><span class="attr">dependencies</span> = [</span><br><span class="line">    <span class="string">&quot;click&gt;=8.0&quot;</span>,</span><br><span class="line">    <span class="string">&quot;black&gt;=23.0&quot;</span>,</span><br><span class="line">    <span class="string">&quot;flake8&gt;=6.0&quot;</span>,</span><br><span class="line">    <span class="string">&quot;pytest&gt;=7.0&quot;</span>,</span><br><span class="line">]</span><br><span class="line"></span><br><span class="line"><span class="section">[project.scripts]</span></span><br><span class="line"><span class="attr">pytool</span> = <span class="string">&quot;pytool.cli:main&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="section">[tool.setuptools.packages.find]</span></span><br><span class="line"><span class="attr">where</span> = [<span class="string">&quot;src&quot;</span>]</span><br></pre></td></tr></table></figure><p><strong>cli.py</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><span class="line"><span class="keyword">import</span> click</span><br><span class="line"><span class="keyword">import</span> importlib</span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"></span><br><span class="line"><span class="meta">@click.group()</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">main</span>():</span><br><span class="line">    <span class="string">&quot;&quot;&quot;PyTool - 模块化 CLI 工具&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">_load_commands</span>():</span><br><span class="line">    <span class="string">&quot;&quot;&quot;自动加载 commands 目录下的所有命令。&quot;&quot;&quot;</span></span><br><span class="line">    commands_dir = os.path.join(os.path.dirname(__file__), <span class="string">&quot;commands&quot;</span>)</span><br><span class="line">    <span class="keyword">for</span> filename <span class="keyword">in</span> os.listdir(commands_dir):</span><br><span class="line">        <span class="keyword">if</span> filename.endswith(<span class="string">&quot;.py&quot;</span>) <span class="keyword">and</span> <span class="keyword">not</span> filename.startswith(<span class="string">&quot;_&quot;</span>):</span><br><span class="line">            module_name = filename[:-<span class="number">3</span>]</span><br><span class="line">            module = importlib.import_module(<span class="string">f&quot;pytool.commands.<span class="subst">&#123;module_name&#125;</span>&quot;</span>)</span><br><span class="line">            <span class="keyword">if</span> <span class="built_in">hasattr</span>(module, <span class="string">&quot;command&quot;</span>):</span><br><span class="line">                main.add_command(module.command)</span><br><span class="line"></span><br><span class="line">_load_commands()</span><br></pre></td></tr></table></figure><p><strong>commands&#x2F;init.py</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><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> click</span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"><span class="keyword">from</span> pytool.utils.helpers <span class="keyword">import</span> create_file</span><br><span class="line"></span><br><span class="line"><span class="meta">@click.command(<span class="params"><span class="string">&quot;init&quot;</span></span>)</span></span><br><span class="line"><span class="meta">@click.argument(<span class="params"><span class="string">&quot;project&quot;</span></span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">command</span>(<span class="params">project</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;初始化一个新项目。&quot;&quot;&quot;</span></span><br><span class="line">    os.makedirs(project, exist_ok=<span class="literal">True</span>)</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 创建项目结构</span></span><br><span class="line">    files = &#123;</span><br><span class="line">        <span class="string">f&quot;<span class="subst">&#123;project&#125;</span>/README.md&quot;</span>: <span class="string">&quot;# &#123;project&#125;\n&quot;</span>,</span><br><span class="line">        <span class="string">f&quot;<span class="subst">&#123;project&#125;</span>/pyproject.toml&quot;</span>: <span class="string">&#x27;[build-system]\nrequires = [&quot;setuptools&quot;]\n&#x27;</span>,</span><br><span class="line">        <span class="string">f&quot;<span class="subst">&#123;project&#125;</span>/src/<span class="subst">&#123;project&#125;</span>/__init__.py&quot;</span>: <span class="string">&quot;&quot;</span>,</span><br><span class="line">        <span class="string">f&quot;<span class="subst">&#123;project&#125;</span>/tests/__init__.py&quot;</span>: <span class="string">&quot;&quot;</span>,</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">for</span> filepath, content <span class="keyword">in</span> files.items():</span><br><span class="line">        create_file(filepath, content.<span class="built_in">format</span>(project=project))</span><br><span class="line">    </span><br><span class="line">    click.echo(<span class="string">f&quot;项目 <span class="subst">&#123;project&#125;</span> 初始化完成&quot;</span>)</span><br></pre></td></tr></table></figure><p><strong>commands&#x2F;format.py</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><span class="line"><span class="keyword">import</span> click</span><br><span class="line"><span class="keyword">import</span> subprocess</span><br><span class="line"></span><br><span class="line"><span class="meta">@click.command(<span class="params"><span class="string">&quot;format&quot;</span></span>)</span></span><br><span class="line"><span class="meta">@click.argument(<span class="params"><span class="string">&quot;path&quot;</span>, default=<span class="string">&quot;.&quot;</span></span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">command</span>(<span class="params">path</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;使用 black 格式化代码。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        subprocess.run([<span class="string">&quot;black&quot;</span>, path], check=<span class="literal">True</span>)</span><br><span class="line">        click.echo(<span class="string">f&quot;代码格式化完成: <span class="subst">&#123;path&#125;</span>&quot;</span>)</span><br><span class="line">    <span class="keyword">except</span> subprocess.CalledProcessError:</span><br><span class="line">        click.echo(<span class="string">&quot;格式化失败&quot;</span>, err=<span class="literal">True</span>)</span><br><span class="line">    <span class="keyword">except</span> FileNotFoundError:</span><br><span class="line">        click.echo(<span class="string">&quot;black 未安装，请运行: pip install black&quot;</span>, err=<span class="literal">True</span>)</span><br></pre></td></tr></table></figure><p><strong>commands&#x2F;version.py</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><span class="line"><span class="keyword">import</span> click</span><br><span class="line"><span class="keyword">from</span> pytool <span class="keyword">import</span> __version__</span><br><span class="line"></span><br><span class="line"><span class="meta">@click.command(<span class="params"><span class="string">&quot;version&quot;</span></span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">command</span>():</span><br><span class="line">    <span class="string">&quot;&quot;&quot;显示版本信息。&quot;&quot;&quot;</span></span><br><span class="line">    click.echo(<span class="string">f&quot;PyTool v<span class="subst">&#123;__version__&#125;</span>&quot;</span>)</span><br></pre></td></tr></table></figure><p><strong>utils&#x2F;helpers.py</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> os</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">create_file</span>(<span class="params">filepath, content</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;创建文件，如果父目录不存在则创建。&quot;&quot;&quot;</span></span><br><span class="line">    dirname = os.path.dirname(filepath)</span><br><span class="line">    <span class="keyword">if</span> dirname:</span><br><span class="line">        os.makedirs(dirname, exist_ok=<span class="literal">True</span>)</span><br><span class="line">    <span class="keyword">with</span> <span class="built_in">open</span>(filepath, <span class="string">&quot;w&quot;</span>, encoding=<span class="string">&quot;utf-8&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">        f.write(content)</span><br></pre></td></tr></table></figure><p><strong><strong>init</strong>.py</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><span class="line"><span class="string">&quot;&quot;&quot;PyTool - 模块化 CLI 工具&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">try</span>:</span><br><span class="line">    <span class="keyword">from</span> importlib.metadata <span class="keyword">import</span> version</span><br><span class="line">    __version__ = version(<span class="string">&quot;pytool&quot;</span>)</span><br><span class="line"><span class="keyword">except</span> ImportError:</span><br><span class="line">    __version__ = <span class="string">&quot;1.0.0&quot;</span></span><br></pre></td></tr></table></figure><p><strong><strong>main</strong>.py</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></pre></td><td class="code"><pre><span class="line"><span class="string">&quot;&quot;&quot;python -m pytool 的入口。&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> pytool.cli <span class="keyword">import</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    main()</span><br></pre></td></tr></table></figure><h3 id="8-4-项目运行"><a href="#8-4-项目运行" class="headerlink" title="8.4 项目运行"></a>8.4 项目运行</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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 开发模式安装</span></span><br><span class="line">pip install -e .</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用命令</span></span><br><span class="line">pytool init myproject    <span class="comment"># 初始化项目</span></span><br><span class="line">pytool format .          <span class="comment"># 格式化代码</span></span><br><span class="line">pytool lint              <span class="comment"># 代码检查</span></span><br><span class="line">pytool <span class="built_in">test</span>              <span class="comment"># 运行测试</span></span><br><span class="line">pytool version           <span class="comment"># 显示版本</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 或者使用 python -m</span></span><br><span class="line">python -m pytool version</span><br></pre></td></tr></table></figure><h3 id="8-5-项目亮点"><a href="#8-5-项目亮点" class="headerlink" title="8.5 项目亮点"></a>8.5 项目亮点</h3><ol><li><strong>模块化设计</strong>：每个命令都是独立模块，易于扩展</li><li><strong>自动发现</strong>：命令模块自动加载，无需手动注册</li><li><strong>标准布局</strong>：使用 src layout，符合现代 Python 项目规范</li><li><strong>可编辑安装</strong>：<code>pip install -e .</code> 支持开发模式</li><li><strong>命令行接口</strong>：使用 click 库，功能丰富、文档友好</li></ol><hr><h2 id="附录：第五阶段自检清单"><a href="#附录：第五阶段自检清单" class="headerlink" title="附录：第五阶段自检清单"></a>附录：第五阶段自检清单</h2><p>在继续学习第六阶段之前，请确保你能：</p><ul><li><input disabled="" type="checkbox"> 解释 <code>sys.path</code> 的搜索顺序，并说明如何正确添加自定义模块路径</li><li><input disabled="" type="checkbox"> 区分 <code>import</code>、<code>from ... import</code>、<code>from ... import *</code> 的使用场景</li><li><input disabled="" type="checkbox"> 解释 <code>if __name__ == &quot;__main__&quot;</code> 的作用和原理</li><li><input disabled="" type="checkbox"> 编写合理的 <code>__init__.py</code>，控制包的公开 API</li><li><input disabled="" type="checkbox"> 区分绝对导入和相对导入，并知道各自的使用场景</li><li><input disabled="" type="checkbox"> 设计一个标准的 Python 项目目录结构（使用 src layout）</li><li><input disabled="" type="checkbox"> 编写 <code>pyproject.toml</code> 配置项目元数据、依赖和工具</li><li><input disabled="" type="checkbox"> 解释循环导入的原因，并能给出至少两种解决方案</li><li><input disabled="" type="checkbox"> 避免模块级别的副作用（如导入时执行网络请求）</li><li><input disabled="" type="checkbox"> 使用 <code>python -m</code> 正确运行包内的模块</li><li><input disabled="" type="checkbox"> 使用 <code>pip install -e .</code> 进行可编辑安装</li><li><input disabled="" type="checkbox"> 使用 <code>zipapp</code> 打包 Python 应用</li></ul><hr><blockquote><p><strong>工程师寄语</strong>：好的项目结构是”自解释的”——新成员打开代码库，看目录结构就能猜到项目的架构和职责划分。花时间在项目初期设计好模块边界，比后期重构要划算得多。</p></blockquote>]]>
    </content>
    <id>https://itdxb.cn/2026/07/22/python/document/%E3%80%90Python%E3%80%91%E6%A8%A1%E5%9D%97%E3%80%81%E5%8C%85%E4%B8%8E%E9%A1%B9%E7%9B%AE%E7%BB%93%E6%9E%84%EF%BC%88%E7%AC%AC%E4%BA%94%E9%98%B6%E6%AE%B5%EF%BC%89/</id>
    <link href="https://itdxb.cn/2026/07/22/python/document/%E3%80%90Python%E3%80%91%E6%A8%A1%E5%9D%97%E3%80%81%E5%8C%85%E4%B8%8E%E9%A1%B9%E7%9B%AE%E7%BB%93%E6%9E%84%EF%BC%88%E7%AC%AC%E4%BA%94%E9%98%B6%E6%AE%B5%EF%BC%89/"/>
    <published>2026-07-22T08:00:00.000Z</published>
    <summary>系统掌握 Python 模块与包的导入机制、项目目录组织、pyproject.toml 配置、循环导入解决和工程最佳实践。</summary>
    <title>【Python】模块、包与项目结构（第五阶段）</title>
    <updated>2026-07-24T05:39:58.374Z</updated>
  </entry>
  <entry>
    <author>
      <name>Super Bing</name>
    </author>
    <category term="Python" scheme="https://itdxb.cn/categories/Python/"/>
    <category term="教程" scheme="https://itdxb.cn/tags/%E6%95%99%E7%A8%8B/"/>
    <category term="Python" scheme="https://itdxb.cn/tags/Python/"/>
    <category term="异常处理" scheme="https://itdxb.cn/tags/%E5%BC%82%E5%B8%B8%E5%A4%84%E7%90%86/"/>
    <category term="调试" scheme="https://itdxb.cn/tags/%E8%B0%83%E8%AF%95/"/>
    <content>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><h1 id="四、异常处理与调试"><a href="#四、异常处理与调试" class="headerlink" title="四、异常处理与调试"></a>四、异常处理与调试</h1><blockquote><p><strong>阶段定位</strong>：异常处理是程序健壮性的基石，调试能力是开发效率的倍增器。本阶段从异常机制原理出发，延伸到生产级的日志系统和调试方法论，帮你建立”防御性编程”的思维习惯。</p></blockquote><figure class="highlight plaintext"><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><span class="line">┌─────────────────────────────────────────────────────────────────┐</span><br><span class="line">│                    异常处理与调试知识图谱                          │</span><br><span class="line">├─────────────────────────────────────────────────────────────────┤</span><br><span class="line">│                                                                 │</span><br><span class="line">│  1. 异常体系 ──→ BaseException / Exception / 常见异常分类          │</span><br><span class="line">│       │                                                         │</span><br><span class="line">│  2. 处理机制 ──→ try/except/else/finally / raise / 异常链         │</span><br><span class="line">│       │                                                         │</span><br><span class="line">│  3. 自定义异常 ─→ 异常层级设计 / 异常信息规范                       │</span><br><span class="line">│       │                                                         │</span><br><span class="line">│  4. 断言 ──────→ assert vs raise / 开发期 vs 生产期                │</span><br><span class="line">│       │                                                         │</span><br><span class="line">│  5. 日志系统 ──→ logging / 日志级别 / 结构化日志 / 轮转策略         │</span><br><span class="line">│       │                                                         │</span><br><span class="line">│  6. 调试技巧 ──→ pdb / breakpoint() / 事后调试 / IDE 调试         │</span><br><span class="line">│       │                                                         │</span><br><span class="line">│  7. 警告控制 ──→ warnings / 废弃 API 标记 / 警告过滤器             │</span><br><span class="line">│       │                                                         │</span><br><span class="line">│  8. 生产模式 ──→ 全局捕获 / 重试 / 断路器 / 优雅降级               │</span><br><span class="line">│                                                                 │</span><br><span class="line">└─────────────────────────────────────────────────────────────────┘</span><br></pre></td></tr></table></figure><hr><h2 id="1-异常体系"><a href="#1-异常体系" class="headerlink" title="1. 异常体系"></a>1. 异常体系</h2><h3 id="1-1-异常层级结构"><a href="#1-1-异常层级结构" class="headerlink" title="1.1 异常层级结构"></a>1.1 异常层级结构</h3><p>Python 所有异常都继承自 <code>BaseException</code>，理解层级是正确捕获异常的前提：</p><figure class="highlight plaintext"><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><span class="line">BaseException</span><br><span class="line"> ├── SystemExit              # sys.exit() 触发（不应捕获）</span><br><span class="line"> ├── KeyboardInterrupt       # Ctrl+C 触发（不应捕获）</span><br><span class="line"> ├── GeneratorExit           # 生成器关闭时触发（不应捕获）</span><br><span class="line"> └── Exception               # 所有常规异常的基类（重点捕获）</span><br><span class="line">      ├── ArithmeticError</span><br><span class="line">      │    ├── ZeroDivisionError</span><br><span class="line">      │    └── OverflowError</span><br><span class="line">      ├── AssertionError     # assert 失败</span><br><span class="line">      ├── AttributeError     # 属性不存在</span><br><span class="line">      ├── ImportError / ModuleNotFoundError</span><br><span class="line">      ├── LookupError</span><br><span class="line">      │    ├── IndexError    # 序列索引越界</span><br><span class="line">      │    └── KeyError      # 字典键不存在</span><br><span class="line">      ├── NameError          # 变量未定义</span><br><span class="line">      ├── OSError</span><br><span class="line">      │    ├── FileNotFoundError</span><br><span class="line">      │    ├── PermissionError</span><br><span class="line">      │    └── ConnectionError</span><br><span class="line">      ├── RuntimeError       # 通用运行时错误</span><br><span class="line">      ├── TypeError          # 类型错误</span><br><span class="line">      ├── ValueError         # 值错误（类型正确但值不合法）</span><br><span class="line">      ├── StopIteration      # 迭代器耗尽（for 循环自动处理）</span><br><span class="line">      └── ... 自定义异常</span><br></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><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><span class="line"><span class="comment"># 错误：捕获 BaseException 会拦截 SystemExit 和 KeyboardInterrupt</span></span><br><span class="line"><span class="keyword">try</span>:</span><br><span class="line">    do_something()</span><br><span class="line"><span class="keyword">except</span> BaseException:   <span class="comment"># 不要这样做！</span></span><br><span class="line">    <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 正确：捕获 Exception</span></span><br><span class="line"><span class="keyword">try</span>:</span><br><span class="line">    do_something()</span><br><span class="line"><span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">    logger.error(<span class="string">f&quot;操作失败: <span class="subst">&#123;e&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 正确：区分不同类型的异常</span></span><br><span class="line"><span class="keyword">try</span>:</span><br><span class="line">    result = <span class="number">10</span> / <span class="built_in">int</span>(user_input)</span><br><span class="line"><span class="keyword">except</span> ValueError:</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;请输入有效的整数&quot;</span>)</span><br><span class="line"><span class="keyword">except</span> ZeroDivisionError:</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;不能除以零&quot;</span>)</span><br><span class="line"><span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;未知错误: <span class="subst">&#123;e&#125;</span>&quot;</span>)</span><br></pre></td></tr></table></figure><p><strong>异常捕获优先级规则</strong>：</p><figure class="highlight plaintext"><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><span class="line">try:</span><br><span class="line">    1 / 0</span><br><span class="line">except ArithmeticError:    # 优先检查（更具体）</span><br><span class="line">    print(&quot;算术错误&quot;)</span><br><span class="line">except Exception:          # 不会触发（因为 ArithmeticError 先匹配）</span><br><span class="line">    print(&quot;通用错误&quot;)</span><br><span class="line"></span><br><span class="line"># 输出: 算术错误</span><br><span class="line"></span><br><span class="line"># 规则：从上到下匹配，一旦匹配成功就不再继续检查</span><br></pre></td></tr></table></figure><blockquote><p><strong>面试真题</strong>：为什么不要捕获 <code>BaseException</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">try</span>:</span><br><span class="line">    sys.exit(<span class="number">0</span>)</span><br><span class="line"><span class="keyword">except</span> BaseException:</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;退出被拦截！&quot;</span>)  <span class="comment"># 错误：用户无法正常退出程序</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">try</span>:</span><br><span class="line">    <span class="keyword">import</span> time</span><br><span class="line">    time.sleep(<span class="number">10</span>)</span><br><span class="line"><span class="keyword">except</span> BaseException:</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;Ctrl+C 被拦截！&quot;</span>)  <span class="comment"># 错误：用户无法中断程序</span></span><br></pre></td></tr></table></figure><p><strong>答案</strong>：<code>BaseException</code> 包含 <code>SystemExit</code> 和 <code>KeyboardInterrupt</code>，这两种异常是程序的合法退出机制，不应被捕获。应捕获 <code>Exception</code>，它不包含这三个特殊异常。</p></blockquote><h3 id="1-2-常见异常速查"><a href="#1-2-常见异常速查" class="headerlink" title="1.2 常见异常速查"></a>1.2 常见异常速查</h3><table><thead><tr><th>异常</th><th>触发场景</th><th>示例</th></tr></thead><tbody><tr><td><code>TypeError</code></td><td>对不支持的类型执行操作</td><td><code>&quot;a&quot; + 1</code></td></tr><tr><td><code>ValueError</code></td><td>类型正确但值不合法</td><td><code>int(&quot;abc&quot;)</code></td></tr><tr><td><code>IndexError</code></td><td>列表索引越界</td><td><code>[1, 2][5]</code></td></tr><tr><td><code>KeyError</code></td><td>字典键不存在</td><td><code>{&quot;a&quot;: 1}[&quot;b&quot;]</code></td></tr><tr><td><code>AttributeError</code></td><td>对象没有该属性</td><td><code>[].push()</code></td></tr><tr><td><code>NameError</code></td><td>变量未定义</td><td><code>print(undefined_var)</code></td></tr><tr><td><code>FileNotFoundError</code></td><td>文件不存在</td><td><code>open(&quot;missing.txt&quot;)</code></td></tr><tr><td><code>ImportError</code></td><td>模块导入失败</td><td><code>import nonexistent</code></td></tr><tr><td><code>ZeroDivisionError</code></td><td>除以零</td><td><code>1 / 0</code></td></tr><tr><td><code>StopIteration</code></td><td>迭代器耗尽</td><td>手动 <code>next()</code> 调用</td></tr></tbody></table><blockquote><p><strong>易错点</strong>：区分 <code>TypeError</code> 和 <code>ValueError</code>：</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><span class="line"><span class="string">&quot;a&quot;</span> + <span class="number">1</span>       <span class="comment"># TypeError —— 类型不兼容</span></span><br><span class="line"><span class="built_in">int</span>(<span class="string">&quot;abc&quot;</span>)    <span class="comment"># ValueError —— 类型正确（str），但值无法转换</span></span><br></pre></td></tr></table></figure></blockquote><hr><h2 id="2-异常处理机制"><a href="#2-异常处理机制" class="headerlink" title="2. 异常处理机制"></a>2. 异常处理机制</h2><h3 id="2-1-基本结构：try-except-else-finally"><a href="#2-1-基本结构：try-except-else-finally" class="headerlink" title="2.1 基本结构：try / except / else / finally"></a>2.1 基本结构：<code>try / except / else / finally</code></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><span class="line"><span class="keyword">def</span> <span class="title function_">read_config</span>(<span class="params">filepath</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;读取配置文件，展示完整的异常处理结构。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        <span class="keyword">with</span> <span class="built_in">open</span>(filepath, <span class="string">&quot;r&quot;</span>, encoding=<span class="string">&quot;utf-8&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">            config = f.read()</span><br><span class="line">    <span class="keyword">except</span> FileNotFoundError:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;配置文件不存在: <span class="subst">&#123;filepath&#125;</span>&quot;</span>)</span><br><span class="line">        config = <span class="string">&quot;&quot;</span>       <span class="comment"># 使用默认值</span></span><br><span class="line">    <span class="keyword">except</span> PermissionError:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;无权限读取文件: <span class="subst">&#123;filepath&#125;</span>&quot;</span>)</span><br><span class="line">        <span class="keyword">raise</span>             <span class="comment"># 重新抛出，让调用方处理</span></span><br><span class="line">    <span class="keyword">except</span> OSError <span class="keyword">as</span> e:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;读取文件时出错: <span class="subst">&#123;e&#125;</span>&quot;</span>)</span><br><span class="line">        <span class="keyword">raise</span></span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        <span class="comment"># else 子句：仅在 try 块没有异常时执行</span></span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;配置文件读取成功&quot;</span>)</span><br><span class="line">        config = config.strip()</span><br><span class="line">    <span class="keyword">finally</span>:</span><br><span class="line">        <span class="comment"># finally 子句：无论是否发生异常，始终执行</span></span><br><span class="line">        <span class="comment"># 常用于释放资源（文件、数据库连接、锁等）</span></span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;读取操作完成&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> config</span><br></pre></td></tr></table></figure><p><strong>各子句的执行流程图</strong>：</p><figure class="highlight plaintext"><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><span class="line">try:</span><br><span class="line">    do_something()</span><br><span class="line">    ↓</span><br><span class="line">    ├── 无异常 ──→ else:（可选）──→ finally:（始终）──→ 继续执行</span><br><span class="line">    │</span><br><span class="line">    └── 有异常 ──→ except:（匹配则执行，不匹配则向上抛出）</span><br><span class="line">                       │</span><br><span class="line">                       └──→ finally:（始终）──→ 继续执行（如果异常被处理）</span><br></pre></td></tr></table></figure><table><thead><tr><th>子句</th><th>执行条件</th><th>典型用途</th></tr></thead><tbody><tr><td><code>try</code></td><td>始终执行</td><td>执行可能出错的代码</td></tr><tr><td><code>except</code></td><td>仅当 try 中发生匹配的异常</td><td>处理异常、降级、记录日志</td></tr><tr><td><code>else</code></td><td>仅当 try 中无异常</td><td>正常流程的后续处理</td></tr><tr><td><code>finally</code></td><td>无论是否有异常，始终执行</td><td>资源释放、清理操作</td></tr></tbody></table><blockquote><p><strong>易错点</strong>：<code>else</code> 和 <code>finally</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">try</span>:</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;try&quot;</span>)</span><br><span class="line">    <span class="keyword">raise</span> ValueError(<span class="string">&quot;test&quot;</span>)</span><br><span class="line"><span class="keyword">except</span>:</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;except&quot;</span>)</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;else&quot;</span>)    <span class="comment"># 不会执行（因为有异常）</span></span><br><span class="line"><span class="keyword">finally</span>:</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;finally&quot;</span>) <span class="comment"># 会执行（始终执行）</span></span><br></pre></td></tr></table></figure></blockquote><h3 id="2-2-捕获多个异常"><a href="#2-2-捕获多个异常" class="headerlink" title="2.2 捕获多个异常"></a>2.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><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><span class="line"><span class="comment"># 方式一：一个 except 捕获多个异常（用元组）</span></span><br><span class="line"><span class="keyword">try</span>:</span><br><span class="line">    value = config[<span class="string">&quot;key&quot;</span>]</span><br><span class="line"><span class="keyword">except</span> (KeyError, TypeError):</span><br><span class="line">    value = <span class="string">&quot;default&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 方式二：多个 except 分别处理（推荐，更精确）</span></span><br><span class="line"><span class="keyword">try</span>:</span><br><span class="line">    result = api.fetch_data()</span><br><span class="line"><span class="keyword">except</span> ConnectionError:</span><br><span class="line">    <span class="comment"># 网络问题，重试</span></span><br><span class="line">    retry()</span><br><span class="line"><span class="keyword">except</span> TimeoutError:</span><br><span class="line">    <span class="comment"># 超时，降级处理</span></span><br><span class="line">    fallback()</span><br><span class="line"><span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">    <span class="comment"># 未知异常，记录并上报</span></span><br><span class="line">    logger.exception(<span class="string">&quot;API 调用失败&quot;</span>)</span><br><span class="line">    <span class="keyword">raise</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 方式三：捕获所有异常（不推荐，除非最外层）</span></span><br><span class="line"><span class="keyword">try</span>:</span><br><span class="line">    risky_operation()</span><br><span class="line"><span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">    logger.error(<span class="string">f&quot;操作失败: <span class="subst">&#123;e&#125;</span>&quot;</span>)</span><br><span class="line">    <span class="comment"># 注意：不要直接 pass，至少记录日志</span></span><br></pre></td></tr></table></figure><h3 id="2-3-异常链：raise-from"><a href="#2-3-异常链：raise-from" class="headerlink" title="2.3 异常链：raise ... from"></a>2.3 异常链：<code>raise ... from</code></h3><p><strong>异常链的作用</strong>：保留原始异常信息，便于追溯根因。</p><figure class="highlight plaintext"><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><span class="line">原始异常（根因）</span><br><span class="line">    ↓</span><br><span class="line">新异常（业务描述）</span><br><span class="line">    ↓</span><br><span class="line">最终抛出（保留完整链）</span><br></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></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">ConfigError</span>(<span class="title class_ inherited__">Exception</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;配置相关异常。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">load_config</span>(<span class="params">filepath</span>):</span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        <span class="keyword">with</span> <span class="built_in">open</span>(filepath) <span class="keyword">as</span> f:</span><br><span class="line">            <span class="keyword">return</span> json.load(f)</span><br><span class="line">    <span class="keyword">except</span> FileNotFoundError <span class="keyword">as</span> e:</span><br><span class="line">        <span class="comment"># raise ... from：保留原始异常链，方便追溯根因</span></span><br><span class="line">        <span class="keyword">raise</span> ConfigError(<span class="string">f&quot;配置文件 <span class="subst">&#123;filepath&#125;</span> 不存在&quot;</span>) <span class="keyword">from</span> e</span><br><span class="line">    <span class="keyword">except</span> json.JSONDecodeError <span class="keyword">as</span> e:</span><br><span class="line">        <span class="keyword">raise</span> ConfigError(<span class="string">f&quot;配置文件 <span class="subst">&#123;filepath&#125;</span> 格式错误&quot;</span>) <span class="keyword">from</span> e</span><br><span class="line"></span><br><span class="line"><span class="comment"># 调用</span></span><br><span class="line"><span class="keyword">try</span>:</span><br><span class="line">    config = load_config(<span class="string">&quot;config.json&quot;</span>)</span><br><span class="line"><span class="keyword">except</span> ConfigError <span class="keyword">as</span> e:</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;配置加载失败: <span class="subst">&#123;e&#125;</span>&quot;</span>)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;根因: <span class="subst">&#123;e.__cause__&#125;</span>&quot;</span>)    <span class="comment"># 原始异常</span></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;完整链:&quot;</span>)</span><br><span class="line">    <span class="keyword">import</span> traceback</span><br><span class="line">    traceback.print_exception(<span class="built_in">type</span>(e), e, e.__traceback__)</span><br></pre></td></tr></table></figure><p><strong><code>raise ... from</code> 的三种用法</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><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 1. raise NewError(...) from original_error</span></span><br><span class="line"><span class="comment">#    明确表示新异常是由原始异常导致的</span></span><br><span class="line"><span class="keyword">try</span>:</span><br><span class="line">    <span class="built_in">open</span>(<span class="string">&quot;missing.txt&quot;</span>)</span><br><span class="line"><span class="keyword">except</span> FileNotFoundError <span class="keyword">as</span> e:</span><br><span class="line">    <span class="keyword">raise</span> ConfigError(<span class="string">&quot;文件不存在&quot;</span>) <span class="keyword">from</span> e</span><br><span class="line"></span><br><span class="line"><span class="comment"># 2. raise NewError(...) from None</span></span><br><span class="line"><span class="comment">#    抑制原始异常，不显示异常链（保密场景）</span></span><br><span class="line"><span class="keyword">try</span>:</span><br><span class="line">    validate_password(data)</span><br><span class="line"><span class="keyword">except</span> ValueError <span class="keyword">as</span> e:</span><br><span class="line">    <span class="comment"># 不泄露原始错误详情</span></span><br><span class="line">    <span class="keyword">raise</span> AuthenticationError(<span class="string">&quot;认证失败&quot;</span>) <span class="keyword">from</span> <span class="literal">None</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 3. raise（不带参数）</span></span><br><span class="line"><span class="comment">#    在 except 块中重新抛出当前异常，保留完整 traceback</span></span><br><span class="line"><span class="keyword">try</span>:</span><br><span class="line">    risky_operation()</span><br><span class="line"><span class="keyword">except</span> Exception:</span><br><span class="line">    logger.error(<span class="string">&quot;操作失败&quot;</span>)</span><br><span class="line">    <span class="keyword">raise</span>   <span class="comment"># 重新抛出，不改变异常信息</span></span><br></pre></td></tr></table></figure><blockquote><p><strong>面试真题</strong>：<code>raise</code> 和 <code>raise e</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">try</span>:</span><br><span class="line">    <span class="number">1</span> / <span class="number">0</span></span><br><span class="line"><span class="keyword">except</span> ZeroDivisionError <span class="keyword">as</span> e:</span><br><span class="line">    <span class="keyword">raise</span>        <span class="comment"># 保留原始 traceback（推荐）</span></span><br><span class="line">    <span class="comment"># raise e    # 重新创建 traceback（不推荐，丢失原始位置）</span></span><br></pre></td></tr></table></figure></blockquote><h3 id="2-4-获取异常详情"><a href="#2-4-获取异常详情" class="headerlink" title="2.4 获取异常详情"></a>2.4 获取异常详情</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><span class="line"><span class="keyword">import</span> traceback</span><br><span class="line"><span class="keyword">import</span> sys</span><br><span class="line"></span><br><span class="line"><span class="keyword">try</span>:</span><br><span class="line">    <span class="number">1</span> / <span class="number">0</span></span><br><span class="line"><span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">    <span class="comment"># 异常类型</span></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;类型: <span class="subst">&#123;<span class="built_in">type</span>(e).__name__&#125;</span>&quot;</span>)     <span class="comment"># ZeroDivisionError</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 异常信息</span></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;信息: <span class="subst">&#123;e&#125;</span>&quot;</span>)                     <span class="comment"># division by zero</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 异常参数</span></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;参数: <span class="subst">&#123;e.args&#125;</span>&quot;</span>)                <span class="comment"># (&#x27;division by zero&#x27;,)</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 获取 traceback 信息（详细）</span></span><br><span class="line">    exc_type, exc_value, exc_tb = sys.exc_info()</span><br><span class="line">    extracted = traceback.extract_tb(exc_tb)</span><br><span class="line">    <span class="keyword">for</span> frame <span class="keyword">in</span> extracted:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;文件: <span class="subst">&#123;frame.filename&#125;</span>, 行: <span class="subst">&#123;frame.lineno&#125;</span>, 函数: <span class="subst">&#123;frame.name&#125;</span>&quot;</span>)</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;  代码: <span class="subst">&#123;frame.line&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 最简单的方式：打印完整 traceback</span></span><br><span class="line">    traceback.print_exc()</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 也可以写入文件</span></span><br><span class="line">    traceback.print_exc(file=<span class="built_in">open</span>(<span class="string">&quot;error.log&quot;</span>, <span class="string">&quot;w&quot;</span>, encoding=<span class="string">&quot;utf-8&quot;</span>))</span><br></pre></td></tr></table></figure><hr><h2 id="3-自定义异常"><a href="#3-自定义异常" class="headerlink" title="3. 自定义异常"></a>3. 自定义异常</h2><h3 id="3-1-定义异常类"><a href="#3-1-定义异常类" class="headerlink" title="3.1 定义异常类"></a>3.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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">AppError</span>(<span class="title class_ inherited__">Exception</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;应用根异常：所有自定义异常的基类。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, message, code=<span class="literal">None</span>, details=<span class="literal">None</span></span>):</span><br><span class="line">        <span class="built_in">super</span>().__init__(message)</span><br><span class="line">        <span class="variable language_">self</span>.message = message</span><br><span class="line">        <span class="variable language_">self</span>.code = code</span><br><span class="line">        <span class="variable language_">self</span>.details = details <span class="keyword">or</span> &#123;&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">ValidationError</span>(<span class="title class_ inherited__">AppError</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;数据验证异常。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, message, field=<span class="literal">None</span>, **kwargs</span>):</span><br><span class="line">        <span class="built_in">super</span>().__init__(message, code=<span class="string">&quot;VALIDATION_ERROR&quot;</span>, **kwargs)</span><br><span class="line">        <span class="variable language_">self</span>.field = field</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">AuthenticationError</span>(<span class="title class_ inherited__">AppError</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;认证失败异常。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, message=<span class="string">&quot;认证失败&quot;</span>, **kwargs</span>):</span><br><span class="line">        <span class="built_in">super</span>().__init__(message, code=<span class="string">&quot;AUTH_ERROR&quot;</span>, **kwargs)</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">NotFoundError</span>(<span class="title class_ inherited__">AppError</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;资源未找到异常。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, resource, identifier, **kwargs</span>):</span><br><span class="line">        message = <span class="string">f&quot;<span class="subst">&#123;resource&#125;</span> 未找到: <span class="subst">&#123;identifier&#125;</span>&quot;</span></span><br><span class="line">        <span class="built_in">super</span>().__init__(message, code=<span class="string">&quot;NOT_FOUND&quot;</span>, **kwargs)</span><br><span class="line">        <span class="variable language_">self</span>.resource = resource</span><br><span class="line">        <span class="variable language_">self</span>.identifier = identifier</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">get_user</span>(<span class="params">user_id</span>):</span><br><span class="line">    <span class="keyword">if</span> user_id &lt;= <span class="number">0</span>:</span><br><span class="line">        <span class="keyword">raise</span> ValidationError(<span class="string">&quot;用户 ID 无效&quot;</span>, field=<span class="string">&quot;user_id&quot;</span>, details=&#123;<span class="string">&quot;value&quot;</span>: user_id&#125;)</span><br><span class="line">    user = db.find_user(user_id)</span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> user:</span><br><span class="line">        <span class="keyword">raise</span> NotFoundError(<span class="string">&quot;用户&quot;</span>, user_id)</span><br><span class="line">    <span class="keyword">return</span> user</span><br><span class="line"></span><br><span class="line"><span class="comment"># 捕获（按层级）</span></span><br><span class="line"><span class="keyword">try</span>:</span><br><span class="line">    user = get_user(-<span class="number">1</span>)</span><br><span class="line"><span class="keyword">except</span> ValidationError <span class="keyword">as</span> e:</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;参数错误: <span class="subst">&#123;e.message&#125;</span>, 字段: <span class="subst">&#123;e.field&#125;</span>&quot;</span>)</span><br><span class="line"><span class="keyword">except</span> NotFoundError <span class="keyword">as</span> e:</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;资源不存在: <span class="subst">&#123;e.message&#125;</span>&quot;</span>)</span><br><span class="line"><span class="keyword">except</span> AppError <span class="keyword">as</span> e:</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;应用错误 [<span class="subst">&#123;e.code&#125;</span>]: <span class="subst">&#123;e.message&#125;</span>&quot;</span>)</span><br></pre></td></tr></table></figure><p><strong>异常层级设计原则</strong>：</p><figure class="highlight plaintext"><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><span class="line">┌───────────────────────────────────────────────────┐</span><br><span class="line">│                  异常层级设计                      │</span><br><span class="line">├───────────────────────────────────────────────────┤</span><br><span class="line">│                                                   │</span><br><span class="line">│  AppError（根异常）                                │</span><br><span class="line">│    │                                              │</span><br><span class="line">│    ├── DataError                                  │</span><br><span class="line">│    │    ├── DatabaseError                         │</span><br><span class="line">│    │    │    ├── ConnectionError                  │</span><br><span class="line">│    │    │    └── QueryError                       │</span><br><span class="line">│    │    └── CacheError                            │</span><br><span class="line">│    │                                              │</span><br><span class="line">│    ├── BusinessError                              │</span><br><span class="line">│    │    ├── ValidationError                       │</span><br><span class="line">│    │    ├── AuthenticationError                   │</span><br><span class="line">│    │    └── NotFoundError                         │</span><br><span class="line">│    │                                              │</span><br><span class="line">│    └── ExternalError                              │</span><br><span class="line">│         ├── APIError                              │</span><br><span class="line">│         └── PaymentError                          │</span><br><span class="line">│                                                   │</span><br><span class="line">│  优点：                                           │</span><br><span class="line">│  1. 调用方可以按粒度捕获（如捕获所有 DataError）      │</span><br><span class="line">│  2. 异常信息可以携带结构化数据（code、details）      │</span><br><span class="line">│  3. 易于扩展和维护                                 │</span><br><span class="line">│                                                   │</span><br><span class="line">└───────────────────────────────────────────────────┘</span><br></pre></td></tr></table></figure><h3 id="3-2-异常设计原则"><a href="#3-2-异常设计原则" class="headerlink" title="3.2 异常设计原则"></a>3.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><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><span class="line"><span class="comment"># 1. 建立异常层级，让调用者可以按粒度捕获</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">DataError</span>(<span class="title class_ inherited__">AppError</span>):</span><br><span class="line">    <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">DatabaseError</span>(<span class="title class_ inherited__">DataError</span>):</span><br><span class="line">    <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">CacheError</span>(<span class="title class_ inherited__">DataError</span>):</span><br><span class="line">    <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 2. 异常信息应包含足够上下文，便于排查</span></span><br><span class="line"><span class="comment"># 不好的异常</span></span><br><span class="line"><span class="keyword">raise</span> ValueError(<span class="string">&quot;无效&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 好的异常</span></span><br><span class="line"><span class="keyword">raise</span> ValueError(<span class="string">f&quot;用户 <span class="subst">&#123;user_id&#125;</span> 的邮箱 <span class="subst">&#123;email&#125;</span> 格式无效&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 3. 不要让异常信息泄露敏感数据（如密码、token）</span></span><br><span class="line"><span class="comment"># 不好</span></span><br><span class="line"><span class="keyword">raise</span> AuthenticationError(<span class="string">f&quot;密码 <span class="subst">&#123;password&#125;</span> 错误&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 好</span></span><br><span class="line"><span class="keyword">raise</span> AuthenticationError(<span class="string">&quot;用户名或密码错误&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 4. 异常信息用英文还是中文？</span></span><br><span class="line"><span class="comment">#    - 内部系统：中文（团队沟通方便）</span></span><br><span class="line"><span class="comment">#    - 对外 API：英文（国际化）</span></span><br><span class="line"><span class="comment">#    - 最好两种都支持（i18n）</span></span><br></pre></td></tr></table></figure><blockquote><p><strong>工程建议</strong>：自定义异常应继承 <code>Exception</code>（而非 <code>BaseException</code>），并在根异常中统一处理日志和错误码。</p></blockquote><hr><h2 id="4-断言（assert）"><a href="#4-断言（assert）" class="headerlink" title="4. 断言（assert）"></a>4. 断言（assert）</h2><h3 id="4-1-基本用法"><a href="#4-1-基本用法" class="headerlink" title="4.1 基本用法"></a>4.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></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">divide</span>(<span class="params">a, b</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;安全的除法：断言检查前置条件。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">assert</span> b != <span class="number">0</span>, <span class="string">&quot;除数不能为零&quot;</span></span><br><span class="line">    <span class="keyword">assert</span> <span class="built_in">isinstance</span>(a, (<span class="built_in">int</span>, <span class="built_in">float</span>)), <span class="string">f&quot;a 必须是数字，实际是 <span class="subst">&#123;<span class="built_in">type</span>(a)&#125;</span>&quot;</span></span><br><span class="line">    <span class="keyword">return</span> a / b</span><br><span class="line"></span><br><span class="line"><span class="comment"># 开发阶段：断言帮助发现 bug</span></span><br><span class="line">divide(<span class="number">10</span>, <span class="number">2</span>)    <span class="comment"># 正常</span></span><br><span class="line">divide(<span class="number">10</span>, <span class="number">0</span>)    <span class="comment"># AssertionError: 除数不能为零</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 生产环境：使用 -O 参数禁用断言</span></span><br><span class="line"><span class="comment"># python -O script.py  → 所有 assert 语句被忽略</span></span><br></pre></td></tr></table></figure><p><strong>断言的执行流程</strong>：</p><figure class="highlight plaintext"><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><span class="line">assert condition, message</span><br><span class="line"></span><br><span class="line">等价于（在非优化模式下）：</span><br><span class="line">if __debug__:</span><br><span class="line">    if not condition:</span><br><span class="line">        raise AssertionError(message)</span><br><span class="line"></span><br><span class="line">__debug__ 在 -O 模式下为 False，断言被完全跳过</span><br></pre></td></tr></table></figure><h3 id="4-2-assert-vs-异常"><a href="#4-2-assert-vs-异常" class="headerlink" title="4.2 assert vs 异常"></a>4.2 assert vs 异常</h3><table><thead><tr><th>特性</th><th><code>assert</code></th><th><code>raise</code></th></tr></thead><tbody><tr><td>用途</td><td>开发期检查”不该发生”的逻辑错误</td><td>处理可预期的运行时错误</td></tr><tr><td>可禁用</td><td>是（<code>-O</code> 参数）</td><td>否</td></tr><tr><td>适用场景</td><td>内部一致性检查、前置条件</td><td>外部输入验证、业务逻辑错误</td></tr><tr><td>性能影响</td><td>非优化模式下有开销</td><td>始终有开销（但很小）</td></tr></tbody></table><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 正确的分工：</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">process_order</span>(<span class="params">order</span>):</span><br><span class="line">    <span class="comment"># 外部输入 → 用异常（必须处理）</span></span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> order.items:</span><br><span class="line">        <span class="keyword">raise</span> ValueError(<span class="string">&quot;订单不能为空&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 内部逻辑 → 用断言（除非有 bug，否则不会触发）</span></span><br><span class="line">    total = <span class="built_in">sum</span>(item.price <span class="keyword">for</span> item <span class="keyword">in</span> order.items)</span><br><span class="line">    <span class="keyword">assert</span> total &gt;= <span class="number">0</span>, <span class="string">f&quot;订单总额不应为负数: <span class="subst">&#123;total&#125;</span>&quot;</span>   <span class="comment"># 除非有 bug，否则不会触发</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> total</span><br></pre></td></tr></table></figure><blockquote><p><strong>工程建议</strong>：永远不要在生产代码中依赖 <code>assert</code> 做业务逻辑检查，因为用户可能用 <code>-O</code> 参数运行。断言只用于开发期的自我检查。</p></blockquote><blockquote><p><strong>面试真题</strong>：以下代码在 <code>python -O</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">login</span>(<span class="params">username, password</span>):</span><br><span class="line">    <span class="keyword">assert</span> username, <span class="string">&quot;用户名不能为空&quot;</span></span><br><span class="line">    <span class="keyword">assert</span> password, <span class="string">&quot;密码不能为空&quot;</span></span><br><span class="line">    <span class="comment"># ...</span></span><br></pre></td></tr></table></figure><p><strong>答案</strong>：两个断言都被跳过，即使传入空字符串也会继续执行后续逻辑，可能导致更严重的错误。这就是为什么外部输入验证要用 <code>raise</code>。</p></blockquote><hr><h2 id="5-日志系统"><a href="#5-日志系统" class="headerlink" title="5. 日志系统"></a>5. 日志系统</h2><h3 id="5-1-logging-模块基础"><a href="#5-1-logging-模块基础" class="headerlink" title="5.1 logging 模块基础"></a>5.1 <code>logging</code> 模块基础</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><span class="line"><span class="keyword">import</span> logging</span><br><span class="line"><span class="keyword">import</span> sys</span><br><span class="line"></span><br><span class="line"><span class="comment"># 最简单的配置</span></span><br><span class="line">logging.basicConfig(</span><br><span class="line">    level=logging.INFO,</span><br><span class="line">    <span class="built_in">format</span>=<span class="string">&quot;%(asctime)s [%(levelname)s] %(name)s: %(message)s&quot;</span>,</span><br><span class="line">    datefmt=<span class="string">&quot;%Y-%m-%d %H:%M:%S&quot;</span>,</span><br><span class="line">    handlers=[</span><br><span class="line">        logging.StreamHandler(sys.stdout),           <span class="comment"># 输出到控制台</span></span><br><span class="line">        logging.FileHandler(<span class="string">&quot;app.log&quot;</span>, encoding=<span class="string">&quot;utf-8&quot;</span>),  <span class="comment"># 输出到文件</span></span><br><span class="line">    ],</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 日志级别（从低到高）</span></span><br><span class="line">logger = logging.getLogger(__name__)</span><br><span class="line"></span><br><span class="line">logger.debug(<span class="string">&quot;调试信息：变量值 x=%d&quot;</span>, <span class="number">42</span>)        <span class="comment"># 开发调试</span></span><br><span class="line">logger.info(<span class="string">&quot;用户登录成功：user_id=%s&quot;</span>, <span class="string">&quot;123&quot;</span>)   <span class="comment"># 关键流程</span></span><br><span class="line">logger.warning(<span class="string">&quot;磁盘空间不足，剩余: %d%%&quot;</span>, <span class="number">10</span>)    <span class="comment"># 潜在问题</span></span><br><span class="line">logger.error(<span class="string">&quot;数据库连接失败: %s&quot;</span>, <span class="string">&quot;timeout&quot;</span>)     <span class="comment"># 错误但程序可继续</span></span><br><span class="line">logger.critical(<span class="string">&quot;系统无法启动，端口被占用&quot;</span>)        <span class="comment"># 严重错误，程序可能终止</span></span><br></pre></td></tr></table></figure><p><strong>日志模块架构</strong>：</p><figure class="highlight plaintext"><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><span class="line">┌─────────────────────────────────────────────────────────┐</span><br><span class="line">│                    logging 模块架构                      │</span><br><span class="line">├─────────────────────────────────────────────────────────┤</span><br><span class="line">│                                                         │</span><br><span class="line">│  Logger（记录器）                                        │</span><br><span class="line">│    │  logger.debug() / logger.info() / ...              │</span><br><span class="line">│    ↓                                                    │</span><br><span class="line">│  Filter（过滤器）                                        │</span><br><span class="line">│    │  决定哪些日志记录可以通过                            │</span><br><span class="line">│    ↓                                                    │</span><br><span class="line">│  Handler（处理器）                                       │</span><br><span class="line">│    │  StreamHandler（控制台）/ FileHandler（文件）/ ...   │</span><br><span class="line">│    ↓                                                    │</span><br><span class="line">│  Formatter（格式化器）                                    │</span><br><span class="line">│    │  将日志记录格式化为字符串                             │</span><br><span class="line">│    ↓                                                    │</span><br><span class="line">│  输出                                                    │</span><br><span class="line">│                                                         │</span><br><span class="line">└─────────────────────────────────────────────────────────┘</span><br></pre></td></tr></table></figure><h3 id="5-2-日志级别选择策略"><a href="#5-2-日志级别选择策略" class="headerlink" title="5.2 日志级别选择策略"></a>5.2 日志级别选择策略</h3><table><thead><tr><th>级别</th><th>含义</th><th>生产环境</th><th>开发环境</th><th>使用场景</th></tr></thead><tbody><tr><td><code>DEBUG</code></td><td>详细诊断信息</td><td>关闭</td><td>开启</td><td>变量值、函数参数、中间结果</td></tr><tr><td><code>INFO</code></td><td>关键业务流程</td><td>开启</td><td>开启</td><td>用户操作、服务启动、定时任务执行</td></tr><tr><td><code>WARNING</code></td><td>潜在问题</td><td>开启</td><td>开启</td><td>配置缺失、磁盘空间不足、重试</td></tr><tr><td><code>ERROR</code></td><td>错误但可恢复</td><td>开启</td><td>开启</td><td>数据库连接失败、API 调用失败</td></tr><tr><td><code>CRITICAL</code></td><td>严重错误</td><td>开启</td><td>开启</td><td>系统崩溃、数据丢失、安全问题</td></tr></tbody></table><h3 id="5-3-高级日志配置"><a href="#5-3-高级日志配置" class="headerlink" title="5.3 高级日志配置"></a>5.3 高级日志配置</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> logging</span><br><span class="line"><span class="keyword">import</span> logging.config</span><br><span class="line"></span><br><span class="line">LOGGING_CONFIG = &#123;</span><br><span class="line">    <span class="string">&quot;version&quot;</span>: <span class="number">1</span>,</span><br><span class="line">    <span class="string">&quot;disable_existing_loggers&quot;</span>: <span class="literal">False</span>,</span><br><span class="line">    <span class="string">&quot;formatters&quot;</span>: &#123;</span><br><span class="line">        <span class="string">&quot;standard&quot;</span>: &#123;</span><br><span class="line">            <span class="string">&quot;format&quot;</span>: <span class="string">&quot;%(asctime)s [%(levelname)s] %(name)s:%(lineno)d: %(message)s&quot;</span>,</span><br><span class="line">            <span class="string">&quot;datefmt&quot;</span>: <span class="string">&quot;%Y-%m-%d %H:%M:%S&quot;</span>,</span><br><span class="line">        &#125;,</span><br><span class="line">        <span class="string">&quot;detailed&quot;</span>: &#123;</span><br><span class="line">            <span class="string">&quot;format&quot;</span>: (</span><br><span class="line">                <span class="string">&#x27;&#123;&quot;time&quot;: &quot;%(asctime)s&quot;, &quot;level&quot;: &quot;%(levelname)s&quot;, &#x27;</span></span><br><span class="line">                <span class="string">&#x27;&quot;logger&quot;: &quot;%(name)s&quot;, &quot;line&quot;: %(lineno)d, &#x27;</span></span><br><span class="line">                <span class="string">&#x27;&quot;message&quot;: &quot;%(message)s&quot;&#125;&#x27;</span></span><br><span class="line">            ),</span><br><span class="line">        &#125;,</span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="string">&quot;handlers&quot;</span>: &#123;</span><br><span class="line">        <span class="string">&quot;console&quot;</span>: &#123;</span><br><span class="line">            <span class="string">&quot;class&quot;</span>: <span class="string">&quot;logging.StreamHandler&quot;</span>,</span><br><span class="line">            <span class="string">&quot;level&quot;</span>: <span class="string">&quot;DEBUG&quot;</span>,</span><br><span class="line">            <span class="string">&quot;formatter&quot;</span>: <span class="string">&quot;standard&quot;</span>,</span><br><span class="line">            <span class="string">&quot;stream&quot;</span>: <span class="string">&quot;ext://sys.stdout&quot;</span>,</span><br><span class="line">        &#125;,</span><br><span class="line">        <span class="string">&quot;file&quot;</span>: &#123;</span><br><span class="line">            <span class="string">&quot;class&quot;</span>: <span class="string">&quot;logging.handlers.RotatingFileHandler&quot;</span>,</span><br><span class="line">            <span class="string">&quot;level&quot;</span>: <span class="string">&quot;INFO&quot;</span>,</span><br><span class="line">            <span class="string">&quot;formatter&quot;</span>: <span class="string">&quot;detailed&quot;</span>,</span><br><span class="line">            <span class="string">&quot;filename&quot;</span>: <span class="string">&quot;app.log&quot;</span>,</span><br><span class="line">            <span class="string">&quot;maxBytes&quot;</span>: <span class="number">10</span> * <span class="number">1024</span> * <span class="number">1024</span>,  <span class="comment"># 10MB</span></span><br><span class="line">            <span class="string">&quot;backupCount&quot;</span>: <span class="number">5</span>,               <span class="comment"># 保留 5 个备份</span></span><br><span class="line">            <span class="string">&quot;encoding&quot;</span>: <span class="string">&quot;utf-8&quot;</span>,</span><br><span class="line">        &#125;,</span><br><span class="line">        <span class="string">&quot;error_file&quot;</span>: &#123;</span><br><span class="line">            <span class="string">&quot;class&quot;</span>: <span class="string">&quot;logging.handlers.RotatingFileHandler&quot;</span>,</span><br><span class="line">            <span class="string">&quot;level&quot;</span>: <span class="string">&quot;ERROR&quot;</span>,</span><br><span class="line">            <span class="string">&quot;formatter&quot;</span>: <span class="string">&quot;detailed&quot;</span>,</span><br><span class="line">            <span class="string">&quot;filename&quot;</span>: <span class="string">&quot;error.log&quot;</span>,</span><br><span class="line">            <span class="string">&quot;maxBytes&quot;</span>: <span class="number">10</span> * <span class="number">1024</span> * <span class="number">1024</span>,</span><br><span class="line">            <span class="string">&quot;backupCount&quot;</span>: <span class="number">5</span>,</span><br><span class="line">            <span class="string">&quot;encoding&quot;</span>: <span class="string">&quot;utf-8&quot;</span>,</span><br><span class="line">        &#125;,</span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="string">&quot;loggers&quot;</span>: &#123;</span><br><span class="line">        <span class="string">&quot;&quot;</span>: &#123;  <span class="comment"># 根 logger</span></span><br><span class="line">            <span class="string">&quot;handlers&quot;</span>: [<span class="string">&quot;console&quot;</span>, <span class="string">&quot;file&quot;</span>, <span class="string">&quot;error_file&quot;</span>],</span><br><span class="line">            <span class="string">&quot;level&quot;</span>: <span class="string">&quot;DEBUG&quot;</span>,</span><br><span class="line">        &#125;,</span><br><span class="line">        <span class="string">&quot;urllib3&quot;</span>: &#123;  <span class="comment"># 第三方库日志控制</span></span><br><span class="line">            <span class="string">&quot;level&quot;</span>: <span class="string">&quot;WARNING&quot;</span>,</span><br><span class="line">        &#125;,</span><br><span class="line">    &#125;,</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">logging.config.dictConfig(LOGGING_CONFIG)</span><br><span class="line">logger = logging.getLogger(__name__)</span><br></pre></td></tr></table></figure><p><strong>日志轮转策略</strong>：</p><figure class="highlight plaintext"><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><span class="line">┌─────────────────────────────────────────────────────────┐</span><br><span class="line">│                    日志轮转策略                          │</span><br><span class="line">├─────────────────────────────────────────────────────────┤</span><br><span class="line">│                                                         │</span><br><span class="line">│  RotatingFileHandler（按大小轮转）                        │</span><br><span class="line">│    ├── maxBytes: 10MB                                   │</span><br><span class="line">│    ├── backupCount: 5                                   │</span><br><span class="line">│    └── 生成: app.log, app.log.1, app.log.2, ..., app.log.5│</span><br><span class="line">│                                                         │</span><br><span class="line">│  TimedRotatingFileHandler（按时间轮转）                    │</span><br><span class="line">│    ├── when: &quot;D&quot;（每天）/ &quot;H&quot;（每小时）/ &quot;W0&quot;（每周一）    │</span><br><span class="line">│    ├── interval: 1                                      │</span><br><span class="line">│    └── 生成: app.log, app.log.2024-01-01, app.log.2024-01-02│</span><br><span class="line">│                                                         │</span><br><span class="line">│  组合策略：                                              │</span><br><span class="line">│    1. 按大小轮转（防止单文件过大）                         │</span><br><span class="line">│    2. 按时间轮转（便于按日期查找）                         │</span><br><span class="line">│    3. 设置合理的保留数量（避免磁盘占满）                   │</span><br><span class="line">│                                                         │</span><br><span class="line">└─────────────────────────────────────────────────────────┘</span><br></pre></td></tr></table></figure><h3 id="5-4-日志最佳实践"><a href="#5-4-日志最佳实践" class="headerlink" title="5.4 日志最佳实践"></a>5.4 日志最佳实践</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> logging</span><br><span class="line"><span class="keyword">import</span> traceback</span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"><span class="keyword">from</span> contextlib <span class="keyword">import</span> contextmanager</span><br><span class="line"></span><br><span class="line">logger = logging.getLogger(__name__)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 1. 使用 logger.exception() 记录完整 traceback</span></span><br><span class="line"><span class="keyword">try</span>:</span><br><span class="line">    <span class="number">1</span> / <span class="number">0</span></span><br><span class="line"><span class="keyword">except</span> Exception:</span><br><span class="line">    logger.exception(<span class="string">&quot;计算出错&quot;</span>)   <span class="comment"># 自动包含 traceback</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 等价于</span></span><br><span class="line"><span class="keyword">try</span>:</span><br><span class="line">    <span class="number">1</span> / <span class="number">0</span></span><br><span class="line"><span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">    logger.error(<span class="string">&quot;计算出错&quot;</span>, exc_info=<span class="literal">True</span>)</span><br><span class="line">    <span class="comment"># 或</span></span><br><span class="line">    logger.error(<span class="string">f&quot;计算出错: <span class="subst">&#123;e&#125;</span>&quot;</span>, exc_info=<span class="literal">True</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 2. 结构化日志（便于 ELK 等日志系统解析）</span></span><br><span class="line">logger.info(<span class="string">&quot;订单创建&quot;</span>, extra=&#123;</span><br><span class="line">    <span class="string">&quot;order_id&quot;</span>: <span class="string">&quot;ORD-12345&quot;</span>,</span><br><span class="line">    <span class="string">&quot;user_id&quot;</span>: <span class="string">&quot;U-678&quot;</span>,</span><br><span class="line">    <span class="string">&quot;amount&quot;</span>: <span class="number">99.99</span>,</span><br><span class="line">    <span class="string">&quot;status&quot;</span>: <span class="string">&quot;created&quot;</span>,</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 3. 敏感信息脱敏</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">log_safe_user</span>(<span class="params">user</span>):</span><br><span class="line">    logger.info(<span class="string">f&quot;用户操作: id=<span class="subst">&#123;user.<span class="built_in">id</span>&#125;</span>, email=<span class="subst">&#123;mask_email(user.email)&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">mask_email</span>(<span class="params">email</span>):</span><br><span class="line">    <span class="keyword">if</span> <span class="string">&quot;@&quot;</span> <span class="keyword">not</span> <span class="keyword">in</span> email:</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;***&quot;</span></span><br><span class="line">    name, domain = email.split(<span class="string">&quot;@&quot;</span>)</span><br><span class="line">    <span class="keyword">return</span> <span class="string">f&quot;<span class="subst">&#123;name[:<span class="number">2</span>]&#125;</span>***@<span class="subst">&#123;domain&#125;</span>&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 4. 使用上下文管理器记录操作耗时</span></span><br><span class="line"><span class="meta">@contextmanager</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">log_duration</span>(<span class="params">operation</span>):</span><br><span class="line">    logger.info(<span class="string">f&quot;开始: <span class="subst">&#123;operation&#125;</span>&quot;</span>)</span><br><span class="line">    start = time.perf_counter()</span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        <span class="keyword">yield</span></span><br><span class="line">    <span class="keyword">finally</span>:</span><br><span class="line">        elapsed = time.perf_counter() - start</span><br><span class="line">        logger.info(<span class="string">f&quot;完成: <span class="subst">&#123;operation&#125;</span>, 耗时: <span class="subst">&#123;elapsed:<span class="number">.3</span>f&#125;</span>s&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">with</span> log_duration(<span class="string">&quot;用户数据同步&quot;</span>):</span><br><span class="line">    time.sleep(<span class="number">1.5</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 5. 使用 % 格式化（logging 推荐）或 f-string</span></span><br><span class="line">logger.info(<span class="string">&quot;用户 %s 登录成功&quot;</span>, user_id)    <span class="comment"># % 格式（推荐）</span></span><br><span class="line">logger.info(<span class="string">f&quot;用户 <span class="subst">&#123;user_id&#125;</span> 登录成功&quot;</span>)      <span class="comment"># f-string（也可用）</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 6. 模块级 logger（推荐做法）</span></span><br><span class="line"><span class="comment"># 在每个模块顶部：</span></span><br><span class="line"><span class="comment"># logger = logging.getLogger(__name__)</span></span><br><span class="line"><span class="comment"># __name__ 会自动变成模块路径，便于追踪来源</span></span><br></pre></td></tr></table></figure><blockquote><p><strong>面试真题</strong>：<code>logger.exception()</code> 和 <code>logger.error()</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">try</span>:</span><br><span class="line">    <span class="number">1</span> / <span class="number">0</span></span><br><span class="line"><span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">    logger.error(<span class="string">f&quot;出错了: <span class="subst">&#123;e&#125;</span>&quot;</span>)      <span class="comment"># 只有错误信息</span></span><br><span class="line">    logger.exception(<span class="string">&quot;出错了&quot;</span>)       <span class="comment"># 错误信息 + 完整 traceback</span></span><br></pre></td></tr></table></figure><p><strong>答案</strong>：<code>logger.exception()</code> 等价于 <code>logger.error(..., exc_info=True)</code>，会自动附加完整的堆栈跟踪信息。只应在 <code>except</code> 块中使用。</p></blockquote><hr><h2 id="6-调试"><a href="#6-调试" class="headerlink" title="6. 调试"></a>6. 调试</h2><h3 id="6-1-pdb-交互式调试"><a href="#6-1-pdb-交互式调试" class="headerlink" title="6.1 pdb 交互式调试"></a>6.1 <code>pdb</code> 交互式调试</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><span class="line"><span class="keyword">import</span> pdb</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">buggy_function</span>(<span class="params">a, b</span>):</span><br><span class="line">    result = a + b</span><br><span class="line">    pdb.set_trace()     <span class="comment"># 在此处进入调试器（Python 3.7+ 可用 breakpoint()）</span></span><br><span class="line">    result = result / <span class="number">0</span>  <span class="comment"># 故意制造错误</span></span><br><span class="line">    <span class="keyword">return</span> result</span><br><span class="line"></span><br><span class="line"><span class="comment"># buggy_function(1, 2)</span></span><br></pre></td></tr></table></figure><p><strong>pdb 常用命令</strong>：</p><table><thead><tr><th>命令</th><th>缩写</th><th>说明</th></tr></thead><tbody><tr><td><code>list</code></td><td><code>l</code></td><td>显示当前行附近的代码</td></tr><tr><td><code>next</code></td><td><code>n</code></td><td>执行下一行（不进入函数内部）</td></tr><tr><td><code>step</code></td><td><code>s</code></td><td>执行下一行（进入函数内部）</td></tr><tr><td><code>continue</code></td><td><code>c</code></td><td>继续执行直到下一个断点</td></tr><tr><td><code>return</code></td><td><code>r</code></td><td>执行直到当前函数返回</td></tr><tr><td><code>print(var)</code></td><td><code>p var</code></td><td>打印变量值</td></tr><tr><td><code>pp var</code></td><td>-</td><td>美化打印变量</td></tr><tr><td><code>where</code></td><td><code>w</code></td><td>显示调用栈</td></tr><tr><td><code>up</code> &#x2F; <code>down</code></td><td><code>u</code> &#x2F; <code>d</code></td><td>在调用栈中上下移动</td></tr><tr><td><code>break</code></td><td><code>b</code></td><td>设置断点</td></tr><tr><td><code>args</code></td><td><code>a</code></td><td>显示当前函数参数</td></tr><tr><td><code>quit</code></td><td><code>q</code></td><td>退出调试器</td></tr></tbody></table><p><strong>pdb 调试流程</strong>：</p><figure class="highlight plaintext"><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><span class="line">python -m pdb script.py</span><br><span class="line"></span><br><span class="line">&gt; /path/to/script.py(1)&lt;module&gt;()</span><br><span class="line">-&gt; def main():</span><br><span class="line">(Pdb) l                    # 查看代码</span><br><span class="line">(Pdb) b 10                 # 在第 10 行设置断点</span><br><span class="line">(Pdb) c                    # 运行到断点</span><br><span class="line">(Pdb) p variable           # 查看变量值</span><br><span class="line">(Pdb) n                    # 执行下一行</span><br><span class="line">(Pdb) s                    # 进入函数</span><br><span class="line">(Pdb) w                    # 查看调用栈</span><br><span class="line">(Pdb) q                    # 退出</span><br></pre></td></tr></table></figure><h3 id="6-2-breakpoint-（3-7-）"><a href="#6-2-breakpoint-（3-7-）" class="headerlink" title="6.2 breakpoint()（3.7+）"></a>6.2 <code>breakpoint()</code>（3.7+）</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></pre></td><td class="code"><pre><span class="line"><span class="comment"># Python 3.7+ 推荐使用 breakpoint() 替代 pdb.set_trace()</span></span><br><span class="line"><span class="comment"># 环境变量 PYTHONBREAKPOINT=0 可以禁用所有 breakpoint()</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">calculate</span>(<span class="params">x, y</span>):</span><br><span class="line">    temp = x * <span class="number">2</span></span><br><span class="line">    <span class="built_in">breakpoint</span>()    <span class="comment"># 进入调试器，支持 Tab 补全</span></span><br><span class="line">    <span class="keyword">return</span> temp + y</span><br><span class="line"></span><br><span class="line"><span class="comment"># 设置其他调试器：PYTHONBREAKPOINT=ipdb.set_trace</span></span><br><span class="line"><span class="comment"># ipdb 是增强版 pdb，支持语法高亮和更好的补全</span></span><br></pre></td></tr></table></figure><p><strong>breakpoint() 的配置方式</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><span class="line"><span class="comment"># 1. 环境变量</span></span><br><span class="line"><span class="comment"># export PYTHONBREAKPOINT=0                  # 禁用所有断点</span></span><br><span class="line"><span class="comment"># export PYTHONBREAKPOINT=ipdb.set_trace     # 使用 ipdb</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 2. 代码中临时配置</span></span><br><span class="line"><span class="keyword">import</span> sys</span><br><span class="line">sys.breakpointhook = <span class="keyword">lambda</span>: <span class="literal">None</span>  <span class="comment"># 临时禁用</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 3. 上下文管理器（3.11+）</span></span><br><span class="line"><span class="keyword">from</span> contextlib <span class="keyword">import</span> suppress</span><br><span class="line"></span><br><span class="line"><span class="keyword">with</span> suppress(KeyboardInterrupt):</span><br><span class="line">    calculate(<span class="number">3</span>, <span class="number">4</span>)</span><br></pre></td></tr></table></figure><h3 id="6-3-事后调试（Post-mortem-Debugging）"><a href="#6-3-事后调试（Post-mortem-Debugging）" class="headerlink" title="6.3 事后调试（Post-mortem Debugging）"></a>6.3 事后调试（Post-mortem Debugging）</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><span class="line"><span class="keyword">import</span> pdb</span><br><span class="line"><span class="keyword">import</span> sys</span><br><span class="line"><span class="keyword">import</span> traceback</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">main</span>():</span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        do_something_risky()</span><br><span class="line">    <span class="keyword">except</span> Exception:</span><br><span class="line">        <span class="comment"># 捕获异常后自动进入调试器</span></span><br><span class="line">        traceback.print_exc()</span><br><span class="line">        pdb.post_mortem(sys.exc_info()[<span class="number">2</span>])</span><br><span class="line"></span><br><span class="line"><span class="comment"># 或者使用异常钩子，全局生效</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">debug_excepthook</span>(<span class="params"><span class="built_in">type</span>, value, tb</span>):</span><br><span class="line">    traceback.print_exception(<span class="built_in">type</span>, value, tb)</span><br><span class="line">    pdb.post_mortem(tb)</span><br><span class="line"></span><br><span class="line">sys.excepthook = debug_excepthook</span><br></pre></td></tr></table></figure><p><strong>事后调试的场景</strong>：</p><figure class="highlight plaintext"><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><span class="line">┌─────────────────────────────────────────────────────────┐</span><br><span class="line">│                    事后调试场景                          │</span><br><span class="line">├─────────────────────────────────────────────────────────┤</span><br><span class="line">│                                                         │</span><br><span class="line">│  场景 1：异常已经发生，想查看当时的变量状态                 │</span><br><span class="line">│    → pdb.post_mortem(tb)                               │</span><br><span class="line">│                                                         │</span><br><span class="line">│  场景 2：生产环境崩溃后，想分析 core dump                 │</span><br><span class="line">│    → 使用 faulthandler 模块生成 traceback                │</span><br><span class="line">│                                                         │</span><br><span class="line">│  场景 3：测试失败后，自动进入调试器                       │</span><br><span class="line">│    → pytest --pdb 或 unittest 的调试模式                 │</span><br><span class="line">│                                                         │</span><br><span class="line">└─────────────────────────────────────────────────────────┘</span><br></pre></td></tr></table></figure><h3 id="6-4-IDE-调试技巧"><a href="#6-4-IDE-调试技巧" class="headerlink" title="6.4 IDE 调试技巧"></a>6.4 IDE 调试技巧</h3><p><strong>PyCharm &#x2F; VS Code 断点调试核心操作</strong>：</p><ol><li><strong>条件断点</strong>：右键断点 → 添加条件（如 <code>user_id == 123</code>），只在满足条件时暂停</li><li><strong>日志断点</strong>：断点不暂停，但记录表达式值到控制台</li><li><strong>异常断点</strong>：在特定异常抛出时自动暂停（PyCharm: Run → View Breakpoints → Python Exception Breakpoint）</li><li><strong>表达式求值</strong>：暂停时在 Watch 窗口添加表达式，实时查看值变化</li><li><strong>Step Into &#x2F; Step Over &#x2F; Step Out</strong>：精细控制执行流程</li></ol><p><strong>VS Code 调试配置（launch.json）</strong>：</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><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><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;version&quot;</span><span class="punctuation">:</span> <span class="string">&quot;0.2.0&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;configurations&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">        <span class="punctuation">&#123;</span></span><br><span class="line">            <span class="attr">&quot;name&quot;</span><span class="punctuation">:</span> <span class="string">&quot;Python: 当前文件&quot;</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">&quot;type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;python&quot;</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">&quot;request&quot;</span><span class="punctuation">:</span> <span class="string">&quot;launch&quot;</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">&quot;program&quot;</span><span class="punctuation">:</span> <span class="string">&quot;$&#123;file&#125;&quot;</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">&quot;console&quot;</span><span class="punctuation">:</span> <span class="string">&quot;integratedTerminal&quot;</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">&quot;justMyCode&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">false</span></span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">&quot;env&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">                <span class="attr">&quot;PYTHONPATH&quot;</span><span class="punctuation">:</span> <span class="string">&quot;$&#123;workspaceFolder&#125;&quot;</span></span><br><span class="line">            <span class="punctuation">&#125;</span></span><br><span class="line">        <span class="punctuation">&#125;</span></span><br><span class="line">    <span class="punctuation">]</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><h3 id="6-5-实用调试技巧"><a href="#6-5-实用调试技巧" class="headerlink" title="6.5 实用调试技巧"></a>6.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><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><span class="line"><span class="comment"># 1. 使用 icecream 替代 print 调试</span></span><br><span class="line"><span class="comment"># pip install icecream</span></span><br><span class="line"><span class="keyword">from</span> icecream <span class="keyword">import</span> ic</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">process</span>(<span class="params">data</span>):</span><br><span class="line">    data = [x.strip() <span class="keyword">for</span> x <span class="keyword">in</span> data]</span><br><span class="line">    ic(data)                        <span class="comment"># ic| data: [&#x27;a&#x27;, &#x27;b&#x27;, &#x27;c&#x27;]</span></span><br><span class="line">    result = <span class="built_in">sum</span>(<span class="built_in">len</span>(x) <span class="keyword">for</span> x <span class="keyword">in</span> data)</span><br><span class="line">    ic(result)                      <span class="comment"># ic| result: 3</span></span><br><span class="line">    <span class="keyword">return</span> result</span><br><span class="line"></span><br><span class="line"><span class="comment"># 2. 使用 rich 库美化终端输出</span></span><br><span class="line"><span class="comment"># pip install rich</span></span><br><span class="line"><span class="keyword">from</span> rich <span class="keyword">import</span> <span class="built_in">print</span> <span class="keyword">as</span> rprint</span><br><span class="line"><span class="keyword">from</span> rich.console <span class="keyword">import</span> Console</span><br><span class="line"><span class="keyword">from</span> rich.traceback <span class="keyword">import</span> install</span><br><span class="line"></span><br><span class="line">install()   <span class="comment"># 美化异常 traceback</span></span><br><span class="line">console = Console()</span><br><span class="line">console.log(<span class="string">&quot;Hello, [bold red]World![/bold red]&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 3. 使用 locals() 和 dir() 检查当前作用域</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">mystery</span>(<span class="params">a, b</span>):</span><br><span class="line">    <span class="built_in">breakpoint</span>()</span><br><span class="line">    <span class="comment"># 在 pdb 中:</span></span><br><span class="line">    <span class="comment"># p locals()   → 查看所有局部变量</span></span><br><span class="line">    <span class="comment"># p dir(a)     → 查看对象 a 的所有属性和方法</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 4. 使用 sys._getframe() 查看调用栈</span></span><br><span class="line"><span class="keyword">import</span> sys</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">who_called_me</span>():</span><br><span class="line">    frame = sys._getframe(<span class="number">1</span>)    <span class="comment"># 获取调用者的帧</span></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;被 <span class="subst">&#123;frame.f_code.co_name&#125;</span> 调用，在 <span class="subst">&#123;frame.f_code.co_filename&#125;</span>:<span class="subst">&#123;frame.f_lineno&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">caller</span>():</span><br><span class="line">    who_called_me()   <span class="comment"># 被 caller 调用，在 xxx.py:行号</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 5. 使用 inspect 模块获取详细信息</span></span><br><span class="line"><span class="keyword">import</span> inspect</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">debug_info</span>():</span><br><span class="line">    frame = inspect.currentframe()</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;文件: <span class="subst">&#123;frame.f_code.co_filename&#125;</span>&quot;</span>)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;函数: <span class="subst">&#123;frame.f_code.co_name&#125;</span>&quot;</span>)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;行号: <span class="subst">&#123;frame.f_lineno&#125;</span>&quot;</span>)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;局部变量: <span class="subst">&#123;frame.f_locals&#125;</span>&quot;</span>)</span><br></pre></td></tr></table></figure><hr><h2 id="7-警告控制：warnings-模块"><a href="#7-警告控制：warnings-模块" class="headerlink" title="7. 警告控制：warnings 模块"></a>7. 警告控制：<code>warnings</code> 模块</h2><h3 id="7-1-基本用法"><a href="#7-1-基本用法" class="headerlink" title="7.1 基本用法"></a>7.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><span class="line"><span class="keyword">import</span> warnings</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">deprecated_function</span>():</span><br><span class="line">    warnings.warn(<span class="string">&quot;deprecated_function 已废弃，请使用 new_function&quot;</span>, DeprecationWarning)</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;旧功能&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">risky_operation</span>():</span><br><span class="line">    warnings.warn(<span class="string">&quot;此操作可能导致数据丢失&quot;</span>, UserWarning)</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;完成&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 控制警告行为</span></span><br><span class="line"><span class="comment"># warnings.filterwarnings(&quot;ignore&quot;)              # 忽略所有警告</span></span><br><span class="line"><span class="comment"># warnings.filterwarnings(&quot;error&quot;)               # 将警告转为异常</span></span><br><span class="line"><span class="comment"># warnings.filterwarnings(&quot;ignore&quot;, category=DeprecationWarning)</span></span><br><span class="line"><span class="comment"># warnings.filterwarnings(&quot;once&quot;, message=&quot;.*数据丢失.*&quot;)</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 运行时的控制方式</span></span><br><span class="line"><span class="comment"># python -W ignore script.py                     # 忽略所有警告</span></span><br><span class="line"><span class="comment"># python -W error script.py                      # 所有警告转为异常</span></span><br><span class="line"><span class="comment"># python -W &quot;ignore::DeprecationWarning&quot; script.py</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用上下文管理器临时控制</span></span><br><span class="line"><span class="keyword">with</span> warnings.catch_warnings():</span><br><span class="line">    warnings.simplefilter(<span class="string">&quot;ignore&quot;</span>)</span><br><span class="line">    deprecated_function()   <span class="comment"># 警告被抑制</span></span><br></pre></td></tr></table></figure><h3 id="7-2-废弃-API-标记"><a href="#7-2-废弃-API-标记" class="headerlink" title="7.2 废弃 API 标记"></a>7.2 废弃 API 标记</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><span class="line"><span class="keyword">import</span> warnings</span><br><span class="line"><span class="keyword">import</span> functools</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">deprecated</span>(<span class="params">reason=<span class="string">&quot;&quot;</span>, removed_in=<span class="literal">None</span></span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;装饰器：标记函数为废弃。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">decorator</span>(<span class="params">func</span>):</span><br><span class="line"><span class="meta">        @functools.wraps(<span class="params">func</span>)</span></span><br><span class="line">        <span class="keyword">def</span> <span class="title function_">wrapper</span>(<span class="params">*args, **kwargs</span>):</span><br><span class="line">            msg = <span class="string">f&quot;<span class="subst">&#123;func.__name__&#125;</span> 已废弃&quot;</span></span><br><span class="line">            <span class="keyword">if</span> reason:</span><br><span class="line">                msg += <span class="string">f&quot;: <span class="subst">&#123;reason&#125;</span>&quot;</span></span><br><span class="line">            <span class="keyword">if</span> removed_in:</span><br><span class="line">                msg += <span class="string">f&quot;（将在 <span class="subst">&#123;removed_in&#125;</span> 中移除）&quot;</span></span><br><span class="line">            warnings.warn(msg, DeprecationWarning, stacklevel=<span class="number">2</span>)</span><br><span class="line">            <span class="keyword">return</span> func(*args, **kwargs)</span><br><span class="line">        <span class="keyword">return</span> wrapper</span><br><span class="line">    <span class="keyword">return</span> decorator</span><br><span class="line"></span><br><span class="line"><span class="meta">@deprecated(<span class="params">reason=<span class="string">&quot;请使用 new_api()&quot;</span>, removed_in=<span class="string">&quot;v3.0&quot;</span></span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">old_api</span>():</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;旧 API&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用</span></span><br><span class="line">result = old_api()</span><br><span class="line"><span class="comment"># DeprecationWarning: old_api 已废弃: 请使用 new_api()（将在 v3.0 中移除）</span></span><br></pre></td></tr></table></figure><p><strong>警告类别</strong>：</p><table><thead><tr><th>类别</th><th>用途</th></tr></thead><tbody><tr><td><code>UserWarning</code></td><td>用户代码产生的警告</td></tr><tr><td><code>DeprecationWarning</code></td><td>废弃警告（开发者可见）</td></tr><tr><td><code>FutureWarning</code></td><td>将来可能改变行为的警告</td></tr><tr><td><code>SyntaxWarning</code></td><td>语法相关警告</td></tr><tr><td><code>RuntimeWarning</code></td><td>运行时相关警告</td></tr><tr><td><code>PendingDeprecationWarning</code></td><td>即将废弃的警告</td></tr></tbody></table><hr><h2 id="8-生产环境异常处理模式"><a href="#8-生产环境异常处理模式" class="headerlink" title="8. 生产环境异常处理模式"></a>8. 生产环境异常处理模式</h2><h3 id="8-1-全局异常捕获"><a href="#8-1-全局异常捕获" class="headerlink" title="8.1 全局异常捕获"></a>8.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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> sys</span><br><span class="line"><span class="keyword">import</span> logging</span><br><span class="line"><span class="keyword">import</span> traceback</span><br><span class="line"></span><br><span class="line">logger = logging.getLogger(__name__)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">global_exception_handler</span>(<span class="params">exc_type, exc_value, exc_tb</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;全局未捕获异常处理：记录并优雅退出。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">if</span> <span class="built_in">issubclass</span>(exc_type, KeyboardInterrupt):</span><br><span class="line">        <span class="comment"># 让用户正常中断</span></span><br><span class="line">        sys.__excepthook__(exc_type, exc_value, exc_tb)</span><br><span class="line">        <span class="keyword">return</span></span><br><span class="line"></span><br><span class="line">    logger.critical(</span><br><span class="line">        <span class="string">&quot;未捕获的异常&quot;</span>,</span><br><span class="line">        exc_info=(exc_type, exc_value, exc_tb)</span><br><span class="line">    )</span><br><span class="line">    <span class="comment"># 可选：发送告警通知</span></span><br><span class="line">    <span class="comment"># alert(f&quot;未捕获异常: &#123;exc_type.__name__&#125;: &#123;exc_value&#125;&quot;)</span></span><br><span class="line"></span><br><span class="line">sys.excepthook = global_exception_handler</span><br></pre></td></tr></table></figure><p><strong>Web 框架的全局异常处理</strong>（以 Flask 为例）：</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> flask <span class="keyword">import</span> Flask, jsonify</span><br><span class="line"></span><br><span class="line">app = Flask(__name__)</span><br><span class="line"></span><br><span class="line"><span class="meta">@app.errorhandler(<span class="params">Exception</span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">handle_exception</span>(<span class="params">e</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;处理所有未捕获的异常。&quot;&quot;&quot;</span></span><br><span class="line">    logger.exception(<span class="string">&quot;请求处理失败&quot;</span>)</span><br><span class="line">    <span class="keyword">return</span> jsonify(&#123;</span><br><span class="line">        <span class="string">&quot;error&quot;</span>: <span class="string">&quot;服务器内部错误&quot;</span>,</span><br><span class="line">        <span class="string">&quot;code&quot;</span>: <span class="number">500</span></span><br><span class="line">    &#125;), <span class="number">500</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@app.errorhandler(<span class="params"><span class="number">404</span></span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">handle_not_found</span>(<span class="params">e</span>):</span><br><span class="line">    <span class="keyword">return</span> jsonify(&#123;</span><br><span class="line">        <span class="string">&quot;error&quot;</span>: <span class="string">&quot;资源未找到&quot;</span>,</span><br><span class="line">        <span class="string">&quot;code&quot;</span>: <span class="number">404</span></span><br><span class="line">    &#125;), <span class="number">404</span></span><br></pre></td></tr></table></figure><h3 id="8-2-重试模式"><a href="#8-2-重试模式" class="headerlink" title="8.2 重试模式"></a>8.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><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><span class="line"><span class="keyword">import</span> time</span><br><span class="line"><span class="keyword">import</span> functools</span><br><span class="line"><span class="keyword">import</span> random</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">retry</span>(<span class="params">max_attempts=<span class="number">3</span>, delay=<span class="number">1</span>, backoff=<span class="number">2</span>, exceptions=(<span class="params">Exception,</span>)</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;重试装饰器：指数退避 + 随机抖动。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">decorator</span>(<span class="params">func</span>):</span><br><span class="line"><span class="meta">        @functools.wraps(<span class="params">func</span>)</span></span><br><span class="line">        <span class="keyword">def</span> <span class="title function_">wrapper</span>(<span class="params">*args, **kwargs</span>):</span><br><span class="line">            last_exception = <span class="literal">None</span></span><br><span class="line">            <span class="keyword">for</span> attempt <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">1</span>, max_attempts + <span class="number">1</span>):</span><br><span class="line">                <span class="keyword">try</span>:</span><br><span class="line">                    <span class="keyword">return</span> func(*args, **kwargs)</span><br><span class="line">                <span class="keyword">except</span> exceptions <span class="keyword">as</span> e:</span><br><span class="line">                    last_exception = e</span><br><span class="line">                    <span class="keyword">if</span> attempt == max_attempts:</span><br><span class="line">                        <span class="keyword">break</span></span><br><span class="line">                    sleep_time = delay * (backoff ** (attempt - <span class="number">1</span>))</span><br><span class="line">                    sleep_time += random.uniform(<span class="number">0</span>, sleep_time * <span class="number">0.1</span>)  <span class="comment"># 随机抖动</span></span><br><span class="line">                    <span class="built_in">print</span>(<span class="string">f&quot;重试 <span class="subst">&#123;attempt&#125;</span>/<span class="subst">&#123;max_attempts&#125;</span>，等待 <span class="subst">&#123;sleep_time:<span class="number">.1</span>f&#125;</span>s: <span class="subst">&#123;e&#125;</span>&quot;</span>)</span><br><span class="line">                    time.sleep(sleep_time)</span><br><span class="line">            <span class="keyword">raise</span> last_exception</span><br><span class="line">        <span class="keyword">return</span> wrapper</span><br><span class="line">    <span class="keyword">return</span> decorator</span><br><span class="line"></span><br><span class="line"><span class="meta">@retry(<span class="params">max_attempts=<span class="number">3</span>, delay=<span class="number">0.5</span>, exceptions=(<span class="params">ConnectionError, TimeoutError</span>)</span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">fetch_data</span>(<span class="params">url</span>):</span><br><span class="line">    <span class="keyword">import</span> random</span><br><span class="line">    <span class="keyword">if</span> random.random() &lt; <span class="number">0.7</span>:</span><br><span class="line">        <span class="keyword">raise</span> ConnectionError(<span class="string">&quot;连接失败&quot;</span>)</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;数据&quot;</span></span><br></pre></td></tr></table></figure><p><strong>重试策略对比</strong>：</p><figure class="highlight plaintext"><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><span class="line">┌─────────────────────────────────────────────────────────┐</span><br><span class="line">│                    重试策略对比                          │</span><br><span class="line">├──────────────┬──────────────┬──────────────────────────┤</span><br><span class="line">│ 策略          │ 等待时间      │ 适用场景                  │</span><br><span class="line">├──────────────┼──────────────┼──────────────────────────┤</span><br><span class="line">│ 固定间隔      │ 1s, 1s, 1s   │ 简单场景                  │</span><br><span class="line">│ 线性增长      │ 1s, 2s, 3s   │ 轻度拥塞                  │</span><br><span class="line">│ 指数退避      │ 1s, 2s, 4s   │ 高并发场景（推荐）         │</span><br><span class="line">│ 随机抖动      │ 0.9-1.1s     │ 避免请求风暴               │</span><br><span class="line">│ 组合策略      │ 指数+抖动     │ 生产环境最佳实践           │</span><br><span class="line">└──────────────┴──────────────┴──────────────────────────┘</span><br></pre></td></tr></table></figure><h3 id="8-3-断路器模式"><a href="#8-3-断路器模式" class="headerlink" title="8.3 断路器模式"></a>8.3 断路器模式</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> time</span><br><span class="line"><span class="keyword">from</span> collections <span class="keyword">import</span> deque</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">CircuitBreaker</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;断路器：防止反复重试已确认失败的服务。&quot;&quot;&quot;</span></span><br><span class="line">    OPEN = <span class="string">&quot;open&quot;</span>          <span class="comment"># 断路：直接拒绝请求</span></span><br><span class="line">    HALF_OPEN = <span class="string">&quot;half_open&quot;</span>  <span class="comment"># 半开：允许少量请求探测</span></span><br><span class="line">    CLOSED = <span class="string">&quot;closed&quot;</span>      <span class="comment"># 闭合：正常执行</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, failure_threshold=<span class="number">5</span>, recovery_timeout=<span class="number">60</span>, half_open_limit=<span class="number">3</span></span>):</span><br><span class="line">        <span class="variable language_">self</span>.failure_threshold = failure_threshold</span><br><span class="line">        <span class="variable language_">self</span>.recovery_timeout = recovery_timeout</span><br><span class="line">        <span class="variable language_">self</span>.half_open_limit = half_open_limit</span><br><span class="line">        <span class="variable language_">self</span>.state = <span class="variable language_">self</span>.CLOSED</span><br><span class="line">        <span class="variable language_">self</span>.failure_count = <span class="number">0</span></span><br><span class="line">        <span class="variable language_">self</span>.last_failure_time = <span class="number">0</span></span><br><span class="line">        <span class="variable language_">self</span>.half_open_requests = <span class="number">0</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__enter__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">if</span> <span class="variable language_">self</span>.state == <span class="variable language_">self</span>.OPEN:</span><br><span class="line">            <span class="keyword">if</span> time.time() - <span class="variable language_">self</span>.last_failure_time &gt; <span class="variable language_">self</span>.recovery_timeout:</span><br><span class="line">                <span class="variable language_">self</span>.state = <span class="variable language_">self</span>.HALF_OPEN</span><br><span class="line">                <span class="variable language_">self</span>.half_open_requests = <span class="number">0</span></span><br><span class="line">            <span class="keyword">else</span>:</span><br><span class="line">                <span class="keyword">raise</span> RuntimeError(<span class="string">&quot;断路器已打开，服务不可用&quot;</span>)</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> <span class="variable language_">self</span>.state == <span class="variable language_">self</span>.HALF_OPEN:</span><br><span class="line">            <span class="keyword">if</span> <span class="variable language_">self</span>.half_open_requests &gt;= <span class="variable language_">self</span>.half_open_limit:</span><br><span class="line">                <span class="keyword">raise</span> RuntimeError(<span class="string">&quot;半开状态请求已达上限&quot;</span>)</span><br><span class="line">            <span class="variable language_">self</span>.half_open_requests += <span class="number">1</span></span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__exit__</span>(<span class="params">self, exc_type, exc_val, exc_tb</span>):</span><br><span class="line">        <span class="keyword">if</span> exc_type <span class="keyword">is</span> <span class="literal">None</span>:</span><br><span class="line">            <span class="comment"># 成功：恢复</span></span><br><span class="line">            <span class="variable language_">self</span>.state = <span class="variable language_">self</span>.CLOSED</span><br><span class="line">            <span class="variable language_">self</span>.failure_count = <span class="number">0</span></span><br><span class="line">        <span class="keyword">elif</span> exc_type <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">None</span>:</span><br><span class="line">            <span class="comment"># 失败：记录</span></span><br><span class="line">            <span class="variable language_">self</span>.failure_count += <span class="number">1</span></span><br><span class="line">            <span class="variable language_">self</span>.last_failure_time = time.time()</span><br><span class="line">            <span class="keyword">if</span> <span class="variable language_">self</span>.failure_count &gt;= <span class="variable language_">self</span>.failure_threshold:</span><br><span class="line">                <span class="variable language_">self</span>.state = <span class="variable language_">self</span>.OPEN</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">False</span>  <span class="comment"># 不抑制异常</span></span><br></pre></td></tr></table></figure><p><strong>断路器状态转换</strong>：</p><figure class="highlight plaintext"><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><span class="line">┌─────────────────────────────────────────────────────────┐</span><br><span class="line">│                    断路器状态机                          │</span><br><span class="line">├─────────────────────────────────────────────────────────┤</span><br><span class="line">│                                                         │</span><br><span class="line">│     失败次数 &gt;= 阈值         成功请求                      │</span><br><span class="line">│    ┌─────────────────┐    ┌─────────────────┐           │</span><br><span class="line">│    │                 │    │                 │           │</span><br><span class="line">│    ▼                 │    ▼                 │           │</span><br><span class="line">│ ┌───────┐        ┌──────────────┐       ┌─────────┐    │</span><br><span class="line">│ │CLOSED │───────→│    OPEN      │───────→│HALF_OPEN│    │</span><br><span class="line">│ │ 闭合   │        │    断路       │       │  半开    │    │</span><br><span class="line">│ │正常执行│←───────│ 直接拒绝请求  │←───────│ 少量探测 │    │</span><br><span class="line">│ └───────┘        └──────────────┘       └─────────┘    │</span><br><span class="line">│     ↑                 │                     │           │</span><br><span class="line">│     │                 │                     │           │</span><br><span class="line">│     │           超时时间到达           失败请求          │</span><br><span class="line">│     │                 │                     │           │</span><br><span class="line">│     └─────────────────┴─────────────────────┘           │</span><br><span class="line">│                                                         │</span><br><span class="line">└─────────────────────────────────────────────────────────┘</span><br></pre></td></tr></table></figure><h3 id="8-4-优雅降级"><a href="#8-4-优雅降级" class="headerlink" title="8.4 优雅降级"></a>8.4 优雅降级</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><span class="line"><span class="keyword">class</span> <span class="title class_">UserService</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, db, cache</span>):</span><br><span class="line">        <span class="variable language_">self</span>.db = db</span><br><span class="line">        <span class="variable language_">self</span>.cache = cache</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_user</span>(<span class="params">self, user_id</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;获取用户信息，多层降级。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="comment"># 第一级：尝试缓存</span></span><br><span class="line">        <span class="keyword">try</span>:</span><br><span class="line">            user = <span class="variable language_">self</span>.cache.get(<span class="string">f&quot;user:<span class="subst">&#123;user_id&#125;</span>&quot;</span>)</span><br><span class="line">            <span class="keyword">if</span> user:</span><br><span class="line">                <span class="keyword">return</span> user</span><br><span class="line">        <span class="keyword">except</span> Exception:</span><br><span class="line">            logger.warning(<span class="string">&quot;缓存不可用，回退到数据库&quot;</span>)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 第二级：尝试数据库</span></span><br><span class="line">        <span class="keyword">try</span>:</span><br><span class="line">            user = <span class="variable language_">self</span>.db.find_user(user_id)</span><br><span class="line">            <span class="keyword">if</span> user:</span><br><span class="line">                <span class="comment"># 尝试回写缓存（失败不影响返回）</span></span><br><span class="line">                <span class="keyword">try</span>:</span><br><span class="line">                    <span class="variable language_">self</span>.cache.<span class="built_in">set</span>(<span class="string">f&quot;user:<span class="subst">&#123;user_id&#125;</span>&quot;</span>, user, ttl=<span class="number">300</span>)</span><br><span class="line">                <span class="keyword">except</span> Exception:</span><br><span class="line">                    <span class="keyword">pass</span></span><br><span class="line">                <span class="keyword">return</span> user</span><br><span class="line">        <span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">            logger.error(<span class="string">f&quot;数据库查询失败: <span class="subst">&#123;e&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 第三级：返回默认值</span></span><br><span class="line">        logger.warning(<span class="string">f&quot;无法获取用户 <span class="subst">&#123;user_id&#125;</span>，返回默认值&quot;</span>)</span><br><span class="line">        <span class="keyword">return</span> &#123;<span class="string">&quot;id&quot;</span>: user_id, <span class="string">&quot;name&quot;</span>: <span class="string">&quot;未知用户&quot;</span>, <span class="string">&quot;status&quot;</span>: <span class="string">&quot;unknown&quot;</span>&#125;</span><br></pre></td></tr></table></figure><p><strong>优雅降级的设计原则</strong>：</p><figure class="highlight plaintext"><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><span class="line">┌─────────────────────────────────────────────────────────┐</span><br><span class="line">│                    优雅降级策略                          │</span><br><span class="line">├─────────────────────────────────────────────────────────┤</span><br><span class="line">│                                                         │</span><br><span class="line">│  1. 多级缓存：                                          │</span><br><span class="line">│     内存缓存 → Redis → 数据库 → 默认值                   │</span><br><span class="line">│                                                         │</span><br><span class="line">│  2. 服务降级：                                          │</span><br><span class="line">│     核心功能优先，非核心功能可失败                        │</span><br><span class="line">│                                                         │</span><br><span class="line">│  3. 错误隔离：                                          │</span><br><span class="line">│     单个服务失败不影响其他服务                            │</span><br><span class="line">│                                                         │</span><br><span class="line">│  4. 熔断保护：                                          │</span><br><span class="line">│     断路器防止级联故障                                   │</span><br><span class="line">│                                                         │</span><br><span class="line">│  5. 监控告警：                                          │</span><br><span class="line">│     降级时记录日志并发送告警                              │</span><br><span class="line">│                                                         │</span><br><span class="line">└─────────────────────────────────────────────────────────┘</span><br></pre></td></tr></table></figure><hr><h2 id="9-工作实战场景"><a href="#9-工作实战场景" class="headerlink" title="9. 工作实战场景"></a>9. 工作实战场景</h2><h3 id="9-1-API-服务的异常处理中间件"><a href="#9-1-API-服务的异常处理中间件" class="headerlink" title="9.1 API 服务的异常处理中间件"></a>9.1 API 服务的异常处理中间件</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> functools <span class="keyword">import</span> wraps</span><br><span class="line"><span class="keyword">from</span> flask <span class="keyword">import</span> jsonify</span><br><span class="line"><span class="keyword">import</span> logging</span><br><span class="line"></span><br><span class="line">logger = logging.getLogger(__name__)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">error_handler</span>(<span class="params">f</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;API 异常处理装饰器：统一错误响应格式。&quot;&quot;&quot;</span></span><br><span class="line"><span class="meta">    @wraps(<span class="params">f</span>)</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">decorated_function</span>(<span class="params">*args, **kwargs</span>):</span><br><span class="line">        <span class="keyword">try</span>:</span><br><span class="line">            <span class="keyword">return</span> f(*args, **kwargs)</span><br><span class="line">        <span class="keyword">except</span> ValidationError <span class="keyword">as</span> e:</span><br><span class="line">            logger.warning(<span class="string">f&quot;参数校验失败: <span class="subst">&#123;e.message&#125;</span>&quot;</span>)</span><br><span class="line">            <span class="keyword">return</span> jsonify(&#123;</span><br><span class="line">                <span class="string">&quot;code&quot;</span>: <span class="number">400</span>,</span><br><span class="line">                <span class="string">&quot;message&quot;</span>: e.message,</span><br><span class="line">                <span class="string">&quot;field&quot;</span>: e.field</span><br><span class="line">            &#125;), <span class="number">400</span></span><br><span class="line">        <span class="keyword">except</span> AuthenticationError <span class="keyword">as</span> e:</span><br><span class="line">            logger.warning(<span class="string">f&quot;认证失败: <span class="subst">&#123;e.message&#125;</span>&quot;</span>)</span><br><span class="line">            <span class="keyword">return</span> jsonify(&#123;</span><br><span class="line">                <span class="string">&quot;code&quot;</span>: <span class="number">401</span>,</span><br><span class="line">                <span class="string">&quot;message&quot;</span>: <span class="string">&quot;未授权，请重新登录&quot;</span></span><br><span class="line">            &#125;), <span class="number">401</span></span><br><span class="line">        <span class="keyword">except</span> NotFoundError <span class="keyword">as</span> e:</span><br><span class="line">            logger.warning(<span class="string">f&quot;资源未找到: <span class="subst">&#123;e.message&#125;</span>&quot;</span>)</span><br><span class="line">            <span class="keyword">return</span> jsonify(&#123;</span><br><span class="line">                <span class="string">&quot;code&quot;</span>: <span class="number">404</span>,</span><br><span class="line">                <span class="string">&quot;message&quot;</span>: e.message</span><br><span class="line">            &#125;), <span class="number">404</span></span><br><span class="line">        <span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">            logger.exception(<span class="string">f&quot;服务器内部错误: <span class="subst">&#123;e&#125;</span>&quot;</span>)</span><br><span class="line">            <span class="keyword">return</span> jsonify(&#123;</span><br><span class="line">                <span class="string">&quot;code&quot;</span>: <span class="number">500</span>,</span><br><span class="line">                <span class="string">&quot;message&quot;</span>: <span class="string">&quot;服务器内部错误，请稍后重试&quot;</span></span><br><span class="line">            &#125;), <span class="number">500</span></span><br><span class="line">    <span class="keyword">return</span> decorated_function</span><br><span class="line"></span><br><span class="line"><span class="meta">@app.route(<span class="params"><span class="string">&quot;/api/users/&lt;int:user_id&gt;&quot;</span>, methods=[<span class="string">&quot;GET&quot;</span>]</span>)</span></span><br><span class="line"><span class="meta">@error_handler</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">get_user_api</span>(<span class="params">user_id</span>):</span><br><span class="line">    user = get_user(user_id)</span><br><span class="line">    <span class="keyword">return</span> jsonify(user.serialize())</span><br></pre></td></tr></table></figure><p><strong>生产环境的响应格式规范</strong>：</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><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;code&quot;</span><span class="punctuation">:</span> <span class="number">200</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;message&quot;</span><span class="punctuation">:</span> <span class="string">&quot;success&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;data&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span>...<span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;request_id&quot;</span><span class="punctuation">:</span> <span class="string">&quot;abc-123&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;timestamp&quot;</span><span class="punctuation">:</span> <span class="number">1699999999</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><h3 id="9-2-数据处理管道的异常日志"><a href="#9-2-数据处理管道的异常日志" class="headerlink" title="9.2 数据处理管道的异常日志"></a>9.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><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><span class="line"><span class="keyword">import</span> logging</span><br><span class="line"><span class="keyword">from</span> datetime <span class="keyword">import</span> datetime</span><br><span class="line"></span><br><span class="line">logger = logging.getLogger(__name__)</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">DataPipeline</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;数据处理管道：记录每个阶段的处理情况。&quot;&quot;&quot;</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, name</span>):</span><br><span class="line">        <span class="variable language_">self</span>.name = name</span><br><span class="line">        <span class="variable language_">self</span>.start_time = <span class="literal">None</span></span><br><span class="line">        <span class="variable language_">self</span>.processed_count = <span class="number">0</span></span><br><span class="line">        <span class="variable language_">self</span>.failed_count = <span class="number">0</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__enter__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="variable language_">self</span>.start_time = datetime.now()</span><br><span class="line">        logger.info(<span class="string">f&quot;[<span class="subst">&#123;self.name&#125;</span>] 管道开始&quot;</span>)</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__exit__</span>(<span class="params">self, exc_type, exc_val, exc_tb</span>):</span><br><span class="line">        elapsed = datetime.now() - <span class="variable language_">self</span>.start_time</span><br><span class="line">        logger.info(</span><br><span class="line">            <span class="string">f&quot;[<span class="subst">&#123;self.name&#125;</span>] 管道结束 | &quot;</span></span><br><span class="line">            <span class="string">f&quot;处理: <span class="subst">&#123;self.processed_count&#125;</span> | &quot;</span></span><br><span class="line">            <span class="string">f&quot;失败: <span class="subst">&#123;self.failed_count&#125;</span> | &quot;</span></span><br><span class="line">            <span class="string">f&quot;耗时: <span class="subst">&#123;elapsed.total_seconds():<span class="number">.2</span>f&#125;</span>s&quot;</span></span><br><span class="line">        )</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">process_item</span>(<span class="params">self, item, handler</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;处理单个数据项，记录成功/失败。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">try</span>:</span><br><span class="line">            result = handler(item)</span><br><span class="line">            <span class="variable language_">self</span>.processed_count += <span class="number">1</span></span><br><span class="line">            <span class="keyword">return</span> result</span><br><span class="line">        <span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">            <span class="variable language_">self</span>.failed_count += <span class="number">1</span></span><br><span class="line">            logger.error(<span class="string">f&quot;[<span class="subst">&#123;self.name&#125;</span>] 处理失败: item=<span class="subst">&#123;item&#125;</span>, error=<span class="subst">&#123;e&#125;</span>&quot;</span>)</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">None</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用示例</span></span><br><span class="line"><span class="keyword">with</span> DataPipeline(<span class="string">&quot;用户数据同步&quot;</span>) <span class="keyword">as</span> pipeline:</span><br><span class="line">    <span class="keyword">for</span> user <span class="keyword">in</span> source_users:</span><br><span class="line">        pipeline.process_item(user, sync_user)</span><br></pre></td></tr></table></figure><h3 id="9-3-定时任务的异常处理"><a href="#9-3-定时任务的异常处理" class="headerlink" title="9.3 定时任务的异常处理"></a>9.3 定时任务的异常处理</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><span class="line"><span class="keyword">import</span> schedule</span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"><span class="keyword">import</span> logging</span><br><span class="line"></span><br><span class="line">logger = logging.getLogger(__name__)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">safe_job</span>(<span class="params">job_name, job_func, *args, **kwargs</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;安全的定时任务包装：异常捕获和重试。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">wrapper</span>():</span><br><span class="line">        <span class="keyword">try</span>:</span><br><span class="line">            logger.info(<span class="string">f&quot;[<span class="subst">&#123;job_name&#125;</span>] 任务开始&quot;</span>)</span><br><span class="line">            job_func(*args, **kwargs)</span><br><span class="line">            logger.info(<span class="string">f&quot;[<span class="subst">&#123;job_name&#125;</span>] 任务完成&quot;</span>)</span><br><span class="line">        <span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">            logger.exception(<span class="string">f&quot;[<span class="subst">&#123;job_name&#125;</span>] 任务失败&quot;</span>)</span><br><span class="line">            <span class="comment"># 可选：发送告警</span></span><br><span class="line">            <span class="comment"># alert(f&quot;定时任务 &#123;job_name&#125; 失败: &#123;e&#125;&quot;)</span></span><br><span class="line">    <span class="keyword">return</span> wrapper</span><br><span class="line"></span><br><span class="line"><span class="comment"># 注册定时任务</span></span><br><span class="line">schedule.every(<span class="number">10</span>).minutes.do(safe_job(<span class="string">&quot;数据统计&quot;</span>, run_statistics))</span><br><span class="line">schedule.every().hour.do(safe_job(<span class="string">&quot;缓存清理&quot;</span>, clean_cache))</span><br><span class="line">schedule.every().day.at(<span class="string">&quot;02:00&quot;</span>).do(safe_job(<span class="string">&quot;每日备份&quot;</span>, daily_backup))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 运行调度器</span></span><br><span class="line"><span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        schedule.run_pending()</span><br><span class="line">        time.sleep(<span class="number">1</span>)</span><br><span class="line">    <span class="keyword">except</span> KeyboardInterrupt:</span><br><span class="line">        logger.info(<span class="string">&quot;调度器停止&quot;</span>)</span><br><span class="line">        <span class="keyword">break</span></span><br><span class="line">    <span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">        logger.exception(<span class="string">&quot;调度器异常&quot;</span>)</span><br><span class="line">        time.sleep(<span class="number">60</span>)</span><br></pre></td></tr></table></figure><h3 id="9-4-文件操作的安全封装"><a href="#9-4-文件操作的安全封装" class="headerlink" title="9.4 文件操作的安全封装"></a>9.4 文件操作的安全封装</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><span class="line"><span class="keyword">import</span> os</span><br><span class="line"><span class="keyword">import</span> logging</span><br><span class="line"></span><br><span class="line">logger = logging.getLogger(__name__)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">safe_write_file</span>(<span class="params">filepath, content, backup=<span class="literal">True</span></span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;安全写文件：先写临时文件，再原子替换。&quot;&quot;&quot;</span></span><br><span class="line">    temp_file = <span class="string">f&quot;<span class="subst">&#123;filepath&#125;</span>.tmp&quot;</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        <span class="comment"># 写入临时文件</span></span><br><span class="line">        <span class="keyword">with</span> <span class="built_in">open</span>(temp_file, <span class="string">&quot;w&quot;</span>, encoding=<span class="string">&quot;utf-8&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">            f.write(content)</span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 备份原文件</span></span><br><span class="line">        <span class="keyword">if</span> backup <span class="keyword">and</span> os.path.exists(filepath):</span><br><span class="line">            backup_file = <span class="string">f&quot;<span class="subst">&#123;filepath&#125;</span>.bak&quot;</span></span><br><span class="line">            os.replace(filepath, backup_file)</span><br><span class="line">            logger.info(<span class="string">f&quot;已备份原文件: <span class="subst">&#123;backup_file&#125;</span>&quot;</span>)</span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 原子替换</span></span><br><span class="line">        os.replace(temp_file, filepath)</span><br><span class="line">        logger.info(<span class="string">f&quot;文件写入成功: <span class="subst">&#123;filepath&#125;</span>&quot;</span>)</span><br><span class="line">        </span><br><span class="line">    <span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">        <span class="comment"># 清理临时文件</span></span><br><span class="line">        <span class="keyword">if</span> os.path.exists(temp_file):</span><br><span class="line">            os.remove(temp_file)</span><br><span class="line">        logger.error(<span class="string">f&quot;文件写入失败: <span class="subst">&#123;filepath&#125;</span>, error=<span class="subst">&#123;e&#125;</span>&quot;</span>)</span><br><span class="line">        <span class="keyword">raise</span></span><br></pre></td></tr></table></figure><hr><h2 id="10-常见面试题汇总"><a href="#10-常见面试题汇总" class="headerlink" title="10. 常见面试题汇总"></a>10. 常见面试题汇总</h2><h3 id="10-1-基础概念题"><a href="#10-1-基础概念题" class="headerlink" title="10.1 基础概念题"></a>10.1 基础概念题</h3><p><strong>Q1：<code>BaseException</code> 和 <code>Exception</code> 的区别是什么？应该捕获哪个？</strong></p><p><strong>A</strong>：<code>BaseException</code> 是所有异常的基类，包含 <code>SystemExit</code>、<code>KeyboardInterrupt</code>、<code>GeneratorExit</code> 和 <code>Exception</code>。<code>Exception</code> 是所有常规异常的基类。应捕获 <code>Exception</code>，因为 <code>SystemExit</code> 和 <code>KeyboardInterrupt</code> 是程序的合法退出机制，不应被捕获。</p><hr><p><strong>Q2：<code>try/except/else/finally</code> 各子句的执行顺序是什么？</strong></p><p><strong>A</strong>：</p><ol><li><code>try</code> 块始终执行</li><li>如果 <code>try</code> 中无异常：执行 <code>else</code> → 执行 <code>finally</code> → 继续后续代码</li><li>如果 <code>try</code> 中有异常：匹配 <code>except</code>（匹配则执行，不匹配则向上抛出）→ 执行 <code>finally</code> → 根据异常是否被处理决定后续</li></ol><hr><p><strong>Q3：<code>raise</code> 和 <code>raise e</code> 有什么区别？</strong></p><p><strong>A</strong>：<code>raise</code> 在 <code>except</code> 块中重新抛出当前异常，保留完整的原始 traceback；<code>raise e</code> 重新创建 traceback，丢失原始位置信息。推荐使用 <code>raise</code>。</p><hr><p><strong>Q4：<code>assert</code> 在生产环境中是否可靠？为什么？</strong></p><p><strong>A</strong>：不可靠。<code>assert</code> 在 <code>python -O</code>（优化模式）下会被完全跳过。因此，外部输入验证必须用 <code>raise</code>，<code>assert</code> 只用于开发期的内部逻辑检查。</p><hr><p><strong>Q5：<code>logger.exception()</code> 和 <code>logger.error()</code> 的区别？</strong></p><p><strong>A</strong>：<code>logger.exception()</code> 等价于 <code>logger.error(..., exc_info=True)</code>，会自动附加完整的堆栈跟踪信息。只应在 <code>except</code> 块中使用。</p><hr><h3 id="10-2-实战应用题"><a href="#10-2-实战应用题" class="headerlink" title="10.2 实战应用题"></a>10.2 实战应用题</h3><p><strong>Q6：如何设计一个好的异常体系？</strong></p><p><strong>A</strong>：</p><ol><li>建立异常层级：根异常 → 分类异常 → 具体异常</li><li>异常信息包含足够上下文（参数值、操作意图）</li><li>不要泄露敏感数据（密码、token）</li><li>异常携带错误码，便于上层处理</li><li>使用异常链（<code>raise ... from</code>）保留根因</li></ol><hr><p><strong>Q7：生产环境中日志应该如何配置？</strong></p><p><strong>A</strong>：</p><ol><li>多输出目标：控制台（开发）+ 文件（生产）+ 日志聚合系统（ELK）</li><li>日志级别：生产环境至少 INFO，开发环境 DEBUG</li><li>日志轮转：按大小（RotatingFileHandler）+ 按时间（TimedRotatingFileHandler）</li><li>结构化日志：JSON 格式便于解析和搜索</li><li>敏感信息脱敏：密码、手机号、银行卡号等</li><li>模块级 logger：使用 <code>logging.getLogger(__name__)</code></li></ol><hr><p><strong>Q8：如何实现重试机制？应该注意什么？</strong></p><p><strong>A</strong>：</p><ol><li>使用装饰器封装重试逻辑</li><li>设置最大重试次数和退避策略（指数退避最佳）</li><li>添加随机抖动避免请求风暴</li><li>只重试可恢复的异常（网络问题、超时），不重试业务错误（认证失败）</li><li>记录重试日志便于排查</li></ol><hr><p><strong>Q9：什么是断路器模式？适用于什么场景？</strong></p><p><strong>A</strong>：断路器模式用于防止反复重试已确认失败的服务，避免级联故障。当失败次数达到阈值时，断路器打开，直接拒绝请求；经过一段时间后进入半开状态，允许少量请求探测；如果成功则关闭，失败则重新打开。适用于微服务架构中的远程调用。</p><hr><p><strong>Q10：如何优雅地处理生产环境中的未捕获异常？</strong></p><p><strong>A</strong>：</p><ol><li>设置全局异常钩子 <code>sys.excepthook</code></li><li>在 Web 框架中注册全局异常处理器</li><li>记录完整的 traceback 和上下文信息</li><li>发送告警通知（邮件、短信、钉钉等）</li><li>返回友好的错误信息给用户，隐藏技术细节</li></ol><hr><h3 id="10-3-代码分析题"><a href="#10-3-代码分析题" class="headerlink" title="10.3 代码分析题"></a>10.3 代码分析题</h3><p><strong>Q11：分析以下代码的问题：</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">try</span>:</span><br><span class="line">    data = json.loads(user_input)</span><br><span class="line"><span class="keyword">except</span> Exception:</span><br><span class="line">    <span class="keyword">pass</span>  <span class="comment"># 静默忽略</span></span><br></pre></td></tr></table></figure><p><strong>A</strong>：</p><ol><li>静默忽略异常会导致问题被隐藏</li><li>不知道具体是什么错误</li><li>应该至少记录日志</li><li>更好的做法是区分 <code>JSONDecodeError</code> 和其他异常</li></ol><hr><p><strong>Q12：分析以下代码的问题：</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">login</span>(<span class="params">username, password</span>):</span><br><span class="line">    <span class="keyword">assert</span> username, <span class="string">&quot;用户名不能为空&quot;</span></span><br><span class="line">    <span class="keyword">assert</span> password, <span class="string">&quot;密码不能为空&quot;</span></span><br><span class="line">    <span class="comment"># ...</span></span><br></pre></td></tr></table></figure><p><strong>A</strong>：在 <code>python -O</code> 模式下运行时，断言被跳过，即使传入空字符串也会继续执行，可能导致更严重的错误。外部输入验证必须用 <code>raise</code>。</p><hr><p><strong>Q13：以下代码会打印什么？</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">try</span>:</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;try&quot;</span>)</span><br><span class="line">    <span class="keyword">raise</span> ValueError(<span class="string">&quot;test&quot;</span>)</span><br><span class="line"><span class="keyword">except</span>:</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;except&quot;</span>)</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;else&quot;</span>)</span><br><span class="line"><span class="keyword">finally</span>:</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;finally&quot;</span>)</span><br></pre></td></tr></table></figure><p><strong>A</strong>：<code>try</code> → <code>except</code> → <code>finally</code>。<code>else</code> 不会执行（因为有异常）。</p><hr><h2 id="11-实战项目：异常处理与日志系统搭建"><a href="#11-实战项目：异常处理与日志系统搭建" class="headerlink" title="11. 实战项目：异常处理与日志系统搭建"></a>11. 实战项目：异常处理与日志系统搭建</h2><h3 id="11-1-项目概述"><a href="#11-1-项目概述" class="headerlink" title="11.1 项目概述"></a>11.1 项目概述</h3><p><strong>项目目标</strong>：搭建一个生产级的异常处理和日志系统，包含：</p><ul><li>统一的异常层级设计</li><li>多环境日志配置</li><li>API 异常处理中间件</li><li>结构化日志输出</li><li>日志轮转和归档</li><li>全局异常捕获</li></ul><h3 id="11-2-项目结构"><a href="#11-2-项目结构" class="headerlink" title="11.2 项目结构"></a>11.2 项目结构</h3><figure class="highlight plaintext"><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><span class="line">exception_project/</span><br><span class="line">├── app/</span><br><span class="line">│   ├── __init__.py</span><br><span class="line">│   ├── exceptions.py      # 自定义异常定义</span><br><span class="line">│   ├── logging_config.py  # 日志配置</span><br><span class="line">│   ├── middleware.py      # 异常处理中间件</span><br><span class="line">│   ├── services.py        # 业务服务</span><br><span class="line">│   └── api.py             # API 路由</span><br><span class="line">├── config/</span><br><span class="line">│   ├── dev.py             # 开发环境配置</span><br><span class="line">│   └── prod.py            # 生产环境配置</span><br><span class="line">├── requirements.txt</span><br><span class="line">└── main.py</span><br></pre></td></tr></table></figure><h3 id="11-3-核心代码"><a href="#11-3-核心代码" class="headerlink" title="11.3 核心代码"></a>11.3 核心代码</h3><p><strong>exceptions.py</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><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><span class="line"><span class="keyword">class</span> <span class="title class_">AppError</span>(<span class="title class_ inherited__">Exception</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, message, code=<span class="literal">None</span>, details=<span class="literal">None</span></span>):</span><br><span class="line">        <span class="built_in">super</span>().__init__(message)</span><br><span class="line">        <span class="variable language_">self</span>.message = message</span><br><span class="line">        <span class="variable language_">self</span>.code = code</span><br><span class="line">        <span class="variable language_">self</span>.details = details <span class="keyword">or</span> &#123;&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">ValidationError</span>(<span class="title class_ inherited__">AppError</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, message, field=<span class="literal">None</span>, **kwargs</span>):</span><br><span class="line">        <span class="built_in">super</span>().__init__(message, code=<span class="string">&quot;VALIDATION_ERROR&quot;</span>, **kwargs)</span><br><span class="line">        <span class="variable language_">self</span>.field = field</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">AuthenticationError</span>(<span class="title class_ inherited__">AppError</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, message=<span class="string">&quot;认证失败&quot;</span>, **kwargs</span>):</span><br><span class="line">        <span class="built_in">super</span>().__init__(message, code=<span class="string">&quot;AUTH_ERROR&quot;</span>, **kwargs)</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">NotFoundError</span>(<span class="title class_ inherited__">AppError</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, resource, identifier, **kwargs</span>):</span><br><span class="line">        message = <span class="string">f&quot;<span class="subst">&#123;resource&#125;</span> 未找到: <span class="subst">&#123;identifier&#125;</span>&quot;</span></span><br><span class="line">        <span class="built_in">super</span>().__init__(message, code=<span class="string">&quot;NOT_FOUND&quot;</span>, **kwargs)</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">ExternalServiceError</span>(<span class="title class_ inherited__">AppError</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, service, message, **kwargs</span>):</span><br><span class="line">        <span class="built_in">super</span>().__init__(message, code=<span class="string">f&quot;<span class="subst">&#123;service.upper()&#125;</span>_ERROR&quot;</span>, **kwargs)</span><br><span class="line">        <span class="variable language_">self</span>.service = service</span><br></pre></td></tr></table></figure><p><strong>logging_config.py</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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> logging</span><br><span class="line"><span class="keyword">import</span> logging.config</span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"><span class="keyword">from</span> datetime <span class="keyword">import</span> datetime</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">configure_logging</span>(<span class="params">env=<span class="string">&quot;dev&quot;</span></span>):</span><br><span class="line">    log_dir = os.path.join(os.path.dirname(__file__), <span class="string">&quot;..&quot;</span>, <span class="string">&quot;logs&quot;</span>)</span><br><span class="line">    os.makedirs(log_dir, exist_ok=<span class="literal">True</span>)</span><br><span class="line">    </span><br><span class="line">    today = datetime.now().strftime(<span class="string">&quot;%Y%m%d&quot;</span>)</span><br><span class="line">    </span><br><span class="line">    config = &#123;</span><br><span class="line">        <span class="string">&quot;version&quot;</span>: <span class="number">1</span>,</span><br><span class="line">        <span class="string">&quot;disable_existing_loggers&quot;</span>: <span class="literal">False</span>,</span><br><span class="line">        <span class="string">&quot;formatters&quot;</span>: &#123;</span><br><span class="line">            <span class="string">&quot;standard&quot;</span>: &#123;</span><br><span class="line">                <span class="string">&quot;format&quot;</span>: <span class="string">&quot;%(asctime)s [%(levelname)s] %(name)s:%(lineno)d: %(message)s&quot;</span>,</span><br><span class="line">            &#125;,</span><br><span class="line">            <span class="string">&quot;json&quot;</span>: &#123;</span><br><span class="line">                <span class="string">&quot;format&quot;</span>: (</span><br><span class="line">                    <span class="string">&#x27;&#123;&quot;time&quot;: &quot;%(asctime)s&quot;, &quot;level&quot;: &quot;%(levelname)s&quot;, &#x27;</span></span><br><span class="line">                    <span class="string">&#x27;&quot;logger&quot;: &quot;%(name)s&quot;, &quot;line&quot;: %(lineno)d, &#x27;</span></span><br><span class="line">                    <span class="string">&#x27;&quot;message&quot;: &quot;%(message)s&quot;&#125;&#x27;</span></span><br><span class="line">                ),</span><br><span class="line">            &#125;,</span><br><span class="line">        &#125;,</span><br><span class="line">        <span class="string">&quot;handlers&quot;</span>: &#123;</span><br><span class="line">            <span class="string">&quot;console&quot;</span>: &#123;</span><br><span class="line">                <span class="string">&quot;class&quot;</span>: <span class="string">&quot;logging.StreamHandler&quot;</span>,</span><br><span class="line">                <span class="string">&quot;level&quot;</span>: <span class="string">&quot;DEBUG&quot;</span> <span class="keyword">if</span> env == <span class="string">&quot;dev&quot;</span> <span class="keyword">else</span> <span class="string">&quot;INFO&quot;</span>,</span><br><span class="line">                <span class="string">&quot;formatter&quot;</span>: <span class="string">&quot;standard&quot;</span>,</span><br><span class="line">            &#125;,</span><br><span class="line">            <span class="string">&quot;file&quot;</span>: &#123;</span><br><span class="line">                <span class="string">&quot;class&quot;</span>: <span class="string">&quot;logging.handlers.RotatingFileHandler&quot;</span>,</span><br><span class="line">                <span class="string">&quot;level&quot;</span>: <span class="string">&quot;INFO&quot;</span>,</span><br><span class="line">                <span class="string">&quot;formatter&quot;</span>: <span class="string">&quot;json&quot;</span>,</span><br><span class="line">                <span class="string">&quot;filename&quot;</span>: os.path.join(log_dir, <span class="string">f&quot;app_<span class="subst">&#123;today&#125;</span>.log&quot;</span>),</span><br><span class="line">                <span class="string">&quot;maxBytes&quot;</span>: <span class="number">10</span> * <span class="number">1024</span> * <span class="number">1024</span>,</span><br><span class="line">                <span class="string">&quot;backupCount&quot;</span>: <span class="number">30</span>,</span><br><span class="line">                <span class="string">&quot;encoding&quot;</span>: <span class="string">&quot;utf-8&quot;</span>,</span><br><span class="line">            &#125;,</span><br><span class="line">            <span class="string">&quot;error_file&quot;</span>: &#123;</span><br><span class="line">                <span class="string">&quot;class&quot;</span>: <span class="string">&quot;logging.handlers.RotatingFileHandler&quot;</span>,</span><br><span class="line">                <span class="string">&quot;level&quot;</span>: <span class="string">&quot;ERROR&quot;</span>,</span><br><span class="line">                <span class="string">&quot;formatter&quot;</span>: <span class="string">&quot;json&quot;</span>,</span><br><span class="line">                <span class="string">&quot;filename&quot;</span>: os.path.join(log_dir, <span class="string">f&quot;error_<span class="subst">&#123;today&#125;</span>.log&quot;</span>),</span><br><span class="line">                <span class="string">&quot;maxBytes&quot;</span>: <span class="number">5</span> * <span class="number">1024</span> * <span class="number">1024</span>,</span><br><span class="line">                <span class="string">&quot;backupCount&quot;</span>: <span class="number">30</span>,</span><br><span class="line">                <span class="string">&quot;encoding&quot;</span>: <span class="string">&quot;utf-8&quot;</span>,</span><br><span class="line">            &#125;,</span><br><span class="line">        &#125;,</span><br><span class="line">        <span class="string">&quot;loggers&quot;</span>: &#123;</span><br><span class="line">            <span class="string">&quot;&quot;</span>: &#123;</span><br><span class="line">                <span class="string">&quot;handlers&quot;</span>: [<span class="string">&quot;console&quot;</span>, <span class="string">&quot;file&quot;</span>, <span class="string">&quot;error_file&quot;</span>],</span><br><span class="line">                <span class="string">&quot;level&quot;</span>: <span class="string">&quot;DEBUG&quot;</span> <span class="keyword">if</span> env == <span class="string">&quot;dev&quot;</span> <span class="keyword">else</span> <span class="string">&quot;INFO&quot;</span>,</span><br><span class="line">                <span class="string">&quot;propagate&quot;</span>: <span class="literal">False</span>,</span><br><span class="line">            &#125;,</span><br><span class="line">            <span class="string">&quot;requests&quot;</span>: &#123;</span><br><span class="line">                <span class="string">&quot;level&quot;</span>: <span class="string">&quot;WARNING&quot;</span>,</span><br><span class="line">            &#125;,</span><br><span class="line">            <span class="string">&quot;urllib3&quot;</span>: &#123;</span><br><span class="line">                <span class="string">&quot;level&quot;</span>: <span class="string">&quot;WARNING&quot;</span>,</span><br><span class="line">            &#125;,</span><br><span class="line">        &#125;,</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    logging.config.dictConfig(config)</span><br></pre></td></tr></table></figure><p><strong>middleware.py</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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> functools <span class="keyword">import</span> wraps</span><br><span class="line"><span class="keyword">from</span> flask <span class="keyword">import</span> jsonify, request</span><br><span class="line"><span class="keyword">import</span> logging</span><br><span class="line"><span class="keyword">import</span> traceback</span><br><span class="line"><span class="keyword">import</span> uuid</span><br><span class="line"></span><br><span class="line">logger = logging.getLogger(__name__)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">error_handler</span>(<span class="params">f</span>):</span><br><span class="line"><span class="meta">    @wraps(<span class="params">f</span>)</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">decorated_function</span>(<span class="params">*args, **kwargs</span>):</span><br><span class="line">        request_id = <span class="built_in">str</span>(uuid.uuid4())[:<span class="number">8</span>]</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">try</span>:</span><br><span class="line">            logger.info(</span><br><span class="line">                <span class="string">f&quot;[REQ:<span class="subst">&#123;request_id&#125;</span>] <span class="subst">&#123;request.method&#125;</span> <span class="subst">&#123;request.path&#125;</span> | &quot;</span></span><br><span class="line">                <span class="string">f&quot;params=<span class="subst">&#123;request.args&#125;</span> | body=<span class="subst">&#123;request.get_json(silent=<span class="literal">True</span>)&#125;</span>&quot;</span></span><br><span class="line">            )</span><br><span class="line">            </span><br><span class="line">            response = f(*args, **kwargs)</span><br><span class="line">            </span><br><span class="line">            logger.info(<span class="string">f&quot;[RES:<span class="subst">&#123;request_id&#125;</span>] <span class="subst">&#123;response.status_code&#125;</span>&quot;</span>)</span><br><span class="line">            <span class="keyword">return</span> response</span><br><span class="line">            </span><br><span class="line">        <span class="keyword">except</span> ValidationError <span class="keyword">as</span> e:</span><br><span class="line">            logger.warning(<span class="string">f&quot;[ERR:<span class="subst">&#123;request_id&#125;</span>] 参数校验失败: <span class="subst">&#123;e.message&#125;</span>&quot;</span>)</span><br><span class="line">            <span class="keyword">return</span> jsonify(&#123;</span><br><span class="line">                <span class="string">&quot;code&quot;</span>: <span class="number">400</span>,</span><br><span class="line">                <span class="string">&quot;message&quot;</span>: e.message,</span><br><span class="line">                <span class="string">&quot;field&quot;</span>: e.field,</span><br><span class="line">                <span class="string">&quot;request_id&quot;</span>: request_id</span><br><span class="line">            &#125;), <span class="number">400</span></span><br><span class="line">            </span><br><span class="line">        <span class="keyword">except</span> AuthenticationError <span class="keyword">as</span> e:</span><br><span class="line">            logger.warning(<span class="string">f&quot;[ERR:<span class="subst">&#123;request_id&#125;</span>] 认证失败: <span class="subst">&#123;e.message&#125;</span>&quot;</span>)</span><br><span class="line">            <span class="keyword">return</span> jsonify(&#123;</span><br><span class="line">                <span class="string">&quot;code&quot;</span>: <span class="number">401</span>,</span><br><span class="line">                <span class="string">&quot;message&quot;</span>: <span class="string">&quot;未授权，请重新登录&quot;</span>,</span><br><span class="line">                <span class="string">&quot;request_id&quot;</span>: request_id</span><br><span class="line">            &#125;), <span class="number">401</span></span><br><span class="line">            </span><br><span class="line">        <span class="keyword">except</span> NotFoundError <span class="keyword">as</span> e:</span><br><span class="line">            logger.warning(<span class="string">f&quot;[ERR:<span class="subst">&#123;request_id&#125;</span>] 资源未找到: <span class="subst">&#123;e.message&#125;</span>&quot;</span>)</span><br><span class="line">            <span class="keyword">return</span> jsonify(&#123;</span><br><span class="line">                <span class="string">&quot;code&quot;</span>: <span class="number">404</span>,</span><br><span class="line">                <span class="string">&quot;message&quot;</span>: e.message,</span><br><span class="line">                <span class="string">&quot;request_id&quot;</span>: request_id</span><br><span class="line">            &#125;), <span class="number">404</span></span><br><span class="line">            </span><br><span class="line">        <span class="keyword">except</span> ExternalServiceError <span class="keyword">as</span> e:</span><br><span class="line">            logger.error(<span class="string">f&quot;[ERR:<span class="subst">&#123;request_id&#125;</span>] 外部服务错误: <span class="subst">&#123;e.service&#125;</span> - <span class="subst">&#123;e.message&#125;</span>&quot;</span>)</span><br><span class="line">            <span class="keyword">return</span> jsonify(&#123;</span><br><span class="line">                <span class="string">&quot;code&quot;</span>: <span class="number">503</span>,</span><br><span class="line">                <span class="string">&quot;message&quot;</span>: <span class="string">&quot;服务暂时不可用&quot;</span>,</span><br><span class="line">                <span class="string">&quot;request_id&quot;</span>: request_id</span><br><span class="line">            &#125;), <span class="number">503</span></span><br><span class="line">            </span><br><span class="line">        <span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">            logger.exception(<span class="string">f&quot;[ERR:<span class="subst">&#123;request_id&#125;</span>] 服务器内部错误&quot;</span>)</span><br><span class="line">            <span class="keyword">return</span> jsonify(&#123;</span><br><span class="line">                <span class="string">&quot;code&quot;</span>: <span class="number">500</span>,</span><br><span class="line">                <span class="string">&quot;message&quot;</span>: <span class="string">&quot;服务器内部错误，请稍后重试&quot;</span>,</span><br><span class="line">                <span class="string">&quot;request_id&quot;</span>: request_id</span><br><span class="line">            &#125;), <span class="number">500</span></span><br><span class="line">    <span class="keyword">return</span> decorated_function</span><br></pre></td></tr></table></figure><p><strong>services.py</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><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><span class="line"><span class="keyword">import</span> logging</span><br><span class="line"><span class="keyword">from</span> .exceptions <span class="keyword">import</span> ValidationError, NotFoundError, ExternalServiceError</span><br><span class="line"></span><br><span class="line">logger = logging.getLogger(__name__)</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">UserService</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, db_client, redis_client</span>):</span><br><span class="line">        <span class="variable language_">self</span>.db = db_client</span><br><span class="line">        <span class="variable language_">self</span>.cache = redis_client</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_user</span>(<span class="params">self, user_id</span>):</span><br><span class="line">        <span class="keyword">if</span> user_id &lt;= <span class="number">0</span>:</span><br><span class="line">            <span class="keyword">raise</span> ValidationError(<span class="string">&quot;用户 ID 必须大于 0&quot;</span>, field=<span class="string">&quot;user_id&quot;</span>)</span><br><span class="line">        </span><br><span class="line">        cache_key = <span class="string">f&quot;user:<span class="subst">&#123;user_id&#125;</span>&quot;</span></span><br><span class="line">        <span class="keyword">try</span>:</span><br><span class="line">            user = <span class="variable language_">self</span>.cache.get(cache_key)</span><br><span class="line">            <span class="keyword">if</span> user:</span><br><span class="line">                logger.debug(<span class="string">f&quot;从缓存获取用户: <span class="subst">&#123;user_id&#125;</span>&quot;</span>)</span><br><span class="line">                <span class="keyword">return</span> user</span><br><span class="line">        <span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">            logger.warning(<span class="string">f&quot;缓存获取失败: <span class="subst">&#123;e&#125;</span>&quot;</span>)</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">try</span>:</span><br><span class="line">            user = <span class="variable language_">self</span>.db.query(<span class="string">&quot;SELECT * FROM users WHERE id = %s&quot;</span>, (user_id,))</span><br><span class="line">            <span class="keyword">if</span> <span class="keyword">not</span> user:</span><br><span class="line">                <span class="keyword">raise</span> NotFoundError(<span class="string">&quot;用户&quot;</span>, user_id)</span><br><span class="line">            </span><br><span class="line">            <span class="keyword">try</span>:</span><br><span class="line">                <span class="variable language_">self</span>.cache.<span class="built_in">set</span>(cache_key, user, ex=<span class="number">300</span>)</span><br><span class="line">            <span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">                logger.warning(<span class="string">f&quot;缓存设置失败: <span class="subst">&#123;e&#125;</span>&quot;</span>)</span><br><span class="line">            </span><br><span class="line">            <span class="keyword">return</span> user</span><br><span class="line">        <span class="keyword">except</span> ExternalServiceError:</span><br><span class="line">            <span class="keyword">raise</span></span><br><span class="line">        <span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">            logger.error(<span class="string">f&quot;数据库查询失败: <span class="subst">&#123;e&#125;</span>&quot;</span>)</span><br><span class="line">            <span class="keyword">raise</span> ExternalServiceError(<span class="string">&quot;database&quot;</span>, <span class="string">f&quot;查询用户失败: <span class="subst">&#123;e&#125;</span>&quot;</span>)</span><br></pre></td></tr></table></figure><p><strong>main.py</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><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><span class="line"><span class="keyword">import</span> sys</span><br><span class="line"><span class="keyword">import</span> logging</span><br><span class="line"><span class="keyword">from</span> flask <span class="keyword">import</span> Flask</span><br><span class="line"><span class="keyword">from</span> app.logging_config <span class="keyword">import</span> configure_logging</span><br><span class="line"><span class="keyword">from</span> app.api <span class="keyword">import</span> register_routes</span><br><span class="line"><span class="keyword">from</span> app.exceptions <span class="keyword">import</span> AppError</span><br><span class="line"></span><br><span class="line">logger = logging.getLogger(__name__)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">create_app</span>(<span class="params">env=<span class="string">&quot;dev&quot;</span></span>):</span><br><span class="line">    configure_logging(env)</span><br><span class="line">    </span><br><span class="line">    app = Flask(__name__)</span><br><span class="line">    register_routes(app)</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 全局未捕获异常处理</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">global_exception_handler</span>(<span class="params">exc_type, exc_value, exc_tb</span>):</span><br><span class="line">        <span class="keyword">if</span> <span class="built_in">issubclass</span>(exc_type, KeyboardInterrupt):</span><br><span class="line">            sys.__excepthook__(exc_type, exc_value, exc_tb)</span><br><span class="line">            <span class="keyword">return</span></span><br><span class="line">        </span><br><span class="line">        logger.critical(<span class="string">&quot;未捕获的异常&quot;</span>, exc_info=(exc_type, exc_value, exc_tb))</span><br><span class="line">    </span><br><span class="line">    sys.excepthook = global_exception_handler</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> app</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    app = create_app(env=<span class="string">&quot;dev&quot;</span>)</span><br><span class="line">    app.run(host=<span class="string">&quot;0.0.0.0&quot;</span>, port=<span class="number">5000</span>, debug=<span class="literal">True</span>)</span><br></pre></td></tr></table></figure><h3 id="11-4-项目运行"><a href="#11-4-项目运行" class="headerlink" title="11.4 项目运行"></a>11.4 项目运行</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><span class="line"><span class="comment"># 安装依赖</span></span><br><span class="line">pip install flask redis</span><br><span class="line"></span><br><span class="line"><span class="comment"># 开发环境运行</span></span><br><span class="line">python main.py</span><br><span class="line"></span><br><span class="line"><span class="comment"># 生产环境运行</span></span><br><span class="line">gunicorn -w 4 main:create_app(<span class="string">&#x27;prod&#x27;</span>)</span><br></pre></td></tr></table></figure><h3 id="11-5-项目亮点"><a href="#11-5-项目亮点" class="headerlink" title="11.5 项目亮点"></a>11.5 项目亮点</h3><ol><li><strong>统一异常层级</strong>：所有自定义异常继承自 <code>AppError</code>，便于统一处理</li><li><strong>结构化日志</strong>：JSON 格式输出，便于日志系统解析和搜索</li><li><strong>请求追踪</strong>：每个请求分配唯一 ID，便于关联日志</li><li><strong>多级缓存</strong>：缓存失败自动降级到数据库</li><li><strong>优雅降级</strong>：外部服务失败返回友好错误，不暴露技术细节</li><li><strong>配置分离</strong>：开发和生产环境使用不同的日志级别和配置</li></ol><hr><h2 id="附录：第四阶段自检清单"><a href="#附录：第四阶段自检清单" class="headerlink" title="附录：第四阶段自检清单"></a>附录：第四阶段自检清单</h2><p>在继续学习第五阶段之前，请确保你能：</p><ul><li><input disabled="" type="checkbox"> 画出 Python 异常层级结构，并说明 <code>BaseException</code> 和 <code>Exception</code> 的区别</li><li><input disabled="" type="checkbox"> 正确使用 <code>try / except / else / finally</code> 四个子句</li><li><input disabled="" type="checkbox"> 解释 <code>raise ... from</code> 的三种用法</li><li><input disabled="" type="checkbox"> 设计合理的自定义异常层级</li><li><input disabled="" type="checkbox"> 区分 <code>assert</code> 和 <code>raise</code> 的使用场景</li><li><input disabled="" type="checkbox"> 配置生产级的 <code>logging</code> 系统（控制台+文件+轮转+JSON 格式）</li><li><input disabled="" type="checkbox"> 使用 <code>pdb</code> &#x2F; <code>breakpoint()</code> 进行交互式调试</li><li><input disabled="" type="checkbox"> 解释 <code>logger.exception()</code> 和 <code>logger.error(exc_info=True)</code> 的等价性</li><li><input disabled="" type="checkbox"> 使用 <code>warnings</code> 模块标记废弃 API</li><li><input disabled="" type="checkbox"> 实现重试装饰器和断路器模式</li><li><input disabled="" type="checkbox"> 编写全局异常处理器</li><li><input disabled="" type="checkbox"> 实现优雅降级策略</li><li><input disabled="" type="checkbox"> 使用结构化日志（JSON 格式）</li><li><input disabled="" type="checkbox"> 使用 <code>icecream</code> &#x2F; <code>rich</code> 增强调试体验</li><li><input disabled="" type="checkbox"> 设置日志轮转策略</li></ul><hr><blockquote><p><strong>工程师寄语</strong>：优秀的异常处理不是”捕获所有异常然后忽略”，而是”知道什么可能出错，并优雅地处理每种情况”。日志是你的第二双眼睛——在生产环境中，好的日志比好的调试器更重要。记住：<strong>永远不要让用户看到 traceback</strong>。</p></blockquote>]]>
    </content>
    <id>https://itdxb.cn/2026/07/22/python/document/%E3%80%90Python%E3%80%91%E5%BC%82%E5%B8%B8%E5%A4%84%E7%90%86%E4%B8%8E%E8%B0%83%E8%AF%95%EF%BC%88%E7%AC%AC%E5%9B%9B%E9%98%B6%E6%AE%B5%EF%BC%89/</id>
    <link href="https://itdxb.cn/2026/07/22/python/document/%E3%80%90Python%E3%80%91%E5%BC%82%E5%B8%B8%E5%A4%84%E7%90%86%E4%B8%8E%E8%B0%83%E8%AF%95%EF%BC%88%E7%AC%AC%E5%9B%9B%E9%98%B6%E6%AE%B5%EF%BC%89/"/>
    <published>2026-07-22T06:00:00.000Z</published>
    <summary>系统掌握 Python 异常处理机制、日志系统、调试技巧和生产环境最佳实践，建立防御性编程思维。</summary>
    <title>【Python】异常处理与调试（第四阶段）</title>
    <updated>2026-07-24T05:37:43.720Z</updated>
  </entry>
  <entry>
    <author>
      <name>Super Bing</name>
    </author>
    <category term="Python" scheme="https://itdxb.cn/categories/Python/"/>
    <category term="教程" scheme="https://itdxb.cn/tags/%E6%95%99%E7%A8%8B/"/>
    <category term="Python" scheme="https://itdxb.cn/tags/Python/"/>
    <category term="面向对象" scheme="https://itdxb.cn/tags/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1/"/>
    <content>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><h1 id="三、面向对象编程"><a href="#三、面向对象编程" class="headerlink" title="三、面向对象编程"></a>三、面向对象编程</h1><blockquote><p><strong>阶段定位</strong>：面向对象编程（OOP）是 Python 组织大型项目的核心范式。Python 的 OOP 灵活而不教条——它支持封装、继承、多态等经典特性，同时通过鸭子类型和魔术方法提供了独特的表达能力。本阶段不仅要学会”怎么写类”，更要理解”为什么这样设计”。</p></blockquote><figure class="highlight plaintext"><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><span class="line">┌─────────────────────────────────────────────────────────────┐</span><br><span class="line">│                   Python OOP 知识图谱                        │</span><br><span class="line">├─────────────────────────────────────────────────────────────┤</span><br><span class="line">│                                                             │</span><br><span class="line">│  1. 类与对象 ──→ __init__ / __new__ / self / 类属性 vs 实例属性 │</span><br><span class="line">│       │                                                     │</span><br><span class="line">│  2. 三种方法 ──→ 实例方法 / @classmethod / @staticmethod      │</span><br><span class="line">│       │                                                     │</span><br><span class="line">│  3. 封装 ─────→ _受保护 / __私有 / @property / __slots__      │</span><br><span class="line">│       │                                                     │</span><br><span class="line">│  4. 继承多态 ──→ 单继承 / 多继承 / MRO / super() / Mixin / ABC │</span><br><span class="line">│       │                                                     │</span><br><span class="line">│  5. 魔术方法 ──→ __str__ / __repr__ / __len__ / __getitem__   │</span><br><span class="line">│       │           __enter__ / __call__ / 运算符重载 / __hash__ │</span><br><span class="line">│       │                                                     │</span><br><span class="line">│  6. 元编程 ───→ type / 元类 / __init_subclass__ / 描述符       │</span><br><span class="line">│       │                                                     │</span><br><span class="line">│  7. 设计模式 ─→ 单例 / 工厂 / 策略 / 观察者 / 装饰器           │</span><br><span class="line">│       │                                                     │</span><br><span class="line">│  8. 现代特性 ─→ dataclass / Enum / typing.Protocol / slots    │</span><br><span class="line">│                                                             │</span><br><span class="line">└─────────────────────────────────────────────────────────────┘</span><br></pre></td></tr></table></figure><hr><h2 id="1-类与对象"><a href="#1-类与对象" class="headerlink" title="1. 类与对象"></a>1. 类与对象</h2><h3 id="1-1-类的定义与实例化"><a href="#1-1-类的定义与实例化" class="headerlink" title="1.1 类的定义与实例化"></a>1.1 类的定义与实例化</h3><p><strong>面向对象的三大支柱</strong>：</p><figure class="highlight plaintext"><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><span class="line">┌──────────┐    ┌──────────┐    ┌──────────┐</span><br><span class="line">│  封装     │    │  继承     │    │  多态     │</span><br><span class="line">│ Encaps.  │    │ Inherit. │    │ Polymor.│</span><br><span class="line">├──────────┤    ├──────────┤    ├──────────┤</span><br><span class="line">│ 隐藏内部  │    │ 复用父类  │    │ 同一接口  │</span><br><span class="line">│ 实现细节  │    │ 代码和结构│    │ 不同实现  │</span><br><span class="line">└──────────┘    └──────────┘    └──────────┘</span><br></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></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Person</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;人类：最简单的类定义。&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 类属性（所有实例共享）</span></span><br><span class="line">    species = <span class="string">&quot;Homo sapiens&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, name, age</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;构造方法：创建实例时自动调用。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="variable language_">self</span>.name = name      <span class="comment"># 实例属性</span></span><br><span class="line">        <span class="variable language_">self</span>.age = age</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">introduce</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;实例方法：第一个参数 self 指向当前实例。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">return</span> <span class="string">f&quot;我叫 <span class="subst">&#123;self.name&#125;</span>，今年 <span class="subst">&#123;self.age&#125;</span> 岁&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 实例化</span></span><br><span class="line">alice = Person(<span class="string">&quot;Alice&quot;</span>, <span class="number">30</span>)</span><br><span class="line">bob = Person(<span class="string">&quot;Bob&quot;</span>, <span class="number">25</span>)</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(alice.introduce())    <span class="comment"># 我叫 Alice，今年 30 岁</span></span><br><span class="line"><span class="built_in">print</span>(alice.species)        <span class="comment"># Homo sapiens</span></span><br><span class="line"><span class="built_in">print</span>(bob.species)          <span class="comment"># Homo sapiens</span></span><br></pre></td></tr></table></figure><p><strong>类属性 vs 实例属性的内存模型</strong>：</p><figure class="highlight plaintext"><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><span class="line">      Person 类对象</span><br><span class="line">    ┌──────────────────┐</span><br><span class="line">    │ species = &quot;Human&quot; │  ← 类属性（存储在类的 __dict__ 中）</span><br><span class="line">    │ __init__ 方法     │</span><br><span class="line">    └────────┬─────────┘</span><br><span class="line">             │ 实例化</span><br><span class="line">     ┌───────┴───────┐</span><br><span class="line">     ▼               ▼</span><br><span class="line">┌─────────┐    ┌─────────┐</span><br><span class="line">│ alice   │    │ bob     │</span><br><span class="line">├─────────┤    ├─────────┤</span><br><span class="line">│name=Alice│   │name=Bob │  ← 实例属性（各自独立）</span><br><span class="line">│age=30   │    │age=25   │</span><br><span class="line">└─────────┘    └─────────┘</span><br><span class="line">  alice.__dict__  bob.__dict__</span><br><span class="line">  没有 species     没有 species</span><br><span class="line">  → 去 Person 找   → 去 Person 找</span><br></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><span class="line"><span class="comment"># 类属性与实例属性的区别</span></span><br><span class="line">Person.species = <span class="string">&quot;Human&quot;</span>    <span class="comment"># 修改类属性，所有实例都受影响</span></span><br><span class="line"><span class="built_in">print</span>(alice.species)        <span class="comment"># Human</span></span><br><span class="line"><span class="built_in">print</span>(bob.species)          <span class="comment"># Human</span></span><br><span class="line"></span><br><span class="line">alice.species = <span class="string">&quot;Alien&quot;</span>     <span class="comment"># 这其实是给实例创建了一个同名实例属性，不影响类属性</span></span><br><span class="line"><span class="built_in">print</span>(alice.species)        <span class="comment"># Alien（实例属性遮盖了类属性）</span></span><br><span class="line"><span class="built_in">print</span>(Person.species)       <span class="comment"># Human（类属性未变）</span></span><br><span class="line"><span class="built_in">print</span>(bob.species)          <span class="comment"># Human（bob 没有实例属性，仍读取类属性）</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 验证：查看各自的 __dict__</span></span><br><span class="line"><span class="built_in">print</span>(alice.__dict__)       <span class="comment"># &#123;&#x27;name&#x27;: &#x27;Alice&#x27;, &#x27;age&#x27;: 30, &#x27;species&#x27;: &#x27;Alien&#x27;&#125;</span></span><br><span class="line"><span class="built_in">print</span>(bob.__dict__)        <span class="comment"># &#123;&#x27;name&#x27;: &#x27;Bob&#x27;, &#x27;age&#x27;: 25&#125;  —— 没有 species</span></span><br><span class="line"><span class="built_in">print</span>(Person.__dict__[<span class="string">&#x27;species&#x27;</span>])  <span class="comment"># &#x27;Human&#x27;</span></span><br></pre></td></tr></table></figure><p><strong>属性查找顺序</strong>：实例属性 → 类属性 → 父类属性（MRO 顺序）</p><figure class="highlight plaintext"><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><span class="line">查找 alice.species 的过程：</span><br><span class="line"></span><br><span class="line">  1. alice.__dict__[&#x27;species&#x27;]  → 找到了！返回 &quot;Alien&quot;</span><br><span class="line">     （如果没有，继续往下找）</span><br><span class="line"></span><br><span class="line">  2. Person.__dict__[&#x27;species&#x27;] → 找到了！返回 &quot;Human&quot;</span><br><span class="line">     （如果没有，继续往下找）</span><br><span class="line"></span><br><span class="line">  3. object.__dict__ → 没有 species → AttributeError</span><br></pre></td></tr></table></figure><blockquote><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Test</span>:</span><br><span class="line">    items = []</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">add</span>(<span class="params">self, x</span>):</span><br><span class="line">        <span class="variable language_">self</span>.items.append(x)</span><br><span class="line"></span><br><span class="line">t1 = Test()</span><br><span class="line">t2 = Test()</span><br><span class="line">t1.add(<span class="number">1</span>)</span><br><span class="line">t2.add(<span class="number">2</span>)</span><br><span class="line"><span class="built_in">print</span>(t1.items)  <span class="comment"># [1, 2]  ← 因为 items 是类属性，所有实例共享同一个列表！</span></span><br><span class="line"><span class="built_in">print</span>(t2.items)  <span class="comment"># [1, 2]</span></span><br></pre></td></tr></table></figure><p><strong>解析</strong>：<code>self.items.append(x)</code> 查找 <code>items</code> 时，实例 <code>__dict__</code> 没有，找到类属性。所有实例操作的是同一个列表。这与可变默认参数陷阱是同一类问题。</p></blockquote><blockquote><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></pre></td><td class="code"><pre><span class="line">alice.species = <span class="string">&quot;Alien&quot;</span>   <span class="comment"># 创建实例属性，不影响 Person.species</span></span><br><span class="line">Person.species = <span class="string">&quot;Robot&quot;</span>  <span class="comment"># 修改类属性，影响所有没有实例属性的实例</span></span><br><span class="line"><span class="built_in">print</span>(alice.species)       <span class="comment"># 仍然是 &quot;Alien&quot;（实例属性优先）</span></span><br><span class="line"><span class="built_in">print</span>(bob.species)        <span class="comment"># &quot;Robot&quot;（读取新的类属性）</span></span><br></pre></td></tr></table></figure></blockquote><h3 id="1-2-self-的本质"><a href="#1-2-self-的本质" class="headerlink" title="1.2 self 的本质"></a>1.2 <code>self</code> 的本质</h3><p><code>self</code> 不是关键字，而是一个约定俗成的参数名。它代表<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></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Demo</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, value</span>):</span><br><span class="line">        <span class="variable language_">self</span>.value = value</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">show</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="variable language_">self</span>.value)</span><br><span class="line"></span><br><span class="line">d = Demo(<span class="number">42</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 两种调用方式等价</span></span><br><span class="line">d.show()            <span class="comment"># 方式一：实例调用，self 自动传入</span></span><br><span class="line">Demo.show(d)        <span class="comment"># 方式二：类调用，需手动传入实例</span></span><br></pre></td></tr></table></figure><p><strong>Python 方法绑定的底层原理</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">MyClass</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">greet</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="string">f&quot;Hello from <span class="subst">&#123;self&#125;</span>&quot;</span></span><br><span class="line"></span><br><span class="line">obj = MyClass()</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(MyClass.greet)    <span class="comment"># &lt;function MyClass.greet&gt;  —— 这是普通函数</span></span><br><span class="line"><span class="built_in">print</span>(obj.greet)        <span class="comment"># &lt;bound method MyClass.greet of &lt;...&gt;&gt;  —— 这是绑定方法</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># obj.greet 等价于：</span></span><br><span class="line"><span class="built_in">print</span>(obj.greet() == MyClass.greet(obj))  <span class="comment"># True</span></span><br></pre></td></tr></table></figure><blockquote><p><strong>面试真题</strong>：<code>self</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Test</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">this, x</span>):</span><br><span class="line">        this.x = x</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">show</span>(<span class="params">me</span>):</span><br><span class="line">        <span class="built_in">print</span>(me.x)</span><br></pre></td></tr></table></figure><p><strong>答案</strong>：可以运行，但<strong>强烈不推荐</strong>。PEP 8 规范要求使用 <code>self</code> 作为实例方法第一个参数名，<code>cls</code> 作为类方法第一个参数名。</p></blockquote><h3 id="1-3-init-vs-new"><a href="#1-3-init-vs-new" class="headerlink" title="1.3 __init__ vs __new__"></a>1.3 <code>__init__</code> vs <code>__new__</code></h3><p><strong>对象创建的完整流程</strong>：</p><figure class="highlight plaintext"><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><span class="line">执行 Person(&quot;Alice&quot;, 30) 时的内部流程：</span><br><span class="line"></span><br><span class="line">  Step 1: Person.__new__(Person, &quot;Alice&quot;, 30)</span><br><span class="line">          ├── 创建实例对象（分配内存）</span><br><span class="line">          └── 返回实例 obj</span><br><span class="line"></span><br><span class="line">  Step 2: Person.__init__(obj, &quot;Alice&quot;, 30)</span><br><span class="line">          ├── 初始化实例属性</span><br><span class="line">          └── 返回 None</span><br><span class="line"></span><br><span class="line">  Step 3: 返回 obj 给调用者</span><br></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></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Singleton</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;单例模式：通过 __new__ 控制实例创建。&quot;&quot;&quot;</span></span><br><span class="line">    _instance = <span class="literal">None</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__new__</span>(<span class="params">cls, *args, **kwargs</span>):</span><br><span class="line">        <span class="comment"># __new__ 负责创建实例（真正的构造函数）</span></span><br><span class="line">        <span class="keyword">if</span> cls._instance <span class="keyword">is</span> <span class="literal">None</span>:</span><br><span class="line">            cls._instance = <span class="built_in">super</span>().__new__(cls)</span><br><span class="line">        <span class="keyword">return</span> cls._instance</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, value</span>):</span><br><span class="line">        <span class="comment"># __init__ 负责初始化实例（每次&quot;实例化&quot;都会调用）</span></span><br><span class="line">        <span class="comment"># 注意：单例中 __init__ 每次都会被调用！</span></span><br><span class="line">        <span class="variable language_">self</span>.value = value</span><br><span class="line"></span><br><span class="line">a = Singleton(<span class="number">1</span>)</span><br><span class="line">b = Singleton(<span class="number">2</span>)</span><br><span class="line"><span class="built_in">print</span>(a <span class="keyword">is</span> b)          <span class="comment"># True —— 同一个实例</span></span><br><span class="line"><span class="built_in">print</span>(a.value)         <span class="comment"># 2 —— 第二次 __init__ 覆盖了 value</span></span><br><span class="line"><span class="built_in">print</span>(b.value)         <span class="comment"># 2</span></span><br></pre></td></tr></table></figure><p><strong>关键区别</strong>：</p><table><thead><tr><th>方法</th><th>职责</th><th>返回值</th><th>调用时机</th><th>是否常重写</th></tr></thead><tbody><tr><td><code>__new__(cls)</code></td><td>创建实例对象</td><td>必须返回实例</td><td>在 <code>__init__</code> 之前</td><td>很少重写</td></tr><tr><td><code>__init__(self)</code></td><td>初始化实例属性</td><td>必须返回 <code>None</code></td><td>在 <code>__new__</code> 之后</td><td>经常重写</td></tr></tbody></table><blockquote><p><strong>易错点</strong>：单例模式中 <code>__init__</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></pre></td><td class="code"><pre><span class="line">s1 = Singleton(<span class="number">1</span>)</span><br><span class="line">s2 = Singleton(<span class="number">2</span>)  <span class="comment"># __init__ 再次执行，value 被覆盖为 2</span></span><br><span class="line"><span class="built_in">print</span>(s1 <span class="keyword">is</span> s2)     <span class="comment"># True</span></span><br><span class="line"><span class="built_in">print</span>(s1.value)     <span class="comment"># 2 ← 被覆盖了！</span></span><br></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></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, value</span>):</span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> <span class="built_in">hasattr</span>(<span class="variable language_">self</span>, <span class="string">&#x27;_initialized&#x27;</span>):</span><br><span class="line">        <span class="variable language_">self</span>.value = value</span><br><span class="line">        <span class="variable language_">self</span>._initialized = <span class="literal">True</span></span><br></pre></td></tr></table></figure></blockquote><blockquote><p><strong>面试真题</strong>：<code>__new__</code> 返回的不是 <code>cls</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><span class="line"><span class="keyword">class</span> <span class="title class_">Weird</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__new__</span>(<span class="params">cls, *args, **kwargs</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;I&#x27;m a string, not an instance&quot;</span></span><br><span class="line"></span><br><span class="line">w = Weird()</span><br><span class="line"><span class="built_in">print</span>(w)           <span class="comment"># &quot;I&#x27;m a string, not an instance&quot;</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">type</span>(w))     <span class="comment"># &lt;class &#x27;str&#x27;&gt;  —— __init__ 不会被调用！</span></span><br></pre></td></tr></table></figure></blockquote><p><strong>工程建议</strong>：99% 的场景不需要重写 <code>__new__</code>，只在以下场景使用：</p><ul><li>单例模式</li><li>不可变类型子类化（如 <code>str</code>、<code>int</code>、<code>tuple</code> 子类）</li><li>自定义元类中控制类创建</li></ul><h3 id="1-4-slots-：限制实例属性"><a href="#1-4-slots-：限制实例属性" class="headerlink" title="1.4 __slots__：限制实例属性"></a>1.4 <code>__slots__</code>：限制实例属性</h3><p>默认情况下，Python 实例可以动态添加任意属性。使用 <code>__slots__</code> 可以限制允许的属性，同时<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></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Point</span>:</span><br><span class="line">    __slots__ = (<span class="string">&#x27;x&#x27;</span>, <span class="string">&#x27;y&#x27;</span>)  <span class="comment"># 只允许 x 和 y 属性</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, x, y</span>):</span><br><span class="line">        <span class="variable language_">self</span>.x = x</span><br><span class="line">        <span class="variable language_">self</span>.y = y</span><br><span class="line"></span><br><span class="line">p = Point(<span class="number">1</span>, <span class="number">2</span>)</span><br><span class="line"><span class="comment"># p.z = 3  # AttributeError: &#x27;Point&#x27; object has no attribute &#x27;z&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># p.__dict__  # AttributeError: 没有 __dict__ 了！</span></span><br></pre></td></tr></table></figure><p><strong><code>__slots__</code> 的内存对比</strong>：</p><figure class="highlight plaintext"><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><span class="line">普通类实例：</span><br><span class="line">  ┌──────────────┐</span><br><span class="line">  │ __dict__ ────┼──→ &#123;name: ..., age: ..., ...&#125;  ← 字典开销大</span><br><span class="line">  │ __weakref__  │</span><br><span class="line">  └──────────────┘</span><br><span class="line">  100 万个实例 ≈ 200+ MB</span><br><span class="line"></span><br><span class="line">__slots__ 类实例：</span><br><span class="line">  ┌──────────────┐</span><br><span class="line">  │ x (slot)     │  ← 直接存储，无字典</span><br><span class="line">  │ y (slot)     │</span><br><span class="line">  └──────────────┘</span><br><span class="line">  100 万个实例 ≈ 16 MB（约 10 倍差距）</span><br></pre></td></tr></table></figure><table><thead><tr><th>特性</th><th>普通类</th><th><code>__slots__</code> 类</th></tr></thead><tbody><tr><td>内存占用</td><td>大（每个实例有 <code>__dict__</code>）</td><td>小（直接存储）</td></tr><tr><td>动态属性</td><td>支持</td><td>不支持</td></tr><tr><td>属性速度</td><td>慢（字典查找）</td><td>快（描述符直接访问）</td></tr><tr><td>继承</td><td>正常</td><td>子类也需定义 <code>__slots__</code></td></tr><tr><td>序列化</td><td>直接 <code>__dict__</code></td><td>需手动处理</td></tr></tbody></table><blockquote><p><strong>工程建议</strong>：当你需要创建大量实例（如百万级数据对象）时，使用 <code>__slots__</code> 可以显著节省内存。但如果项目规模不大，不需要为了优化而使用。</p></blockquote><hr><h2 id="2-类方法、静态方法、实例方法"><a href="#2-类方法、静态方法、实例方法" class="headerlink" title="2. 类方法、静态方法、实例方法"></a>2. 类方法、静态方法、实例方法</h2><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><span class="line"><span class="keyword">class</span> <span class="title class_">DateUtils</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;三种方法的对比。&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">instance_method</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;实例方法：操作实例数据，需要 self。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">return</span> <span class="string">f&quot;实例方法，可以访问: <span class="subst">&#123;self&#125;</span>&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="meta">    @classmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">class_method</span>(<span class="params">cls</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;类方法：操作类级别数据，第一个参数是类本身 cls。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">return</span> <span class="string">f&quot;类方法，可以访问: <span class="subst">&#123;cls&#125;</span>&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="meta">    @staticmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">static_method</span>():</span><br><span class="line">        <span class="string">&quot;&quot;&quot;静态方法：与类/实例无关的工具函数，放在类中仅是逻辑分组。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;静态方法，不需要 self 或 cls&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="meta">    @classmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">from_string</span>(<span class="params">cls, date_str</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;类方法典型用途：替代构造器（工厂方法）。&quot;&quot;&quot;</span></span><br><span class="line">        year, month, day = date_str.split(<span class="string">&quot;-&quot;</span>)</span><br><span class="line">        <span class="keyword">return</span> cls(<span class="built_in">int</span>(year), <span class="built_in">int</span>(month), <span class="built_in">int</span>(day))</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用对比</span></span><br><span class="line">du = DateUtils()</span><br><span class="line"><span class="built_in">print</span>(du.instance_method())   <span class="comment"># 实例方法</span></span><br><span class="line"><span class="built_in">print</span>(DateUtils.class_method())    <span class="comment"># 类方法（推荐用类调用）</span></span><br><span class="line"><span class="built_in">print</span>(DateUtils.static_method())   <span class="comment"># 静态方法</span></span><br></pre></td></tr></table></figure><p><strong>三种方法对比决策树</strong>：</p><figure class="highlight plaintext"><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><span class="line">需要访问实例属性（self）？</span><br><span class="line">  ├── 是 → 实例方法</span><br><span class="line">  └── 否 → 需要访问类属性（cls）或创建实例？</span><br><span class="line">              ├── 是 → @classmethod</span><br><span class="line">              │         （工厂方法、替代构造器、修改类状态）</span><br><span class="line">              └── 否 → @staticmethod</span><br><span class="line">                        （纯工具函数，逻辑上属于这个类）</span><br></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><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><span class="line"><span class="keyword">class</span> <span class="title class_">Date</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, year, month, day</span>):</span><br><span class="line">        <span class="variable language_">self</span>.year = year</span><br><span class="line">        <span class="variable language_">self</span>.month = month</span><br><span class="line">        <span class="variable language_">self</span>.day = day</span><br><span class="line"></span><br><span class="line"><span class="meta">    @classmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">from_string</span>(<span class="params">cls, date_str</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;从字符串创建：Date.from_string(&quot;2024-01-15&quot;)&quot;&quot;&quot;</span></span><br><span class="line">        year, month, day = <span class="built_in">map</span>(<span class="built_in">int</span>, date_str.split(<span class="string">&quot;-&quot;</span>))</span><br><span class="line">        <span class="keyword">return</span> cls(year, month, day)</span><br><span class="line"></span><br><span class="line"><span class="meta">    @classmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">from_timestamp</span>(<span class="params">cls, ts</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;从时间戳创建：Date.from_timestamp(1705276800)&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">import</span> time</span><br><span class="line">        t = time.localtime(ts)</span><br><span class="line">        <span class="keyword">return</span> cls(t.tm_year, t.tm_mon, t.tm_mday)</span><br><span class="line"></span><br><span class="line"><span class="meta">    @classmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">today</span>(<span class="params">cls</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;从当前日期创建：Date.today()&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">import</span> time</span><br><span class="line">        t = time.localtime()</span><br><span class="line">        <span class="keyword">return</span> cls(t.tm_year, t.tm_mon, t.tm_mday)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__repr__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="string">f&quot;Date(<span class="subst">&#123;self.year&#125;</span>-<span class="subst">&#123;self.month:02d&#125;</span>-<span class="subst">&#123;self.day:02d&#125;</span>)&quot;</span></span><br><span class="line"></span><br><span class="line">d1 = Date.from_string(<span class="string">&quot;2024-01-15&quot;</span>)</span><br><span class="line">d2 = Date.from_timestamp(<span class="number">1705276800</span>)</span><br><span class="line">d3 = Date.today()</span><br><span class="line"><span class="built_in">print</span>(d1, d2, d3)</span><br></pre></td></tr></table></figure><blockquote><p><strong>面试真题</strong>：<code>@staticmethod</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><span class="line"><span class="keyword">class</span> <span class="title class_">MathUtils</span>:</span><br><span class="line"><span class="meta">    @staticmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">square</span>(<span class="params">x</span>):</span><br><span class="line">        <span class="keyword">return</span> x ** <span class="number">2</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">square_outside</span>(<span class="params">x</span>):</span><br><span class="line">    <span class="keyword">return</span> x ** <span class="number">2</span></span><br></pre></td></tr></table></figure><p><strong>答案</strong>：功能上没有区别。<code>@staticmethod</code> 只是逻辑分组——告诉使用者”这个函数和这个类有关”。调用方式上 <code>MathUtils.square(5)</code> 比全局 <code>square_outside(5)</code> 更有组织性。如果你的工具函数和某个类概念上相关，就放在类里作为静态方法；否则直接放模块级。</p></blockquote><blockquote><p><strong>工程建议</strong>：当你发现自己写了多个创建对象的替代方式时，用 <code>@classmethod</code> 做工厂方法，而不是在 <code>__init__</code> 中写一堆 <code>if-else</code> 分支。</p></blockquote><hr><h2 id="3-封装与访问控制"><a href="#3-封装与访问控制" class="headerlink" title="3. 封装与访问控制"></a>3. 封装与访问控制</h2><h3 id="3-1-Python-的”信任”哲学"><a href="#3-1-Python-的”信任”哲学" class="headerlink" title="3.1 Python 的”信任”哲学"></a>3.1 Python 的”信任”哲学</h3><p><strong>Python vs Java&#x2F;C++ 的访问控制对比</strong>：</p><figure class="highlight plaintext"><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><span class="line">Java/C++ 的访问控制（强制）：</span><br><span class="line">  ┌───────────┐</span><br><span class="line">  │  public   │ ← 任何地方都可访问</span><br><span class="line">  ├───────────┤</span><br><span class="line">  │ protected │ ← 子类和同包可访问</span><br><span class="line">  ├───────────┤</span><br><span class="line">  │  private  │ ← 仅类内部可访问（编译器强制）</span><br><span class="line">  └───────────┘</span><br><span class="line"></span><br><span class="line">Python 的访问控制（约定）：</span><br><span class="line">  ┌───────────┐</span><br><span class="line">  │  public   │ ← name</span><br><span class="line">  ├───────────┤</span><br><span class="line">  │ _protected│ ← _name（约定：内部使用，但可以访问）</span><br><span class="line">  ├───────────┤</span><br><span class="line">  │ __private │ ← __name（名称改写：_ClassName__name）</span><br><span class="line">  └───────────┘</span><br><span class="line">  &quot;We are all consenting adults here.&quot; —— Python 社区格言</span><br></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></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">BankAccount</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, owner, balance</span>):</span><br><span class="line">        <span class="variable language_">self</span>.owner = owner          <span class="comment"># 公开属性</span></span><br><span class="line">        <span class="variable language_">self</span>._balance = balance     <span class="comment"># 受保护（约定：子类和内部使用）</span></span><br><span class="line">        <span class="variable language_">self</span>.__pin = <span class="string">&quot;1234&quot;</span>         <span class="comment"># 私有（名称改写：_BankAccount__pin）</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">deposit</span>(<span class="params">self, amount</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;公开方法：存款。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">if</span> amount &gt; <span class="number">0</span>:</span><br><span class="line">            <span class="variable language_">self</span>._balance += amount</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>._balance</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">_validate_amount</span>(<span class="params">self, amount</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;受保护方法：内部验证逻辑。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">return</span> amount &gt; <span class="number">0</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__hash_pin</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;私有方法。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">hash</span>(<span class="variable language_">self</span>.__pin)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用</span></span><br><span class="line">account = BankAccount(<span class="string">&quot;Alice&quot;</span>, <span class="number">1000</span>)</span><br><span class="line"><span class="built_in">print</span>(account._balance)           <span class="comment"># 1000 —— 可以访问，但不应直接访问</span></span><br><span class="line"><span class="comment"># print(account.__pin)            # AttributeError —— 名称改写，无法直接访问</span></span><br><span class="line"><span class="built_in">print</span>(account._BankAccount__pin)  <span class="comment"># &quot;1234&quot; —— 但可以绕过（不推荐！）</span></span><br></pre></td></tr></table></figure><p><strong>名称改写（Name Mangling）的原理</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></pre></td><td class="code"><pre><span class="line"><span class="comment"># Python 解释器在编译类定义时，会将 __attr 改写为 _ClassName__attr</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">MyClass</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="variable language_">self</span>.__secret = <span class="string">&quot;hidden&quot;</span></span><br><span class="line"></span><br><span class="line">obj = MyClass()</span><br><span class="line"><span class="built_in">print</span>(obj.__secret)          <span class="comment"># AttributeError</span></span><br><span class="line"><span class="built_in">print</span>(obj._MyClass__secret)  <span class="comment"># &quot;hidden&quot;</span></span><br><span class="line"><span class="built_in">print</span>([k <span class="keyword">for</span> k <span class="keyword">in</span> <span class="built_in">dir</span>(obj) <span class="keyword">if</span> <span class="string">&#x27;secret&#x27;</span> <span class="keyword">in</span> k])  <span class="comment"># [&#x27;_MyClass__secret&#x27;]</span></span><br></pre></td></tr></table></figure><blockquote><p><strong>面试真题</strong>：<code>_name</code> 和 <code>__name</code> 有什么区别？</p><ul><li><code>_name</code>：纯约定，Python 不做任何特殊处理，外部可以访问</li><li><code>__name</code>：触发名称改写，变为 <code>_ClassName__name</code>，间接”私有化”</li><li><code>__name__</code>（双下划线开头和结尾）：Python 魔术方法&#x2F;属性，<strong>不要</strong>自己发明</li></ul></blockquote><blockquote><p><strong>工程建议</strong>：Python 社区的最佳实践是：</p><ol><li>默认用公开属性（<code>name</code>），因为 Python 没有真正的私有</li><li>需要内部标记时用 <code>_name</code></li><li>只在确实需要防止子类覆盖时用 <code>__name</code></li><li>不要用 <code>__double_leading_and_trailing__</code>，那是 Python 保留的</li></ol></blockquote><h3 id="3-2-property：Pythonic-的-getter-setter"><a href="#3-2-property：Pythonic-的-getter-setter" class="headerlink" title="3.2 @property：Pythonic 的 getter&#x2F;setter"></a>3.2 <code>@property</code>：Pythonic 的 getter&#x2F;setter</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><span class="line"><span class="keyword">class</span> <span class="title class_">Temperature</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, celsius=<span class="number">0</span></span>):</span><br><span class="line">        <span class="variable language_">self</span>._celsius = celsius</span><br><span class="line"></span><br><span class="line"><span class="meta">    @property</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">celsius</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;获取摄氏温度。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>._celsius</span><br><span class="line"></span><br><span class="line"><span class="meta">    @celsius.setter</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">celsius</span>(<span class="params">self, value</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;设置摄氏温度，带验证。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">if</span> value &lt; -<span class="number">273.15</span>:</span><br><span class="line">            <span class="keyword">raise</span> ValueError(<span class="string">&quot;温度不能低于绝对零度&quot;</span>)</span><br><span class="line">        <span class="variable language_">self</span>._celsius = value</span><br><span class="line"></span><br><span class="line"><span class="meta">    @property</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">fahrenheit</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;计算华氏温度（只读属性）。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>._celsius * <span class="number">9</span> / <span class="number">5</span> + <span class="number">32</span></span><br><span class="line"></span><br><span class="line"><span class="meta">    @property</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">kelvin</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;计算开尔文温度（只读属性）。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>._celsius + <span class="number">273.15</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用</span></span><br><span class="line">temp = Temperature(<span class="number">25</span>)</span><br><span class="line"><span class="built_in">print</span>(temp.celsius)       <span class="comment"># 25</span></span><br><span class="line"><span class="built_in">print</span>(temp.fahrenheit)    <span class="comment"># 77.0</span></span><br><span class="line"><span class="built_in">print</span>(temp.kelvin)        <span class="comment"># 298.15</span></span><br><span class="line"></span><br><span class="line">temp.celsius = <span class="number">30</span>         <span class="comment"># 通过 setter 设置</span></span><br><span class="line"><span class="built_in">print</span>(temp.celsius)       <span class="comment"># 30</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># temp.fahrenheit = 100   # AttributeError: 只读属性</span></span><br><span class="line"><span class="comment"># temp.celsius = -300     # ValueError: 温度不能低于绝对零度</span></span><br></pre></td></tr></table></figure><p><strong>property 的完整生命周期</strong>：</p><figure class="highlight plaintext"><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><span class="line">┌────────────────────────────────────────────────┐</span><br><span class="line">│              @property 装饰器                    │</span><br><span class="line">├────────────────────────────────────────────────┤</span><br><span class="line">│                                                │</span><br><span class="line">│  @property         → getter（读取属性）         │</span><br><span class="line">│  @xxx.setter       → setter（赋值属性）          │</span><br><span class="line">│  @xxx.deleter      → deleter（删除属性）         │</span><br><span class="line">│                                                │</span><br><span class="line">│  使用场景：                                      │</span><br><span class="line">│  1. 属性需要验证/转换                            │</span><br><span class="line">│  2. 只读计算属性（如 fahrenheit）                │</span><br><span class="line">│  3. 属性变更需要触发副作用（如日志、通知）         │</span><br><span class="line">│  4. 平滑升级：先公开属性，后续加 property 不破坏 API│</span><br><span class="line">│                                                │</span><br><span class="line">└────────────────────────────────────────────────┘</span><br></pre></td></tr></table></figure><p><strong>property 的优势</strong>：</p><ul><li>对外暴露像属性一样访问，内部包含验证&#x2F;计算逻辑</li><li>后续可以给已有属性添加验证，而不破坏外部 API</li><li>配合 <code>@deleter</code> 可控制删除行为</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">User</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, name</span>):</span><br><span class="line">        <span class="variable language_">self</span>._name = name</span><br><span class="line"></span><br><span class="line"><span class="meta">    @property</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">name</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>._name</span><br><span class="line"></span><br><span class="line"><span class="meta">    @name.setter</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">name</span>(<span class="params">self, value</span>):</span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> value.strip():</span><br><span class="line">            <span class="keyword">raise</span> ValueError(<span class="string">&quot;用户名不能为空&quot;</span>)</span><br><span class="line">        <span class="variable language_">self</span>._name = value.strip()</span><br><span class="line"></span><br><span class="line"><span class="meta">    @name.deleter</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">name</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;删除用户名&quot;</span>)</span><br><span class="line">        <span class="variable language_">self</span>._name = <span class="literal">None</span></span><br></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><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><span class="line"><span class="comment"># 版本 1：直接公开属性</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Product</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, price</span>):</span><br><span class="line">        <span class="variable language_">self</span>.price = price</span><br><span class="line"></span><br><span class="line"><span class="comment"># 用户代码：p.price = 100</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 版本 2：需要加验证，用 property 保护已有 API</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Product</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, price</span>):</span><br><span class="line">        <span class="variable language_">self</span>.price = price  <span class="comment"># 注意：这里 self.price 会调用 setter！</span></span><br><span class="line"></span><br><span class="line"><span class="meta">    @property</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">price</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>._price</span><br><span class="line"></span><br><span class="line"><span class="meta">    @price.setter</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">price</span>(<span class="params">self, value</span>):</span><br><span class="line">        <span class="keyword">if</span> value &lt; <span class="number">0</span>:</span><br><span class="line">            <span class="keyword">raise</span> ValueError(<span class="string">&quot;价格不能为负&quot;</span>)</span><br><span class="line">        <span class="variable language_">self</span>._price = value</span><br><span class="line"></span><br><span class="line"><span class="comment"># 用户代码无需修改：p.price = 100 仍然有效</span></span><br></pre></td></tr></table></figure><blockquote><p><strong>易错点</strong>：在 <code>__init__</code> 中给 <code>self.price</code> 赋值时，会触发 <code>@price.setter</code>！</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">p = Product(-<span class="number">10</span>)  <span class="comment"># ValueError: 价格不能为负</span></span><br></pre></td></tr></table></figure></blockquote><blockquote><p><strong>工程建议</strong>：不要一开始就给所有属性加 <code>@property</code>。先用简单公开属性，需要验证时再用 property 替换——这就是 Python 的”渐进式封装”哲学。</p></blockquote><h3 id="3-3-slots-与内存优化"><a href="#3-3-slots-与内存优化" class="headerlink" title="3.3 __slots__ 与内存优化"></a>3.3 <code>__slots__</code> 与内存优化</h3><p>当需要创建大量实例时，<code>__slots__</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> sys</span><br><span class="line"></span><br><span class="line"><span class="comment"># 普通类</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">PointNormal</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, x, y</span>):</span><br><span class="line">        <span class="variable language_">self</span>.x = x</span><br><span class="line">        <span class="variable language_">self</span>.y = y</span><br><span class="line"></span><br><span class="line"><span class="comment"># slots 类</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">PointSlots</span>:</span><br><span class="line">    __slots__ = (<span class="string">&#x27;x&#x27;</span>, <span class="string">&#x27;y&#x27;</span>)</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, x, y</span>):</span><br><span class="line">        <span class="variable language_">self</span>.x = x</span><br><span class="line">        <span class="variable language_">self</span>.y = y</span><br><span class="line"></span><br><span class="line">p1 = PointNormal(<span class="number">1</span>, <span class="number">2</span>)</span><br><span class="line">p2 = PointSlots(<span class="number">1</span>, <span class="number">2</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 内存对比</span></span><br><span class="line"><span class="built_in">print</span>(sys.getsizeof(p1.__dict__))  <span class="comment"># 104 bytes（字典本身）</span></span><br><span class="line"><span class="built_in">print</span>(sys.getsizeof(p2))           <span class="comment"># 48 bytes（无字典开销）</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建 100 万个实例：</span></span><br><span class="line"><span class="comment"># PointNormal: ~200 MB</span></span><br><span class="line"><span class="comment"># PointSlots:  ~48 MB（约 4 倍差距）</span></span><br></pre></td></tr></table></figure><hr><h2 id="4-继承与多态"><a href="#4-继承与多态" class="headerlink" title="4. 继承与多态"></a>4. 继承与多态</h2><h3 id="4-1-单继承与多态"><a href="#4-1-单继承与多态" class="headerlink" title="4.1 单继承与多态"></a>4.1 单继承与多态</h3><p><strong>继承的 UML 类图</strong>：</p><figure class="highlight plaintext"><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><span class="line">         ┌──────────┐</span><br><span class="line">         │  Animal   │  ← 父类（基类）</span><br><span class="line">         │──────────│</span><br><span class="line">         │ + name   │</span><br><span class="line">         │ + speak()│  ← 抽象方法（子类必须实现）</span><br><span class="line">         └────┬─────┘</span><br><span class="line">              │ 继承</span><br><span class="line">     ┌────────┴────────┐</span><br><span class="line">     ▼                 ▼</span><br><span class="line">┌──────────┐    ┌──────────┐</span><br><span class="line">│   Dog     │    │   Cat     │  ← 子类（派生类）</span><br><span class="line">│──────────│    │──────────│</span><br><span class="line">│ + speak()│    │ + speak()│  ← 多态：各自实现</span><br><span class="line">│ &quot;汪汪！&quot;  │    │ &quot;喵喵！&quot;  │</span><br><span class="line">└──────────┘    └──────────┘</span><br></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></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Animal</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, name</span>):</span><br><span class="line">        <span class="variable language_">self</span>.name = name</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">speak</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">raise</span> NotImplementedError(<span class="string">&quot;子类必须实现 speak 方法&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Dog</span>(<span class="title class_ inherited__">Animal</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">speak</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="string">f&quot;<span class="subst">&#123;self.name&#125;</span>: 汪汪！&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Cat</span>(<span class="title class_ inherited__">Animal</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">speak</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="string">f&quot;<span class="subst">&#123;self.name&#125;</span>: 喵喵！&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 多态：同一接口，不同行为</span></span><br><span class="line">animals = [Dog(<span class="string">&quot;大黄&quot;</span>), Cat(<span class="string">&quot;小花&quot;</span>), Dog(<span class="string">&quot;小黑&quot;</span>)]</span><br><span class="line"><span class="keyword">for</span> animal <span class="keyword">in</span> animals:</span><br><span class="line">    <span class="built_in">print</span>(animal.speak())</span><br><span class="line"><span class="comment"># 大黄: 汪汪！</span></span><br><span class="line"><span class="comment"># 小花: 喵喵！</span></span><br><span class="line"><span class="comment"># 小黑: 汪汪！</span></span><br></pre></td></tr></table></figure><p><strong>多态的本质</strong>：</p><figure class="highlight plaintext"><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><span class="line">多态不是&quot;检查类型&quot;，而是&quot;检查行为&quot;：</span><br><span class="line"></span><br><span class="line">  ┌──────────────────────────────────────────┐</span><br><span class="line">  │  for animal in animals:                  │</span><br><span class="line">  │      animal.speak()                      │</span><br><span class="line">  │                                          │</span><br><span class="line">  │  Python 不关心 animal 是 Dog 还是 Cat，  │</span><br><span class="line">  │  只要有 speak() 方法就行。                │</span><br><span class="line">  │                                          │</span><br><span class="line">  │  这就是&quot;鸭子类型&quot;——多态的 Python 实现。   │</span><br><span class="line">  └──────────────────────────────────────────┘</span><br></pre></td></tr></table></figure><h3 id="4-2-多继承与-MRO（方法解析顺序）"><a href="#4-2-多继承与-MRO（方法解析顺序）" class="headerlink" title="4.2 多继承与 MRO（方法解析顺序）"></a>4.2 多继承与 MRO（方法解析顺序）</h3><p><strong>菱形继承结构</strong>：</p><figure class="highlight plaintext"><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><span class="line">        ┌───┐</span><br><span class="line">        │ A │  ← 共同祖先</span><br><span class="line">        └─┬─┘</span><br><span class="line">       ┌──┴──┐</span><br><span class="line">       ▼     ▼</span><br><span class="line">    ┌───┐ ┌───┐</span><br><span class="line">    │ B │ │ C │  ← 中间层</span><br><span class="line">    └─┬─┘ └─┬─┘</span><br><span class="line">       └──┬──┘</span><br><span class="line">          ▼</span><br><span class="line">       ┌───┐</span><br><span class="line">       │ D │  ← 多继承：class D(B, C)</span><br><span class="line">       └───┘</span><br><span class="line"></span><br><span class="line">MRO 顺序：D → B → C → A → object</span><br><span class="line"></span><br><span class="line">问题：A 的方法会被调用几次？</span><br><span class="line">  ❌ 错误（C++ 菱形继承）：A 被调用 2 次</span><br><span class="line">  ✅ Python（C3 线性化）：A 只被调用 1 次</span><br></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><span class="line"><span class="keyword">class</span> <span class="title class_">A</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">method</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;A.method&quot;</span>)</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;A&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">B</span>(<span class="title class_ inherited__">A</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">method</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;B.method&quot;</span>)</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;B&quot;</span> + <span class="built_in">super</span>().method()</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">C</span>(<span class="title class_ inherited__">A</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">method</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;C.method&quot;</span>)</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;C&quot;</span> + <span class="built_in">super</span>().method()</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">D</span>(B, C):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">method</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;D.method&quot;</span>)</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;D&quot;</span> + <span class="built_in">super</span>().method()</span><br><span class="line"></span><br><span class="line">d = D()</span><br><span class="line"><span class="built_in">print</span>(d.method())   <span class="comment"># D.method → B.method → C.method → A.method → &quot;DBCA&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看 MRO</span></span><br><span class="line"><span class="built_in">print</span>(D.__mro__)</span><br><span class="line"><span class="comment"># (&lt;class &#x27;D&#x27;&gt;, &lt;class &#x27;B&#x27;&gt;, &lt;class &#x27;C&#x27;&gt;, &lt;class &#x27;A&#x27;&gt;, &lt;class &#x27;object&#x27;&gt;)</span></span><br></pre></td></tr></table></figure><p><strong>C3 线性化算法的核心规则</strong>：</p><figure class="highlight plaintext"><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><span class="line">1. 子类优先于父类</span><br><span class="line">2. 在继承列表中，靠前的父类优先于靠后的（B 优先于 C）</span><br><span class="line">3. 所有父类的方法只被调用一次（保证 A 只被调用一次）</span><br><span class="line"></span><br><span class="line">验证：D.__mro__ = (D, B, C, A, object)</span><br><span class="line">  D → B → C → A → object</span><br><span class="line">  ✓ D 在最前（子类优先）</span><br><span class="line">  ✓ B 在 C 前（B 是 D 的第一个父类）</span><br><span class="line">  ✓ A 只出现一次</span><br></pre></td></tr></table></figure><blockquote><p><strong>面试真题</strong>：以下代码的 MRO 是什么？</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">X</span>: <span class="keyword">pass</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Y</span>: <span class="keyword">pass</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">A</span>(X, Y): <span class="keyword">pass</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">B</span>(Y, X): <span class="keyword">pass</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">C</span>(A, B): <span class="keyword">pass</span></span><br><span class="line"><span class="built_in">print</span>(C.__mro__)</span><br></pre></td></tr></table></figure><p><strong>答案</strong>：会抛出 <code>TypeError: Cannot create a consistent method resolution</code>。因为 A 要求 X 在 Y 前面，B 要求 Y 在 X 前面，C 继承 A 和 B 时产生矛盾，C3 算法无法线性化。</p></blockquote><blockquote><p><strong>工程建议</strong>：Python 的多继承虽然强大，但过度使用会导致继承链混乱。最佳实践是：</p><ol><li>主继承链用单继承</li><li>额外功能用 Mixin 模式混入</li><li>接口约束用 ABC</li><li>如果继承超过 3 层，考虑用组合替代继承</li></ol></blockquote><h3 id="4-3-super-的使用"><a href="#4-3-super-的使用" class="headerlink" title="4.3 super() 的使用"></a>4.3 <code>super()</code> 的使用</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><span class="line"><span class="keyword">class</span> <span class="title class_">Parent</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, name, age</span>):</span><br><span class="line">        <span class="variable language_">self</span>.name = name</span><br><span class="line">        <span class="variable language_">self</span>.age = age</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Child</span>(<span class="title class_ inherited__">Parent</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, name, age, school</span>):</span><br><span class="line">        <span class="built_in">super</span>().__init__(name, age)   <span class="comment"># 调用父类构造方法</span></span><br><span class="line">        <span class="variable language_">self</span>.school = school</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__repr__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="string">f&quot;Child(name=<span class="subst">&#123;self.name&#125;</span>, age=<span class="subst">&#123;self.age&#125;</span>, school=<span class="subst">&#123;self.school&#125;</span>)&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># super() 的两种用法</span></span><br><span class="line"><span class="comment"># Python 3: super().__init__(name, age)           # 推荐</span></span><br><span class="line"><span class="comment"># Python 2: super(Child, self).__init__(name, age) # 旧式写法</span></span><br></pre></td></tr></table></figure><p><strong><code>super()</code> 在多继承中的行为</strong>：</p><figure class="highlight plaintext"><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><span class="line">super() 不是&quot;调用父类&quot;，而是&quot;按 MRO 顺序调用下一个类&quot;</span><br><span class="line"></span><br><span class="line">  Combined 的 MRO: [Combined, MixinA, MixinB, Base, object]</span><br><span class="line"></span><br><span class="line">  Combined.process()</span><br><span class="line">      → super().process()  ← super() 指向 MixinA（MRO 中的下一个）</span><br><span class="line">      </span><br><span class="line">  MixinA.process()</span><br><span class="line">      → super().process()  ← super() 指向 MixinB（MRO 中的下一个）</span><br><span class="line">      </span><br><span class="line">  MixinB.process()</span><br><span class="line">      → super().process()  ← super() 指向 Base（MRO 中的下一个）</span><br><span class="line">      </span><br><span class="line">  Base.process()</span><br><span class="line">      → 终止（不再调用 super）</span><br></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></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">MixinA</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">process</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;MixinA.process&quot;</span>)</span><br><span class="line">        <span class="built_in">super</span>().process()       <span class="comment"># 按 MRO 继续调用下一个类</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">MixinB</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">process</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;MixinB.process&quot;</span>)</span><br><span class="line">        <span class="built_in">super</span>().process()</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Base</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">process</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;Base.process -&gt; 终止&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Combined</span>(MixinA, MixinB, Base):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">process</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;Combined.process&quot;</span>)</span><br><span class="line">        <span class="built_in">super</span>().process()</span><br><span class="line"></span><br><span class="line">c = Combined()</span><br><span class="line">c.process()</span><br><span class="line"><span class="comment"># Combined.process → MixinA.process → MixinB.process → Base.process → 终止</span></span><br></pre></td></tr></table></figure><blockquote><p><strong>易错点</strong>：<code>super().__init__()</code> 在多继承中可能会<strong>跳过某些父类的 <code>__init__</code></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></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">A</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;A.__init__&quot;</span>)</span><br><span class="line">        <span class="built_in">super</span>().__init__()</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">B</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;B.__init__&quot;</span>)</span><br><span class="line">        <span class="built_in">super</span>().__init__()</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">C</span>(A, B):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;C.__init__&quot;</span>)</span><br><span class="line">        <span class="built_in">super</span>().__init__()</span><br><span class="line"></span><br><span class="line">C()</span><br><span class="line"><span class="comment"># C.__init__ → A.__init__ → B.__init__ → object.__init__</span></span><br><span class="line"><span class="comment"># 如果 A 的 __init__ 没有调用 super().__init__()，B 的 __init__ 会被跳过！</span></span><br></pre></td></tr></table></figure><p><strong>结论</strong>：多继承时，所有类都必须调用 <code>super().__init__()</code>，否则 MRO 链条会断裂。</p></blockquote><h3 id="4-4-Mixin-模式"><a href="#4-4-Mixin-模式" class="headerlink" title="4.4 Mixin 模式"></a>4.4 Mixin 模式</h3><p><strong>Mixin 的设计原则</strong>：</p><figure class="highlight plaintext"><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><span class="line">┌────────────────────────────────────────────────────────────┐</span><br><span class="line">│                     Mixin 设计原则                          │</span><br><span class="line">├────────────────────────────────────────────────────────────┤</span><br><span class="line">│                                                            │</span><br><span class="line">│  1. Mixin 不单独实例化（它是&quot;插件&quot;）                          │</span><br><span class="line">│  2. Mixin 只添加功能，不改变核心逻辑                           │</span><br><span class="line">│  3. Mixin 通过 super() 参与协作链                             │</span><br><span class="line">│  4. Mixin 通常放在继承列表的最前面                             │</span><br><span class="line">│                                                            │</span><br><span class="line">│  组合优于继承：                                               │</span><br><span class="line">│                                                            │</span><br><span class="line">│  class User(JsonMixin, TimestampMixin, LogMixin):          │</span><br><span class="line">│      ↑          ↑               ↑            ↑             │</span><br><span class="line">│      核心类     JSON序列化      时间戳       日志            │</span><br><span class="line">│                                                            │</span><br><span class="line">│  每个 Mixin 提供一个独立功能，通过组合获得所有能力              │</span><br><span class="line">└────────────────────────────────────────────────────────────┘</span><br></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><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">JsonMixin</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;提供 JSON 序列化能力。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">to_json</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">import</span> json</span><br><span class="line">        <span class="keyword">return</span> json.dumps(<span class="variable language_">self</span>.__dict__, ensure_ascii=<span class="literal">False</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">TimestampMixin</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;提供时间戳记录能力。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, *args, **kwargs</span>):</span><br><span class="line">        <span class="built_in">super</span>().__init__(*args, **kwargs)</span><br><span class="line">        <span class="keyword">import</span> time</span><br><span class="line">        <span class="variable language_">self</span>.created_at = time.time()</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">LogMixin</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;提供日志记录能力。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">log</span>(<span class="params">self, message</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;[<span class="subst">&#123;self.__class__.__name__&#125;</span>] <span class="subst">&#123;message&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">User</span>(JsonMixin, TimestampMixin, LogMixin):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, name, email</span>):</span><br><span class="line">        <span class="built_in">super</span>().__init__()    <span class="comment"># 调用 TimestampMixin.__init__</span></span><br><span class="line">        <span class="variable language_">self</span>.name = name</span><br><span class="line">        <span class="variable language_">self</span>.email = email</span><br><span class="line"></span><br><span class="line">user = User(<span class="string">&quot;Alice&quot;</span>, <span class="string">&quot;alice@example.com&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(user.to_json())</span><br><span class="line"><span class="comment"># &#123;&quot;name&quot;: &quot;Alice&quot;, &quot;email&quot;: &quot;alice@example.com&quot;, &quot;created_at&quot;: 1690000000.0&#125;</span></span><br><span class="line">user.log(<span class="string">&quot;用户创建成功&quot;</span>)</span><br><span class="line"><span class="comment"># [User] 用户创建成功</span></span><br></pre></td></tr></table></figure><blockquote><p><strong>工程建议</strong>：Mixin 是 Python 中实现”组合优于继承”的方式。当你需要给多个不相关的类添加相同功能（如日志、缓存、序列化）时，用 Mixin 比用继承更灵活。</p></blockquote><h3 id="4-5-鸭子类型与抽象基类"><a href="#4-5-鸭子类型与抽象基类" class="headerlink" title="4.5 鸭子类型与抽象基类"></a>4.5 鸭子类型与抽象基类</h3><p><strong>鸭子类型 vs ABC 决策</strong>：</p><figure class="highlight plaintext"><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><span class="line">需要接口约束？</span><br><span class="line">  ├── 否 → 鸭子类型（默认 Pythonic 方式）</span><br><span class="line">  │         优点：灵活、无需继承</span><br><span class="line">  │         缺点：类型不明确，IDE 补全差</span><br><span class="line">  │</span><br><span class="line">  └── 是 → 抽象基类（ABC）</span><br><span class="line">            优点：强制实现接口、提前报错</span><br><span class="line">            缺点：需要继承、不够灵活</span><br></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><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><span class="line"><span class="keyword">class</span> <span class="title class_">Duck</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">quack</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;嘎嘎嘎&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Person</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">quack</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;我在学鸭子叫&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 鸭子类型：只要对象有 quack() 方法，就可以调用</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">make_it_quack</span>(<span class="params">thing</span>):</span><br><span class="line">    <span class="built_in">print</span>(thing.quack())</span><br><span class="line"></span><br><span class="line">make_it_quack(Duck())     <span class="comment"># 嘎嘎嘎</span></span><br><span class="line">make_it_quack(Person())   <span class="comment"># 我在学鸭子叫</span></span><br></pre></td></tr></table></figure><p><strong>抽象基类（ABC）</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><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><span class="line"><span class="keyword">from</span> abc <span class="keyword">import</span> ABC, abstractmethod</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">PaymentProcessor</span>(<span class="title class_ inherited__">ABC</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;支付处理器抽象基类。&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="meta">    @abstractmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">pay</span>(<span class="params">self, amount</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;支付。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"><span class="meta">    @abstractmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">refund</span>(<span class="params">self, amount</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;退款。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">validate_amount</span>(<span class="params">self, amount</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;具体方法：子类可继承使用。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">if</span> amount &lt;= <span class="number">0</span>:</span><br><span class="line">            <span class="keyword">raise</span> ValueError(<span class="string">&quot;金额必须大于 0&quot;</span>)</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">True</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">AlipayProcessor</span>(<span class="title class_ inherited__">PaymentProcessor</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">pay</span>(<span class="params">self, amount</span>):</span><br><span class="line">        <span class="variable language_">self</span>.validate_amount(amount)</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;支付宝支付: <span class="subst">&#123;amount&#125;</span> 元&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">refund</span>(<span class="params">self, amount</span>):</span><br><span class="line">        <span class="variable language_">self</span>.validate_amount(amount)</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;支付宝退款: <span class="subst">&#123;amount&#125;</span> 元&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># p = PaymentProcessor()   # TypeError: 不能实例化抽象类</span></span><br><span class="line">processor = AlipayProcessor()</span><br><span class="line">processor.pay(<span class="number">100</span>)          <span class="comment"># 支付宝支付: 100 元</span></span><br></pre></td></tr></table></figure><p><strong>鸭子类型 vs ABC</strong>：</p><table><thead><tr><th>特性</th><th>鸭子类型</th><th>抽象基类</th></tr></thead><tbody><tr><td>检查方式</td><td>运行时</td><td>可提前（实例化时）</td></tr><tr><td>灵活性</td><td>高</td><td>低（必须实现所有抽象方法）</td></tr><tr><td>接口保证</td><td>无</td><td>有</td></tr><tr><td>Pythonic 程度</td><td>更 Pythonic</td><td>更”工程化”</td></tr><tr><td>IDE 支持</td><td>差</td><td>好</td></tr><tr><td>适用场景</td><td>脚本、快速开发</td><td>大型项目、团队协作</td></tr></tbody></table><h3 id="4-6-typing-Protocol：结构化类型（3-8-）"><a href="#4-6-typing-Protocol：结构化类型（3-8-）" class="headerlink" title="4.6 typing.Protocol：结构化类型（3.8+）"></a>4.6 <code>typing.Protocol</code>：结构化类型（3.8+）</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><span class="line"><span class="keyword">from</span> typing <span class="keyword">import</span> Protocol</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Quackable</span>(<span class="title class_ inherited__">Protocol</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;结构化类型：只要有 quack 方法的类型都算 Quackable。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">quack</span>(<span class="params">self</span>) -&gt; <span class="built_in">str</span>: ...</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Duck</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">quack</span>(<span class="params">self</span>) -&gt; <span class="built_in">str</span>:</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;嘎嘎嘎&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Toy</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">quack</span>(<span class="params">self</span>) -&gt; <span class="built_in">str</span>:</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;电子嘎嘎&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 不需要继承 Quackable，只要有 quack 方法就行</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">make_sound</span>(<span class="params">q: Quackable</span>) -&gt; <span class="literal">None</span>:</span><br><span class="line">    <span class="built_in">print</span>(q.quack())</span><br><span class="line"></span><br><span class="line">make_sound(Duck())  <span class="comment"># 嘎嘎嘎</span></span><br><span class="line">make_sound(Toy())   <span class="comment"># 电子嘎嘎</span></span><br></pre></td></tr></table></figure><blockquote><p><strong>总结</strong>：<code>Protocol</code> 结合了鸭子类型的灵活性和静态类型检查的安全性。在大型项目中推荐使用。</p></blockquote><hr><h2 id="5-魔术方法（协议）"><a href="#5-魔术方法（协议）" class="headerlink" title="5. 魔术方法（协议）"></a>5. 魔术方法（协议）</h2><p><strong>魔术方法全景图</strong>：</p><figure class="highlight plaintext"><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><span class="line">┌─────────────────────────────────────────────────────────────┐</span><br><span class="line">│                    Python 魔术方法分类                        │</span><br><span class="line">├──────────────┬──────────────────────────────────────────────┤</span><br><span class="line">│ 类别          │ 方法                                          │</span><br><span class="line">├──────────────┼──────────────────────────────────────────────┤</span><br><span class="line">│ 构造/销毁     │ __new__ / __init__ / __del__                 │</span><br><span class="line">│ 字符串表示    │ __str__ / __repr__ / __format__              │</span><br><span class="line">│ 容器协议      │ __len__ / __getitem__ / __setitem__ /        │</span><br><span class="line">│              │ __delitem__ / __contains__ / __iter__        │</span><br><span class="line">│ 比较运算      │ __eq__ / __ne__ / __lt__ / __le__ /          │</span><br><span class="line">│              │ __gt__ / __ge__                              │</span><br><span class="line">│ 算术运算      │ __add__ / __sub__ / __mul__ / __truediv__ /  │</span><br><span class="line">│              │ __floordiv__ / __mod__ / __pow__            │</span><br><span class="line">│ 反向算术      │ __radd__ / __rsub__ / __rmul__ 等            │</span><br><span class="line">│ 增量赋值      │ __iadd__ / __isub__ / __imul__ 等            │</span><br><span class="line">│ 一元运算      │ __neg__ / __pos__ / __abs__ / __invert__     │</span><br><span class="line">│ 类型转换      │ __int__ / __float__ / __bool__ / __str__     │</span><br><span class="line">│ 上下文管理    │ __enter__ / __exit__                        │</span><br><span class="line">│ 可调用        │ __call__                                     │</span><br><span class="line">│ 哈希          │ __hash__                                     │</span><br><span class="line">│ 属性访问      │ __getattr__ / __setattr__ / __getattribute__ │</span><br><span class="line">│ 描述符协议    │ __get__ / __set__ / __delete__               │</span><br><span class="line">│ 迭代器协议    │ __iter__ / __next__                          │</span><br><span class="line">│ 切片          │ __getitem__ (支持 slice 对象)                │</span><br><span class="line">└──────────────┴──────────────────────────────────────────────┘</span><br></pre></td></tr></table></figure><h3 id="5-1-表示方法：-str-与-repr"><a href="#5-1-表示方法：-str-与-repr" class="headerlink" title="5.1 表示方法：__str__ 与 __repr__"></a>5.1 表示方法：<code>__str__</code> 与 <code>__repr__</code></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><span class="line"><span class="keyword">class</span> <span class="title class_">Point</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, x, y</span>):</span><br><span class="line">        <span class="variable language_">self</span>.x = x</span><br><span class="line">        <span class="variable language_">self</span>.y = y</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__repr__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;面向开发者的表示：应尽量返回可执行的 Python 代码。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">return</span> <span class="string">f&quot;Point(x=<span class="subst">&#123;self.x&#125;</span>, y=<span class="subst">&#123;self.y&#125;</span>)&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__str__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;面向用户的表示：可读性优先。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">return</span> <span class="string">f&quot;(<span class="subst">&#123;self.x&#125;</span>, <span class="subst">&#123;self.y&#125;</span>)&quot;</span></span><br><span class="line"></span><br><span class="line">p = Point(<span class="number">3</span>, <span class="number">4</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">repr</span>(p))    <span class="comment"># Point(x=3, y=4)</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">str</span>(p))     <span class="comment"># (3, 4)</span></span><br><span class="line"><span class="built_in">print</span>(p)          <span class="comment"># (3, 4) —— print() 默认调用 __str__</span></span><br><span class="line"><span class="comment"># 在交互式环境中直接输入 p 会调用 __repr__</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 如果只实现 __repr__，__str__ 会回退到 __repr__</span></span><br></pre></td></tr></table></figure><p><strong><code>__str__</code> vs <code>__repr__</code> 调用时机</strong>：</p><figure class="highlight plaintext"><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><span class="line">┌──────────────────────────────────────────────────────┐</span><br><span class="line">│               __str__  vs  __repr__                  │</span><br><span class="line">├──────────────────┬───────────────────────────────────┤</span><br><span class="line">│ 调用场景          │ 使用的方法                        │</span><br><span class="line">├──────────────────┼───────────────────────────────────┤</span><br><span class="line">│ print(obj)       │ __str__（回退到 __repr__）         │</span><br><span class="line">│ str(obj)         │ __str__（回退到 __repr__）         │</span><br><span class="line">│ repr(obj)        │ __repr__                           │</span><br><span class="line">│ f&quot;&#123;obj&#125;&quot;         │ __str__（回退到 __repr__）         │</span><br><span class="line">│ f&quot;&#123;obj!r&#125;&quot;       │ __repr__                           │</span><br><span class="line">│ f&quot;&#123;obj!s&#125;&quot;       │ __str__                            │</span><br><span class="line">│ 交互式终端直接输入 │ __repr__                           │</span><br><span class="line">│ 列表中的元素      │ __repr__（不是 __str__！）         │</span><br><span class="line">│ 调试器显示        │ __repr__                           │</span><br><span class="line">└──────────────────┴───────────────────────────────────┘</span><br></pre></td></tr></table></figure><blockquote><p><strong>易错点</strong>：列表打印时，元素使用的是 <code>__repr__</code> 而非 <code>__str__</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></pre></td><td class="code"><pre><span class="line">points = [Point(<span class="number">1</span>, <span class="number">2</span>), Point(<span class="number">3</span>, <span class="number">4</span>)]</span><br><span class="line"><span class="built_in">print</span>(points)</span><br><span class="line"><span class="comment"># [Point(x=1, y=2), Point(x=3, y=4)]  ← 使用 __repr__</span></span><br><span class="line"><span class="comment"># 不是 [(1, 2), (3, 4)]</span></span><br></pre></td></tr></table></figure></blockquote><blockquote><p><strong>工程建议</strong>：至少实现 <code>__repr__</code>，因为：</p><ol><li>调试时能快速看到对象状态</li><li><code>__str__</code> 缺失时自动回退到 <code>__repr__</code></li><li><code>__repr__</code> 应返回可执行的代码（如 <code>Point(x=3, y=4)</code>），方便 <code>eval</code> 重建</li></ol></blockquote><h3 id="5-2-容器协议"><a href="#5-2-容器协议" class="headerlink" title="5.2 容器协议"></a>5.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><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><span class="line"><span class="keyword">class</span> <span class="title class_">Team</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;自定义序列：团队成员列表。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, name, members=<span class="literal">None</span></span>):</span><br><span class="line">        <span class="variable language_">self</span>.name = name</span><br><span class="line">        <span class="variable language_">self</span>._members = <span class="built_in">list</span>(members) <span class="keyword">if</span> members <span class="keyword">else</span> []</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__len__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;支持 len()&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">len</span>(<span class="variable language_">self</span>._members)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__getitem__</span>(<span class="params">self, index</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;支持索引访问 team[i]&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>._members[index]</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__setitem__</span>(<span class="params">self, index, value</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;支持索引赋值 team[i] = value&quot;&quot;&quot;</span></span><br><span class="line">        <span class="variable language_">self</span>._members[index] = value</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__delitem__</span>(<span class="params">self, index</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;支持 del team[i]&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">del</span> <span class="variable language_">self</span>._members[index]</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__contains__</span>(<span class="params">self, item</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;支持 in 操作符&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">return</span> item <span class="keyword">in</span> <span class="variable language_">self</span>._members</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__iter__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;支持 for member in team&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">iter</span>(<span class="variable language_">self</span>._members)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__repr__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="string">f&quot;Team(<span class="subst">&#123;self.name!r&#125;</span>, <span class="subst">&#123;self._members!r&#125;</span>)&quot;</span></span><br><span class="line"></span><br><span class="line">team = Team(<span class="string">&quot;开发组&quot;</span>, [<span class="string">&quot;Alice&quot;</span>, <span class="string">&quot;Bob&quot;</span>, <span class="string">&quot;Carol&quot;</span>])</span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">len</span>(team))              <span class="comment"># 3</span></span><br><span class="line"><span class="built_in">print</span>(team[<span class="number">0</span>])                <span class="comment"># Alice</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;Bob&quot;</span> <span class="keyword">in</span> team)          <span class="comment"># True</span></span><br><span class="line"><span class="keyword">for</span> member <span class="keyword">in</span> team:</span><br><span class="line">    <span class="built_in">print</span>(member, end=<span class="string">&quot; &quot;</span>)    <span class="comment"># Alice Bob Carol</span></span><br></pre></td></tr></table></figure><p><strong>容器协议方法与内置函数的映射</strong>：</p><figure class="highlight plaintext"><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><span class="line">┌──────────────────┬─────────────────────────────────┐</span><br><span class="line">│ 魔术方法          │ 触发的内置操作                    │</span><br><span class="line">├──────────────────┼─────────────────────────────────┤</span><br><span class="line">│ __len__          │ len(obj)                        │</span><br><span class="line">│ __getitem__      │ obj[key], obj[i], obj[a:b]     │</span><br><span class="line">│ __setitem__      │ obj[key] = value               │</span><br><span class="line">│ __delitem__      │ del obj[key]                   │</span><br><span class="line">│ __contains__     │ x in obj                       │</span><br><span class="line">│ __iter__         │ for x in obj, iter(obj)        │</span><br><span class="line">│ __next__         │ next(obj)                      │</span><br><span class="line">│ __reversed__     │ reversed(obj)                  │</span><br><span class="line">│ __missing__      │ dict 子类的 obj[missing_key]   │</span><br><span class="line">└──────────────────┴─────────────────────────────────┘</span><br></pre></td></tr></table></figure><h3 id="5-3-上下文管理器协议"><a href="#5-3-上下文管理器协议" class="headerlink" title="5.3 上下文管理器协议"></a>5.3 上下文管理器协议</h3><p><strong><code>with</code> 语句的执行流程</strong>：</p><figure class="highlight plaintext"><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><span class="line">with resource as r:</span><br><span class="line">    # 代码块</span><br><span class="line">    pass</span><br><span class="line"></span><br><span class="line">执行流程：</span><br><span class="line">  ┌────────────────────────────────────────────────┐</span><br><span class="line">  │ Step 1: 调用 resource.__enter__()              │</span><br><span class="line">  │          返回值赋给 r                           │</span><br><span class="line">  ├────────────────────────────────────────────────┤</span><br><span class="line">  │ Step 2: 执行 with 代码块                        │</span><br><span class="line">  │          ├── 正常结束 ──→ Step 3                │</span><br><span class="line">  │          └── 抛出异常 ──→ Step 3（异常传入参数） │</span><br><span class="line">  ├────────────────────────────────────────────────┤</span><br><span class="line">  │ Step 3: 调用 resource.__exit__(exc_type,        │</span><br><span class="line">  │          exc_val, exc_tb)                      │</span><br><span class="line">  │          ├── 返回 False/None → 异常继续传播      │</span><br><span class="line">  │          └── 返回 True → 异常被抑制（慎用！）      │</span><br><span class="line">  └────────────────────────────────────────────────┘</span><br></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><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><span class="line"><span class="keyword">class</span> <span class="title class_">DatabaseConnection</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;自定义上下文管理器 —— 替代 with 语句中的 try/finally。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, host, port</span>):</span><br><span class="line">        <span class="variable language_">self</span>.host = host</span><br><span class="line">        <span class="variable language_">self</span>.port = port</span><br><span class="line">        <span class="variable language_">self</span>.connected = <span class="literal">False</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__enter__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;进入 with 块时调用，返回值赋给 as 变量。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;连接数据库 <span class="subst">&#123;self.host&#125;</span>:<span class="subst">&#123;self.port&#125;</span>&quot;</span>)</span><br><span class="line">        <span class="variable language_">self</span>.connected = <span class="literal">True</span></span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>    <span class="comment"># 返回给 as 变量</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__exit__</span>(<span class="params">self, exc_type, exc_val, exc_tb</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;退出 with 块时调用（即使发生异常也会执行）。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;关闭数据库连接&quot;</span>)</span><br><span class="line">        <span class="variable language_">self</span>.connected = <span class="literal">False</span></span><br><span class="line">        <span class="comment"># 返回 True 会抑制异常，返回 False/None 则继续传播异常</span></span><br><span class="line">        <span class="keyword">return</span> <span class="literal">False</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">query</span>(<span class="params">self, sql</span>):</span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> <span class="variable language_">self</span>.connected:</span><br><span class="line">            <span class="keyword">raise</span> RuntimeError(<span class="string">&quot;未连接&quot;</span>)</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;执行: <span class="subst">&#123;sql&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用</span></span><br><span class="line"><span class="keyword">with</span> DatabaseConnection(<span class="string">&quot;localhost&quot;</span>, <span class="number">3306</span>) <span class="keyword">as</span> db:</span><br><span class="line">    db.query(<span class="string">&quot;SELECT * FROM users&quot;</span>)</span><br><span class="line"><span class="comment"># 连接数据库 localhost:3306</span></span><br><span class="line"><span class="comment"># 执行: SELECT * FROM users</span></span><br><span class="line"><span class="comment"># 关闭数据库连接</span></span><br></pre></td></tr></table></figure><p><strong>使用 <code>contextlib</code> 简化上下文管理器</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><span class="line"><span class="keyword">from</span> contextlib <span class="keyword">import</span> contextmanager</span><br><span class="line"></span><br><span class="line"><span class="meta">@contextmanager</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">open_file</span>(<span class="params">filename, mode=<span class="string">&#x27;r&#x27;</span></span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;用生成器实现上下文管理器（更简洁）。&quot;&quot;&quot;</span></span><br><span class="line">    f = <span class="built_in">open</span>(filename, mode)</span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        <span class="keyword">yield</span> f          <span class="comment"># yield 之前 = __enter__</span></span><br><span class="line">    <span class="keyword">finally</span>:</span><br><span class="line">        f.close()        <span class="comment"># yield 之后 = __exit__</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">with</span> open_file(<span class="string">&#x27;test.txt&#x27;</span>, <span class="string">&#x27;w&#x27;</span>) <span class="keyword">as</span> f:</span><br><span class="line">    f.write(<span class="string">&#x27;Hello&#x27;</span>)</span><br></pre></td></tr></table></figure><blockquote><p><strong>工程建议</strong>：需要资源管理（文件、数据库连接、锁）时，优先使用上下文管理器。简单场景用 <code>@contextmanager</code>，复杂场景用 <code>__enter__</code>&#x2F;<code>__exit__</code>。</p></blockquote><h3 id="5-4-可调用对象：-call"><a href="#5-4-可调用对象：-call" class="headerlink" title="5.4 可调用对象：__call__"></a>5.4 可调用对象：<code>__call__</code></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><span class="line"><span class="keyword">class</span> <span class="title class_">LRUCache</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;将函数调用缓存为可调用对象。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, func</span>):</span><br><span class="line">        <span class="variable language_">self</span>.func = func</span><br><span class="line">        <span class="variable language_">self</span>.cache = &#123;&#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__call__</span>(<span class="params">self, *args</span>):</span><br><span class="line">        <span class="keyword">if</span> args <span class="keyword">not</span> <span class="keyword">in</span> <span class="variable language_">self</span>.cache:</span><br><span class="line">            <span class="variable language_">self</span>.cache[args] = <span class="variable language_">self</span>.func(*args)</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>.cache[args]</span><br><span class="line"></span><br><span class="line"><span class="meta">@LRUCache</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">fibonacci</span>(<span class="params">n</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;计算斐波那契数列。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">if</span> n &lt; <span class="number">2</span>:</span><br><span class="line">        <span class="keyword">return</span> n</span><br><span class="line">    <span class="keyword">return</span> fibonacci(n - <span class="number">1</span>) + fibonacci(n - <span class="number">2</span>)</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(fibonacci(<span class="number">100</span>))    <span class="comment"># 354224848179261915075 —— 有缓存，瞬间完成</span></span><br></pre></td></tr></table></figure><p><strong><code>__call__</code> 的常见用途</strong>：</p><figure class="highlight plaintext"><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><span class="line">┌──────────────────────┬──────────────────────────────────┐</span><br><span class="line">│ 用途                  │ 示例                              │</span><br><span class="line">├──────────────────────┼──────────────────────────────────┤</span><br><span class="line">│ 装饰器                │ class Timer: __call__(self, ...) │</span><br><span class="line">│ 策略对象              │ class Strategy: __call__(...)    │</span><br><span class="line">│ 状态机                │ class State: __call__(...)       │</span><br><span class="line">│ API 端点（Flask 等）  │ app.route(&#x27;/&#x27;) → callable         │</span><br><span class="line">│ 配置对象              │ class Config: __call__(...)      │</span><br><span class="line">└──────────────────────┴──────────────────────────────────┘</span><br></pre></td></tr></table></figure><blockquote><p><strong>面试真题</strong>：<code>obj()</code> 和 <code>obj.__call__()</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">A</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__call__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;called&quot;</span></span><br><span class="line"></span><br><span class="line">a = A()</span><br><span class="line"><span class="built_in">print</span>(a())          <span class="comment"># &quot;called&quot;  —— 语法糖</span></span><br><span class="line"><span class="built_in">print</span>(a.__call__()) <span class="comment"># &quot;called&quot;  —— 直接调用</span></span><br><span class="line"><span class="comment"># 两者等价，但推荐 a()，更简洁可读</span></span><br></pre></td></tr></table></figure></blockquote><h3 id="5-5-运算符重载"><a href="#5-5-运算符重载" class="headerlink" title="5.5 运算符重载"></a>5.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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Vector</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, x, y</span>):</span><br><span class="line">        <span class="variable language_">self</span>.x = x</span><br><span class="line">        <span class="variable language_">self</span>.y = y</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__add__</span>(<span class="params">self, other</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;支持 v1 + v2&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">if</span> <span class="built_in">isinstance</span>(other, Vector):</span><br><span class="line">            <span class="keyword">return</span> Vector(<span class="variable language_">self</span>.x + other.x, <span class="variable language_">self</span>.y + other.y)</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">NotImplemented</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__sub__</span>(<span class="params">self, other</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;支持 v1 - v2&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">if</span> <span class="built_in">isinstance</span>(other, Vector):</span><br><span class="line">            <span class="keyword">return</span> Vector(<span class="variable language_">self</span>.x - other.x, <span class="variable language_">self</span>.y - other.y)</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">NotImplemented</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__mul__</span>(<span class="params">self, scalar</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;支持 v * 3&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">if</span> <span class="built_in">isinstance</span>(scalar, (<span class="built_in">int</span>, <span class="built_in">float</span>)):</span><br><span class="line">            <span class="keyword">return</span> Vector(<span class="variable language_">self</span>.x * scalar, <span class="variable language_">self</span>.y * scalar)</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">NotImplemented</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__rmul__</span>(<span class="params">self, scalar</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;支持 3 * v（右侧乘法）&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>.__mul__(scalar)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__neg__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;支持 -v&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">return</span> Vector(-<span class="variable language_">self</span>.x, -<span class="variable language_">self</span>.y)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__abs__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;支持 abs(v)&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">return</span> (<span class="variable language_">self</span>.x ** <span class="number">2</span> + <span class="variable language_">self</span>.y ** <span class="number">2</span>) ** <span class="number">0.5</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__eq__</span>(<span class="params">self, other</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;支持 v1 == v2&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">if</span> <span class="built_in">isinstance</span>(other, Vector):</span><br><span class="line">            <span class="keyword">return</span> <span class="variable language_">self</span>.x == other.x <span class="keyword">and</span> <span class="variable language_">self</span>.y == other.y</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">NotImplemented</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__bool__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;支持 bool(v)&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>.x != <span class="number">0</span> <span class="keyword">or</span> <span class="variable language_">self</span>.y != <span class="number">0</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__repr__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="string">f&quot;Vector(<span class="subst">&#123;self.x&#125;</span>, <span class="subst">&#123;self.y&#125;</span>)&quot;</span></span><br><span class="line"></span><br><span class="line">v1 = Vector(<span class="number">3</span>, <span class="number">4</span>)</span><br><span class="line">v2 = Vector(<span class="number">1</span>, <span class="number">2</span>)</span><br><span class="line"><span class="built_in">print</span>(v1 + v2)      <span class="comment"># Vector(4, 6)</span></span><br><span class="line"><span class="built_in">print</span>(v1 - v2)      <span class="comment"># Vector(2, 2)</span></span><br><span class="line"><span class="built_in">print</span>(v1 * <span class="number">3</span>)       <span class="comment"># Vector(9, 12)</span></span><br><span class="line"><span class="built_in">print</span>(<span class="number">3</span> * v1)       <span class="comment"># Vector(9, 12)</span></span><br><span class="line"><span class="built_in">print</span>(-v1)          <span class="comment"># Vector(-3, -4)</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">abs</span>(v1))      <span class="comment"># 5.0</span></span><br><span class="line"><span class="built_in">print</span>(v1 == Vector(<span class="number">3</span>, <span class="number">4</span>))   <span class="comment"># True</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">bool</span>(Vector(<span class="number">0</span>, <span class="number">0</span>)))   <span class="comment"># False</span></span><br></pre></td></tr></table></figure><p><strong>运算符查找顺序</strong>：</p><figure class="highlight plaintext"><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><span class="line">v1 + v2 的查找过程：</span><br><span class="line"></span><br><span class="line">  1. 调用 v1.__add__(v2)</span><br><span class="line">     ├── 返回具体值 → 使用结果</span><br><span class="line">     ├── 返回 NotImplemented → 继续步骤 2</span><br><span class="line">     └── 返回其他值 → 使用结果</span><br><span class="line"></span><br><span class="line">  2. 调用 v2.__radd__(v1)  ← 反向运算符</span><br><span class="line">     ├── 返回具体值 → 使用结果</span><br><span class="line">     └── 返回 NotImplemented → TypeError</span><br><span class="line"></span><br><span class="line">  3 * v1 的查找过程：</span><br><span class="line">  1. int 没有 __mul__(Vector) → 返回 NotImplemented</span><br><span class="line">  2. 调用 v1.__rmul__(3) → 结果</span><br></pre></td></tr></table></figure><blockquote><p><strong>工程建议</strong>：</p><ol><li>运算符重载方法遇到不支持的类型时，返回 <code>NotImplemented</code>（不是 <code>TypeError</code>），让 Python 尝试反向运算符</li><li>实现 <code>__eq__</code> 时，也要考虑 <code>__hash__</code>（见 5.6）</li><li>实现 <code>__lt__</code> 等比较运算符后，可以用 <code>@functools.total_ordering</code> 自动补全其他比较方法</li></ol></blockquote><p><strong><code>@functools.total_ordering</code> 自动补全比较方法</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> functools <span class="keyword">import</span> total_ordering</span><br><span class="line"></span><br><span class="line"><span class="meta">@total_ordering</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Student</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, name, grade</span>):</span><br><span class="line">        <span class="variable language_">self</span>.name = name</span><br><span class="line">        <span class="variable language_">self</span>.grade = grade</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__eq__</span>(<span class="params">self, other</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>.grade == other.grade</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__lt__</span>(<span class="params">self, other</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>.grade &lt; other.grade</span><br><span class="line">    <span class="comment"># total_ordering 自动生成 __le__、__gt__、__ge__！</span></span><br><span class="line"></span><br><span class="line">s1 = Student(<span class="string">&quot;Alice&quot;</span>, <span class="number">90</span>)</span><br><span class="line">s2 = Student(<span class="string">&quot;Bob&quot;</span>, <span class="number">85</span>)</span><br><span class="line"><span class="built_in">print</span>(s1 &gt; s2)   <span class="comment"># True（自动生成）</span></span><br><span class="line"><span class="built_in">print</span>(s1 &gt;= s2)  <span class="comment"># True（自动生成）</span></span><br></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><code>__init__</code></td><td><code>obj = Class()</code></td></tr><tr><td>表示</td><td><code>__str__</code> &#x2F; <code>__repr__</code></td><td><code>str(obj)</code> &#x2F; <code>repr(obj)</code></td></tr><tr><td>长度</td><td><code>__len__</code></td><td><code>len(obj)</code></td></tr><tr><td>索引</td><td><code>__getitem__</code> &#x2F; <code>__setitem__</code></td><td><code>obj[key]</code> &#x2F; <code>obj[key] = v</code></td></tr><tr><td>迭代</td><td><code>__iter__</code> &#x2F; <code>__next__</code></td><td><code>for x in obj</code></td></tr><tr><td>包含</td><td><code>__contains__</code></td><td><code>x in obj</code></td></tr><tr><td>上下文</td><td><code>__enter__</code> &#x2F; <code>__exit__</code></td><td><code>with obj as x:</code></td></tr><tr><td>可调用</td><td><code>__call__</code></td><td><code>obj()</code></td></tr><tr><td>比较</td><td><code>__eq__</code> &#x2F; <code>__lt__</code> &#x2F; <code>__gt__</code> 等</td><td><code>==</code> &#x2F; <code>&lt;</code> &#x2F; <code>&gt;</code></td></tr><tr><td>哈希</td><td><code>__hash__</code></td><td><code>hash(obj)</code> &#x2F; <code>set()</code> 成员</td></tr><tr><td>布尔</td><td><code>__bool__</code></td><td><code>bool(obj)</code> &#x2F; <code>if obj:</code></td></tr><tr><td>算术</td><td><code>__add__</code> &#x2F; <code>__sub__</code> &#x2F; <code>__mul__</code> 等</td><td><code>+</code> &#x2F; <code>-</code> &#x2F; <code>*</code></td></tr><tr><td>反向算术</td><td><code>__radd__</code> &#x2F; <code>__rmul__</code> 等</td><td><code>其他类型 + obj</code></td></tr><tr><td>增量赋值</td><td><code>__iadd__</code> &#x2F; <code>__isub__</code> 等</td><td><code>+=</code> &#x2F; <code>-=</code></td></tr><tr><td>一元</td><td><code>__neg__</code> &#x2F; <code>__pos__</code> &#x2F; <code>__abs__</code></td><td><code>-obj</code> &#x2F; <code>+obj</code> &#x2F; <code>abs(obj)</code></td></tr><tr><td>切片</td><td><code>__getitem__</code>(slice)</td><td><code>obj[a:b]</code></td></tr><tr><td>格式化</td><td><code>__format__</code></td><td><code>f&quot;{obj:spec}&quot;</code></td></tr></tbody></table><h3 id="5-6-hash-与-eq-组合（面试高频）"><a href="#5-6-hash-与-eq-组合（面试高频）" class="headerlink" title="5.6 __hash__ 与 __eq__ 组合（面试高频）"></a>5.6 <code>__hash__</code> 与 <code>__eq__</code> 组合（面试高频）</h3><p><code>__hash__</code> 和 <code>__eq__</code> 共同决定了对象在字典、集合等哈希集合中的行为。</p><p><strong>哈希表工作原理</strong>：</p><figure class="highlight plaintext"><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><span class="line">┌─────────────────────────────────────────────────────────────┐</span><br><span class="line">│                   哈希表（dict / set）                        │</span><br><span class="line">├─────────────────────────────────────────────────────────────┤</span><br><span class="line">│                                                             │</span><br><span class="line">│  插入对象 obj 时：                                            │</span><br><span class="line">│    1. 计算 hash(obj) → 得到哈希值 h                           │</span><br><span class="line">│    2. h % 桶大小 → 确定桶位置                                 │</span><br><span class="line">│    3. 桶中已有元素？用 __eq__ 检查是否相等                    │</span><br><span class="line">│       ├── 相等 → 不重复插入（覆盖值）                         │</span><br><span class="line">│       └── 不相等 → 哈希碰撞，放到下一个位置                    │</span><br><span class="line">│                                                             │</span><br><span class="line">│  核心约定（必须遵守）：                                       │</span><br><span class="line">│    1. a == b → hash(a) == hash(b)  ✓ 必须成立                │</span><br><span class="line">│    2. hash(a) == hash(b) → a 不一定 == b（哈希碰撞可能）      │</span><br><span class="line">│    3. 实现了 __eq__ → __hash__ 自动设为 None（不可哈希！）     │</span><br><span class="line">│                                                             │</span><br><span class="line">│  示意图：                                                    │</span><br><span class="line">│    Bucket 0: [obj1]                                         │</span><br><span class="line">│    Bucket 1: [obj2 → obj3]  ← 哈希碰撞，链表存储              │</span><br><span class="line">│    Bucket 2: []                                              │</span><br><span class="line">│    Bucket 3: [obj4]                                         │</span><br><span class="line">│                                                             │</span><br><span class="line">└─────────────────────────────────────────────────────────────┘</span><br></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><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 默认行为：对象基于 id 比较和哈希</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">DefaultObj</span>:</span><br><span class="line">    <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line">a = DefaultObj()</span><br><span class="line">b = DefaultObj()</span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">hash</span>(a))            <span class="comment"># 不同</span></span><br><span class="line"><span class="built_in">print</span>(a == b)             <span class="comment"># False（默认比较 identity）</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 必须遵守的约定（面试核心考点）：</span></span><br><span class="line"><span class="comment"># 1. 如果 a == b，则 hash(a) == hash(b) —— 双向必须一致</span></span><br><span class="line"><span class="comment"># 2. 如果 hash(a) == hash(b)，a 不一定等于 b（哈希碰撞，可能）</span></span><br><span class="line"><span class="comment"># 3. 如果实现了 __eq__，__hash__ 会被设为 None（对象不可哈希）</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 常见陷阱：实现了 __eq__ 但未实现 __hash__</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Person</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, name</span>):</span><br><span class="line">        <span class="variable language_">self</span>.name = name</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__eq__</span>(<span class="params">self, other</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>.name == other.name</span><br><span class="line">    <span class="comment"># __hash__ 被设为 None —— 不可哈希！</span></span><br><span class="line">    <span class="comment"># 如果想放入集合，必须同时实现 __hash__</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 正确实现</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Person</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, name</span>):</span><br><span class="line">        <span class="variable language_">self</span>.name = name</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__eq__</span>(<span class="params">self, other</span>):</span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> <span class="built_in">isinstance</span>(other, Person):</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">NotImplemented</span></span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>.name == other.name</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__hash__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">hash</span>(<span class="variable language_">self</span>.name)   <span class="comment"># 使用不可变字段计算哈希</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 现在可以放入集合/字典</span></span><br><span class="line">p1 = Person(<span class="string">&quot;Alice&quot;</span>)</span><br><span class="line">p2 = Person(<span class="string">&quot;Alice&quot;</span>)</span><br><span class="line">people = &#123;p1, p2&#125;          <span class="comment"># 只包含一个</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">len</span>(people))         <span class="comment"># 1</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 不可变对象才可哈希：tuple 哈希 ≈ 组合所有元素的哈希</span></span><br><span class="line"><span class="comment"># 可变对象不能哈希：list 的 __hash__ = None</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 工程最佳实践：</span></span><br><span class="line"><span class="comment"># - 如果 Person 在业务上是&quot;同 name 即同一个人&quot; → 实现 __eq__ + __hash__</span></span><br><span class="line"><span class="comment"># - 如果 Person 需要独立身份（即使同名也视为不同人）→ 不实现任何方法</span></span><br><span class="line"><span class="comment"># - 使用不可变字段（str、int、tuple）作为哈希依据</span></span><br><span class="line"><span class="comment"># - 不要用可变字段（list、dict）参与哈希计算</span></span><br></pre></td></tr></table></figure><blockquote><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></pre></td><td class="code"><pre><span class="line">p = Person(<span class="string">&quot;Alice&quot;</span>)</span><br><span class="line">s = &#123;p&#125;</span><br><span class="line">p.name = <span class="string">&quot;Bob&quot;</span>       <span class="comment"># 修改了参与哈希的属性</span></span><br><span class="line"><span class="built_in">print</span>(p <span class="keyword">in</span> s)         <span class="comment"># False！因为哈希值变了，找不到原来的桶</span></span><br></pre></td></tr></table></figure></blockquote><hr><h2 id="6-元编程与高级-OOP"><a href="#6-元编程与高级-OOP" class="headerlink" title="6. 元编程与高级 OOP"></a>6. 元编程与高级 OOP</h2><h3 id="6-1-元类（Metaclass）"><a href="#6-1-元类（Metaclass）" class="headerlink" title="6.1 元类（Metaclass）"></a>6.1 元类（Metaclass）</h3><p><strong>元类层级关系</strong>：</p><figure class="highlight plaintext"><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><span class="line">┌──────────────────────────────────────────────┐</span><br><span class="line">│                                              │</span><br><span class="line">│  type（元类）                                 │</span><br><span class="line">│    │                                         │</span><br><span class="line">│    ├──→ Person（普通类）                      │</span><br><span class="line">│    │      │                                  │</span><br><span class="line">│    │      ├──→ alice = Person()（实例）       │</span><br><span class="line">│    │      └──→ bob = Person()（实例）         │</span><br><span class="line">│    │                                         │</span><br><span class="line">│    ├──→ SingletonMeta（自定义元类）            │</span><br><span class="line">│    │      │                                  │</span><br><span class="line">│    │      └──→ Database（用元类创建的类）      │</span><br><span class="line">│    │             │                           │</span><br><span class="line">│    │             └──→ db = Database()（实例）  │</span><br><span class="line">│    │                                         │</span><br><span class="line">│    └──→ type 自己也是 type 的实例              │</span><br><span class="line">│                                              │</span><br><span class="line">│  理解：&quot;类也是对象&quot;，type 是创建类的类           │</span><br><span class="line">│                                              │</span><br><span class="line">└──────────────────────────────────────────────┘</span><br></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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 类的本质：type 是默认的元类</span></span><br><span class="line">MyClass = <span class="built_in">type</span>(<span class="string">&quot;MyClass&quot;</span>, (<span class="built_in">object</span>,), &#123;<span class="string">&quot;x&quot;</span>: <span class="number">5</span>, <span class="string">&quot;hello&quot;</span>: <span class="keyword">lambda</span> <span class="variable language_">self</span>: <span class="string">&quot;Hello&quot;</span>&#125;)</span><br><span class="line">obj = MyClass()</span><br><span class="line"><span class="built_in">print</span>(obj.x)        <span class="comment"># 5</span></span><br><span class="line"><span class="built_in">print</span>(obj.hello())  <span class="comment"># Hello</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 以上等价于：</span></span><br><span class="line"><span class="comment"># class MyClass:</span></span><br><span class="line"><span class="comment">#     x = 5</span></span><br><span class="line"><span class="comment">#     def hello(self):</span></span><br><span class="line"><span class="comment">#         return &quot;Hello&quot;</span></span><br></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><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><span class="line"><span class="keyword">class</span> <span class="title class_">SingletonMeta</span>(<span class="title class_ inherited__">type</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;单例元类：任何使用此元类的类，自动成为单例。&quot;&quot;&quot;</span></span><br><span class="line">    _instances = &#123;&#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__call__</span>(<span class="params">cls, *args, **kwargs</span>):</span><br><span class="line">        <span class="keyword">if</span> cls <span class="keyword">not</span> <span class="keyword">in</span> cls._instances:</span><br><span class="line">            cls._instances[cls] = <span class="built_in">super</span>().__call__(*args, **kwargs)</span><br><span class="line">        <span class="keyword">return</span> cls._instances[cls]</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Database</span>(metaclass=SingletonMeta):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, host=<span class="string">&quot;localhost&quot;</span></span>):</span><br><span class="line">        <span class="variable language_">self</span>.host = host</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;初始化 Database: <span class="subst">&#123;self.host&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line">db1 = Database(<span class="string">&quot;prod-server&quot;</span>)</span><br><span class="line">db2 = Database(<span class="string">&quot;test-server&quot;</span>)   <span class="comment"># 不会重新初始化</span></span><br><span class="line"><span class="built_in">print</span>(db1 <span class="keyword">is</span> db2)                <span class="comment"># True</span></span><br><span class="line"><span class="built_in">print</span>(db1.host)                  <span class="comment"># prod-server —— 第一次的值</span></span><br></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><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><span class="line"><span class="keyword">class</span> <span class="title class_">PluginRegistry</span>(<span class="title class_ inherited__">type</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;自动注册所有子类的元类。&quot;&quot;&quot;</span></span><br><span class="line">    registry = &#123;&#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__new__</span>(<span class="params">cls, name, bases, namespace</span>):</span><br><span class="line">        new_cls = <span class="built_in">super</span>().__new__(cls, name, bases, namespace)</span><br><span class="line">        <span class="keyword">if</span> name != <span class="string">&quot;Plugin&quot;</span>:</span><br><span class="line">            cls.registry[name] = new_cls</span><br><span class="line">        <span class="keyword">return</span> new_cls</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Plugin</span>(metaclass=PluginRegistry):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;插件基类。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">ImagePlugin</span>(<span class="title class_ inherited__">Plugin</span>):</span><br><span class="line">    <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">VideoPlugin</span>(<span class="title class_ inherited__">Plugin</span>):</span><br><span class="line">    <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">AudioPlugin</span>(<span class="title class_ inherited__">Plugin</span>):</span><br><span class="line">    <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(PluginRegistry.registry)</span><br><span class="line"><span class="comment"># &#123;&#x27;ImagePlugin&#x27;: &lt;class &#x27;ImagePlugin&#x27;&gt;, &#x27;VideoPlugin&#x27;: &lt;class &#x27;VideoPlugin&#x27;&gt;, &#x27;AudioPlugin&#x27;: &lt;class &#x27;AudioPlugin&#x27;&gt;&#125;</span></span><br></pre></td></tr></table></figure><p><strong><code>__init_subclass__</code> 替代元类（3.6+）</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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 3.6+ 的 __init_subclass__ 常可替代元类，更简单</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">PluginBase</span>:</span><br><span class="line">    _registry = &#123;&#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init_subclass__</span>(<span class="params">cls, **kwargs</span>):</span><br><span class="line">        <span class="built_in">super</span>().__init_subclass__(**kwargs)</span><br><span class="line">        <span class="keyword">if</span> cls.__name__ != <span class="string">&quot;PluginBase&quot;</span>:</span><br><span class="line">            PluginBase._registry[cls.__name__] = cls</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">ImagePlugin</span>(<span class="title class_ inherited__">PluginBase</span>):</span><br><span class="line">    <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">VideoPlugin</span>(<span class="title class_ inherited__">PluginBase</span>):</span><br><span class="line">    <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(PluginBase._registry)</span><br><span class="line"><span class="comment"># &#123;&#x27;ImagePlugin&#x27;: &lt;class &#x27;ImagePlugin&#x27;&gt;, &#x27;VideoPlugin&#x27;: &lt;class &#x27;VideoPlugin&#x27;&gt;&#125;</span></span><br></pre></td></tr></table></figure><blockquote><p><strong>工程建议</strong>：元类是 Python 中最强大的特性之一，也是最容易被滥用的。优先级：</p><ol><li>能用普通类解决 → 不用元类</li><li>能用 <code>@property</code> &#x2F; 描述符 → 不用元类</li><li>能用类装饰器 → 不用元类</li><li>能用 <code>__init_subclass__</code> → 不用元类</li><li>以上都不行 → 才考虑元类</li></ol></blockquote><h3 id="6-2-描述符协议"><a href="#6-2-描述符协议" class="headerlink" title="6.2 描述符协议"></a>6.2 描述符协议</h3><p><strong>描述符是 <code>@property</code> 的底层实现</strong>：</p><figure class="highlight plaintext"><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><span class="line">┌────────────────────────────────────────────────────────────┐</span><br><span class="line">│                    描述符的工作原理                          │</span><br><span class="line">├────────────────────────────────────────────────────────────┤</span><br><span class="line">│                                                            │</span><br><span class="line">│  当你访问 obj.attr 时：                                     │</span><br><span class="line">│                                                            │</span><br><span class="line">│  1. Python 检查 type(obj) 的 __dict__                      │</span><br><span class="line">│  2. 如果 attr 是描述符（有 __get__ 方法）                    │</span><br><span class="line">│     → 调用 descriptor.__get__(obj, type(obj))              │</span><br><span class="line">│                                                            │</span><br><span class="line">│  三种描述符：                                               │</span><br><span class="line">│  ├── 数据描述符：有 __get__ + __set__（如 @property）       │</span><br><span class="line">│  ├── 非数据描述符：只有 __get__（如普通方法）                 │</span><br><span class="line">│  └── 查找优先级：数据描述符 &gt; 实例属性 &gt; 非数据描述符         │</span><br><span class="line">│                                                            │</span><br><span class="line">│  @property 就是数据描述符的语法糖                            │</span><br><span class="line">│                                                            │</span><br><span class="line">└────────────────────────────────────────────────────────────┘</span><br></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><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><span class="line"><span class="keyword">class</span> <span class="title class_">ValidatedField</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;带类型验证的描述符。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, field_type, min_value=<span class="literal">None</span>, max_value=<span class="literal">None</span></span>):</span><br><span class="line">        <span class="variable language_">self</span>.field_type = field_type</span><br><span class="line">        <span class="variable language_">self</span>.min_value = min_value</span><br><span class="line">        <span class="variable language_">self</span>.max_value = max_value</span><br><span class="line">        <span class="variable language_">self</span>.data = &#123;&#125;          <span class="comment"># 使用字典存储每个实例的值</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__get__</span>(<span class="params">self, instance, owner</span>):</span><br><span class="line">        <span class="keyword">if</span> instance <span class="keyword">is</span> <span class="literal">None</span>:</span><br><span class="line">            <span class="keyword">return</span> <span class="variable language_">self</span>         <span class="comment"># 类属性访问时返回描述符本身</span></span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>.data.get(instance, <span class="literal">None</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__set__</span>(<span class="params">self, instance, value</span>):</span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> <span class="built_in">isinstance</span>(value, <span class="variable language_">self</span>.field_type):</span><br><span class="line">            <span class="keyword">raise</span> TypeError(<span class="string">f&quot;期望 <span class="subst">&#123;self.field_type.__name__&#125;</span>，实际 <span class="subst">&#123;<span class="built_in">type</span>(value).__name__&#125;</span>&quot;</span>)</span><br><span class="line">        <span class="keyword">if</span> <span class="variable language_">self</span>.min_value <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">None</span> <span class="keyword">and</span> value &lt; <span class="variable language_">self</span>.min_value:</span><br><span class="line">            <span class="keyword">raise</span> ValueError(<span class="string">f&quot;值不能小于 <span class="subst">&#123;self.min_value&#125;</span>&quot;</span>)</span><br><span class="line">        <span class="keyword">if</span> <span class="variable language_">self</span>.max_value <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">None</span> <span class="keyword">and</span> value &gt; <span class="variable language_">self</span>.max_value:</span><br><span class="line">            <span class="keyword">raise</span> ValueError(<span class="string">f&quot;值不能大于 <span class="subst">&#123;self.max_value&#125;</span>&quot;</span>)</span><br><span class="line">        <span class="variable language_">self</span>.data[instance] = value</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__delete__</span>(<span class="params">self, instance</span>):</span><br><span class="line">        <span class="keyword">del</span> <span class="variable language_">self</span>.data[instance]</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Person</span>:</span><br><span class="line">    name = ValidatedField(<span class="built_in">str</span>)             <span class="comment"># 必须为字符串</span></span><br><span class="line">    age = ValidatedField(<span class="built_in">int</span>, <span class="number">0</span>, <span class="number">150</span>)       <span class="comment"># 0-150 的整数</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, name, age</span>):</span><br><span class="line">        <span class="variable language_">self</span>.name = name</span><br><span class="line">        <span class="variable language_">self</span>.age = age</span><br><span class="line"></span><br><span class="line">p = Person(<span class="string">&quot;Alice&quot;</span>, <span class="number">30</span>)</span><br><span class="line">p.age = <span class="number">31</span></span><br><span class="line"><span class="comment"># p.age = &quot;abc&quot;     # TypeError</span></span><br><span class="line"><span class="comment"># p.age = -1        # ValueError</span></span><br><span class="line"><span class="comment"># p.age = 200       # ValueError</span></span><br></pre></td></tr></table></figure><blockquote><p><strong>面试真题</strong>：<code>@property</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></pre></td><td class="code"><pre><span class="line"><span class="comment"># @property 本质上就是创建了一个数据描述符</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">C</span>:</span><br><span class="line"><span class="meta">    @property</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">x</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>._x</span><br><span class="line"></span><br><span class="line"><span class="meta">    @x.setter</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">x</span>(<span class="params">self, value</span>):</span><br><span class="line">        <span class="variable language_">self</span>._x = value</span><br><span class="line"></span><br><span class="line"><span class="comment"># 等价于：</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Property</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__get__</span>(<span class="params">self, obj, owner</span>):</span><br><span class="line">        <span class="keyword">return</span> obj._x</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__set__</span>(<span class="params">self, obj, value</span>):</span><br><span class="line">        obj._x = value</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">C</span>:</span><br><span class="line">    x = Property()</span><br></pre></td></tr></table></figure></blockquote><h3 id="6-3-数据类-dataclass（3-7-）"><a href="#6-3-数据类-dataclass（3-7-）" class="headerlink" title="6.3 数据类 dataclass（3.7+）"></a>6.3 数据类 <code>dataclass</code>（3.7+）</h3><p><code>dataclass</code> 自动生成 <code>__init__</code>、<code>__repr__</code>、<code>__eq__</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> dataclasses <span class="keyword">import</span> dataclass, field, asdict, astuple</span><br><span class="line"></span><br><span class="line"><span class="meta">@dataclass(<span class="params">order=<span class="literal">True</span></span>)     </span><span class="comment"># order=True 自动生成比较方法</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Person</span>:</span><br><span class="line">    name: <span class="built_in">str</span></span><br><span class="line">    age: <span class="built_in">int</span></span><br><span class="line">    <span class="comment"># 有默认值的字段必须放在无默认值字段之后</span></span><br><span class="line">    email: <span class="built_in">str</span> = <span class="string">&quot;&quot;</span></span><br><span class="line">    <span class="comment"># field 工厂函数：每个实例独立创建默认值</span></span><br><span class="line">    tags: <span class="built_in">list</span> = field(default_factory=<span class="built_in">list</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__post_init__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;在 __init__ 之后调用的自定义初始化逻辑。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">if</span> <span class="variable language_">self</span>.age &lt; <span class="number">0</span>:</span><br><span class="line">            <span class="keyword">raise</span> ValueError(<span class="string">&quot;年龄不能为负数&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 自动生成的方法</span></span><br><span class="line">p1 = Person(<span class="string">&quot;Alice&quot;</span>, <span class="number">30</span>, <span class="string">&quot;alice@example.com&quot;</span>)</span><br><span class="line">p2 = Person(<span class="string">&quot;Bob&quot;</span>, <span class="number">25</span>)</span><br><span class="line"><span class="built_in">print</span>(p1)                    <span class="comment"># Person(name=&#x27;Alice&#x27;, age=30, email=&#x27;alice@example.com&#x27;, tags=[])</span></span><br><span class="line"><span class="built_in">print</span>(p1 == p2)              <span class="comment"># False</span></span><br><span class="line"><span class="built_in">print</span>(p1 &gt; p2)               <span class="comment"># True（按字段顺序比较）</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 转换</span></span><br><span class="line"><span class="built_in">print</span>(asdict(p1))            <span class="comment"># &#123;&#x27;name&#x27;: &#x27;Alice&#x27;, &#x27;age&#x27;: 30, &#x27;email&#x27;: &#x27;alice@example.com&#x27;, &#x27;tags&#x27;: []&#125;</span></span><br><span class="line"><span class="built_in">print</span>(astuple(p1))           <span class="comment"># (&#x27;Alice&#x27;, 30, &#x27;alice@example.com&#x27;, [])</span></span><br></pre></td></tr></table></figure><p><strong>dataclass 常用参数</strong>：</p><table><thead><tr><th>参数</th><th>说明</th></tr></thead><tbody><tr><td><code>init=True</code></td><td>自动生成 <code>__init__</code></td></tr><tr><td><code>repr=True</code></td><td>自动生成 <code>__repr__</code></td></tr><tr><td><code>eq=True</code></td><td>自动生成 <code>__eq__</code></td></tr><tr><td><code>order=False</code></td><td>自动生成 <code>__lt__</code>、<code>__le__</code>、<code>__gt__</code>、<code>__ge__</code></td></tr><tr><td><code>frozen=False</code></td><td>设置为 <code>True</code> 使实例不可变（类似命名元组）</td></tr><tr><td><code>slots=False</code></td><td>3.10+，使用 <code>__slots__</code> 优化内存</td></tr></tbody></table><p><strong>不可变 dataclass</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@dataclass(<span class="params">frozen=<span class="literal">True</span></span>)</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Point</span>:</span><br><span class="line">    x: <span class="built_in">float</span></span><br><span class="line">    y: <span class="built_in">float</span></span><br><span class="line"></span><br><span class="line">p = Point(<span class="number">1.0</span>, <span class="number">2.0</span>)</span><br><span class="line"><span class="comment"># p.x = 3.0  # FrozenInstanceError: cannot assign to field &#x27;x&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># frozen=True 自动实现 __hash__，可用作字典键</span></span><br><span class="line">d = &#123;p: <span class="string">&quot;origin&quot;</span>&#125;</span><br><span class="line"><span class="built_in">print</span>(d[Point(<span class="number">1.0</span>, <span class="number">2.0</span>)])  <span class="comment"># &quot;origin&quot;</span></span><br></pre></td></tr></table></figure><h3 id="6-4-Enum-枚举类（3-4-）"><a href="#6-4-Enum-枚举类（3-4-）" class="headerlink" title="6.4 Enum 枚举类（3.4+）"></a>6.4 Enum 枚举类（3.4+）</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><span class="line"><span class="keyword">from</span> enum <span class="keyword">import</span> Enum, auto</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Color</span>(<span class="title class_ inherited__">Enum</span>):</span><br><span class="line">    RED = <span class="number">1</span></span><br><span class="line">    GREEN = <span class="number">2</span></span><br><span class="line">    BLUE = <span class="number">3</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Status</span>(<span class="title class_ inherited__">Enum</span>):</span><br><span class="line">    PENDING = auto()    <span class="comment"># 自动赋值</span></span><br><span class="line">    ACTIVE = auto()</span><br><span class="line">    CLOSED = auto()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用</span></span><br><span class="line">c = Color.RED</span><br><span class="line"><span class="built_in">print</span>(c)              <span class="comment"># Color.RED</span></span><br><span class="line"><span class="built_in">print</span>(c.name)         <span class="comment"># RED</span></span><br><span class="line"><span class="built_in">print</span>(c.value)        <span class="comment"># 1</span></span><br><span class="line"><span class="built_in">print</span>(Color(<span class="number">1</span>))        <span class="comment"># Color.RED  ← 按值查找</span></span><br><span class="line"><span class="built_in">print</span>(Color[<span class="string">&#x27;RED&#x27;</span>])    <span class="comment"># Color.RED  ← 按名查找</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 比较</span></span><br><span class="line"><span class="built_in">print</span>(Color.RED == Color.RED)    <span class="comment"># True</span></span><br><span class="line"><span class="built_in">print</span>(Color.RED <span class="keyword">is</span> Color.RED)    <span class="comment"># True</span></span><br><span class="line"><span class="comment"># Color.RED &lt; Color.GREEN  # TypeError: Enum 不支持大小比较（除非用 IntEnum）</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 迭代</span></span><br><span class="line"><span class="keyword">for</span> color <span class="keyword">in</span> Color:</span><br><span class="line">    <span class="built_in">print</span>(color)</span><br><span class="line"><span class="comment"># Color.RED</span></span><br><span class="line"><span class="comment"># Color.GREEN</span></span><br><span class="line"><span class="comment"># Color.BLUE</span></span><br></pre></td></tr></table></figure><blockquote><p><strong>工程建议</strong>：用 <code>Enum</code> 替代魔术数字和字符串常量，代码更可读、更安全。</p></blockquote><hr><h2 id="7-设计模式在-Python-中的实践"><a href="#7-设计模式在-Python-中的实践" class="headerlink" title="7. 设计模式在 Python 中的实践"></a>7. 设计模式在 Python 中的实践</h2><h3 id="7-1-单例模式"><a href="#7-1-单例模式" class="headerlink" title="7.1 单例模式"></a>7.1 单例模式</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><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><span class="line"><span class="comment"># 方式一：使用 __new__（见 1.3 节示例）</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">SingletonNew</span>:</span><br><span class="line">    _instance = <span class="literal">None</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__new__</span>(<span class="params">cls, *args, **kwargs</span>):</span><br><span class="line">        <span class="keyword">if</span> cls._instance <span class="keyword">is</span> <span class="literal">None</span>:</span><br><span class="line">            cls._instance = <span class="built_in">super</span>().__new__(cls)</span><br><span class="line">        <span class="keyword">return</span> cls._instance</span><br><span class="line"></span><br><span class="line"><span class="comment"># 方式二：模块级单例（最 Pythonic）</span></span><br><span class="line"><span class="comment"># Python 模块本身就是单例，模块级变量天然只初始化一次</span></span><br><span class="line"><span class="comment"># singleton_config.py:</span></span><br><span class="line"><span class="comment">#     _config = &#123;&quot;debug&quot;: True, &quot;db&quot;: &quot;mysql://...&quot;&#125;</span></span><br><span class="line"><span class="comment">#     def get_config():</span></span><br><span class="line"><span class="comment">#         return _config</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 方式三：使用元类（见 6.1 节示例）</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">SingletonMeta</span>(<span class="title class_ inherited__">type</span>):</span><br><span class="line">    _instances = &#123;&#125;</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__call__</span>(<span class="params">cls, *args, **kwargs</span>):</span><br><span class="line">        <span class="keyword">if</span> cls <span class="keyword">not</span> <span class="keyword">in</span> cls._instances:</span><br><span class="line">            cls._instances[cls] = <span class="built_in">super</span>().__call__(*args, **kwargs)</span><br><span class="line">        <span class="keyword">return</span> cls._instances[cls]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 方式四：使用装饰器</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">singleton</span>(<span class="params">cls</span>):</span><br><span class="line">    instances = &#123;&#125;</span><br><span class="line"><span class="meta">    @functools.wraps(<span class="params">cls</span>)</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">wrapper</span>(<span class="params">*args, **kwargs</span>):</span><br><span class="line">        <span class="keyword">if</span> cls <span class="keyword">not</span> <span class="keyword">in</span> instances:</span><br><span class="line">            instances[cls] = cls(*args, **kwargs)</span><br><span class="line">        <span class="keyword">return</span> instances[cls]</span><br><span class="line">    <span class="keyword">return</span> wrapper</span><br></pre></td></tr></table></figure><table><thead><tr><th>方式</th><th>优点</th><th>缺点</th></tr></thead><tbody><tr><td><code>__new__</code></td><td>简单</td><td><code>__init__</code> 重复调用</td></tr><tr><td>模块级</td><td>最简单、最 Pythonic</td><td>需要单独模块文件</td></tr><tr><td>元类</td><td>最干净、<code>__init__</code> 只调用一次</td><td>理解成本高</td></tr><tr><td>装饰器</td><td>灵活</td><td>改变了类类型</td></tr></tbody></table><blockquote><p><strong>工程建议</strong>：Python 中最推荐的方式是<strong>模块级单例</strong>——把状态放在模块的全局变量中，通过函数访问。这是 Django、Flask 等框架的做法。</p></blockquote><h3 id="7-2-工厂模式"><a href="#7-2-工厂模式" class="headerlink" title="7.2 工厂模式"></a>7.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><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><span class="line"><span class="keyword">class</span> <span class="title class_">PaymentProcessor</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">pay</span>(<span class="params">self, amount</span>):</span><br><span class="line">        <span class="keyword">raise</span> NotImplementedError</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">WechatPay</span>(<span class="title class_ inherited__">PaymentProcessor</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">pay</span>(<span class="params">self, amount</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="string">f&quot;微信支付 <span class="subst">&#123;amount&#125;</span> 元&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Alipay</span>(<span class="title class_ inherited__">PaymentProcessor</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">pay</span>(<span class="params">self, amount</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="string">f&quot;支付宝支付 <span class="subst">&#123;amount&#125;</span> 元&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">PaymentFactory</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;简单工厂。&quot;&quot;&quot;</span></span><br><span class="line">    _processors = &#123;</span><br><span class="line">        <span class="string">&quot;wechat&quot;</span>: WechatPay,</span><br><span class="line">        <span class="string">&quot;alipay&quot;</span>: Alipay,</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">    @classmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">create</span>(<span class="params">cls, method</span>):</span><br><span class="line">        processor_class = cls._processors.get(method)</span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> processor_class:</span><br><span class="line">            <span class="keyword">raise</span> ValueError(<span class="string">f&quot;不支持的支付方式: <span class="subst">&#123;method&#125;</span>&quot;</span>)</span><br><span class="line">        <span class="keyword">return</span> processor_class()</span><br><span class="line"></span><br><span class="line"><span class="meta">    @classmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">register</span>(<span class="params">cls, method, processor_class</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;注册新的支付方式（开放扩展）。&quot;&quot;&quot;</span></span><br><span class="line">        cls._processors[method] = processor_class</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用</span></span><br><span class="line">processor = PaymentFactory.create(<span class="string">&quot;wechat&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(processor.pay(<span class="number">100</span>))   <span class="comment"># 微信支付 100 元</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 扩展：注册新的支付方式</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">ApplePay</span>(<span class="title class_ inherited__">PaymentProcessor</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">pay</span>(<span class="params">self, amount</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="string">f&quot;Apple Pay 支付 <span class="subst">&#123;amount&#125;</span> 元&quot;</span></span><br><span class="line"></span><br><span class="line">PaymentFactory.register(<span class="string">&quot;apple&quot;</span>, ApplePay)</span><br><span class="line"><span class="built_in">print</span>(PaymentFactory.create(<span class="string">&quot;apple&quot;</span>).pay(<span class="number">50</span>))  <span class="comment"># Apple Pay 支付 50 元</span></span><br></pre></td></tr></table></figure><h3 id="7-3-策略模式"><a href="#7-3-策略模式" class="headerlink" title="7.3 策略模式"></a>7.3 策略模式</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><span class="line"><span class="keyword">class</span> <span class="title class_">DiscountStrategy</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">apply</span>(<span class="params">self, price</span>):</span><br><span class="line">        <span class="keyword">raise</span> NotImplementedError</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">NoDiscount</span>(<span class="title class_ inherited__">DiscountStrategy</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">apply</span>(<span class="params">self, price</span>):</span><br><span class="line">        <span class="keyword">return</span> price</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">PercentageDiscount</span>(<span class="title class_ inherited__">DiscountStrategy</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, percent</span>):</span><br><span class="line">        <span class="variable language_">self</span>.percent = percent</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">apply</span>(<span class="params">self, price</span>):</span><br><span class="line">        <span class="keyword">return</span> price * (<span class="number">1</span> - <span class="variable language_">self</span>.percent / <span class="number">100</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">FixedDiscount</span>(<span class="title class_ inherited__">DiscountStrategy</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, amount</span>):</span><br><span class="line">        <span class="variable language_">self</span>.amount = amount</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">apply</span>(<span class="params">self, price</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">max</span>(<span class="number">0</span>, price - <span class="variable language_">self</span>.amount)</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Order</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, price, discount_strategy</span>):</span><br><span class="line">        <span class="variable language_">self</span>.price = price</span><br><span class="line">        <span class="variable language_">self</span>.discount = discount_strategy</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">final_price</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>.discount.apply(<span class="variable language_">self</span>.price)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用</span></span><br><span class="line">order1 = Order(<span class="number">100</span>, PercentageDiscount(<span class="number">20</span>))</span><br><span class="line">order2 = Order(<span class="number">100</span>, FixedDiscount(<span class="number">15</span>))</span><br><span class="line"><span class="built_in">print</span>(order1.final_price())   <span class="comment"># 80.0</span></span><br><span class="line"><span class="built_in">print</span>(order2.final_price())   <span class="comment"># 85</span></span><br></pre></td></tr></table></figure><p><strong>策略模式的 Pythonic 写法（用函数替代类）</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></pre></td><td class="code"><pre><span class="line"><span class="comment"># Python 中函数是一等公民，策略模式可以更简洁</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">no_discount</span>(<span class="params">price</span>):</span><br><span class="line">    <span class="keyword">return</span> price</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">percentage_discount</span>(<span class="params">percent</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">apply</span>(<span class="params">price</span>):</span><br><span class="line">        <span class="keyword">return</span> price * (<span class="number">1</span> - percent / <span class="number">100</span>)</span><br><span class="line">    <span class="keyword">return</span> apply</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">fixed_discount</span>(<span class="params">amount</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">apply</span>(<span class="params">price</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">max</span>(<span class="number">0</span>, price - amount)</span><br><span class="line">    <span class="keyword">return</span> apply</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用</span></span><br><span class="line">order = Order(<span class="number">100</span>, percentage_discount(<span class="number">20</span>))</span><br><span class="line"><span class="built_in">print</span>(order.final_price())  <span class="comment"># 80.0</span></span><br></pre></td></tr></table></figure><h3 id="7-4-观察者模式"><a href="#7-4-观察者模式" class="headerlink" title="7.4 观察者模式"></a>7.4 观察者模式</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><span class="line"><span class="keyword">class</span> <span class="title class_">EventEmitter</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;简单的事件发射器。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="variable language_">self</span>._listeners = &#123;&#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">on</span>(<span class="params">self, event, callback</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;注册事件监听器。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="variable language_">self</span>._listeners.setdefault(event, []).append(callback)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">off</span>(<span class="params">self, event, callback</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;移除事件监听器。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">if</span> event <span class="keyword">in</span> <span class="variable language_">self</span>._listeners:</span><br><span class="line">            <span class="variable language_">self</span>._listeners[event].remove(callback)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">emit</span>(<span class="params">self, event, *args, **kwargs</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;触发事件。&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">for</span> callback <span class="keyword">in</span> <span class="variable language_">self</span>._listeners.get(event, []):</span><br><span class="line">            callback(*args, **kwargs)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用</span></span><br><span class="line">emitter = EventEmitter()</span><br><span class="line"></span><br><span class="line">emitter.on(<span class="string">&#x27;login&#x27;</span>, <span class="keyword">lambda</span> user: <span class="built_in">print</span>(<span class="string">f&quot;用户 <span class="subst">&#123;user&#125;</span> 登录了&quot;</span>))</span><br><span class="line">emitter.on(<span class="string">&#x27;login&#x27;</span>, <span class="keyword">lambda</span> user: <span class="built_in">print</span>(<span class="string">f&quot;发送欢迎邮件给 <span class="subst">&#123;user&#125;</span>&quot;</span>))</span><br><span class="line"></span><br><span class="line">emitter.emit(<span class="string">&#x27;login&#x27;</span>, <span class="string">&#x27;Alice&#x27;</span>)</span><br><span class="line"><span class="comment"># 用户 Alice 登录了</span></span><br><span class="line"><span class="comment"># 发送欢迎邮件给 Alice</span></span><br></pre></td></tr></table></figure><hr><h2 id="Python-vs-Java-C-OOP-对比"><a href="#Python-vs-Java-C-OOP-对比" class="headerlink" title="Python vs Java&#x2F;C++ OOP 对比"></a>Python vs Java&#x2F;C++ OOP 对比</h2><p>很多 Python 学习者来自 Java 或 C++ 背景，了解它们的差异有助于快速建立 Pythonic 的 OOP 思维。</p><h3 id="核心差异对比表"><a href="#核心差异对比表" class="headerlink" title="核心差异对比表"></a>核心差异对比表</h3><table><thead><tr><th>特性</th><th>Python</th><th>Java</th><th>C++</th></tr></thead><tbody><tr><td><strong>访问控制</strong></td><td>约定（<code>_var</code>、<code>__var</code>），无强制</td><td><code>private</code>&#x2F;<code>protected</code>&#x2F;<code>public</code> 关键字</td><td><code>private</code>&#x2F;<code>protected</code>&#x2F;<code>public</code> 关键字</td></tr><tr><td><strong>多重继承</strong></td><td>✅ 直接支持</td><td>❌ 不支持（接口可多继承）</td><td>✅ 支持</td></tr><tr><td><strong>抽象类</strong></td><td><code>ABC</code> 模块 + <code>@abstractmethod</code></td><td><code>abstract</code> 关键字</td><td>纯虚函数 <code>= 0</code></td></tr><tr><td><strong>接口</strong></td><td>无关键字，用抽象类或协议</td><td><code>interface</code> 关键字</td><td>纯虚类</td></tr><tr><td><strong>构造函数</strong></td><td><code>__init__</code>（初始化）+ <code>__new__</code>（创建）</td><td>构造方法与类同名</td><td>构造函数与类同名</td></tr><tr><td><strong>析构函数</strong></td><td><code>__del__</code>（不推荐依赖）</td><td><code>finalize()</code>（已弃用）</td><td>析构函数 <code>~ClassName()</code></td></tr><tr><td><strong>方法重写</strong></td><td>无需关键字，直接重定义</td><td>必须加 <code>@Override</code> 注解</td><td><code>override</code> 关键字（C++11）</td></tr><tr><td><strong>运算符重载</strong></td><td>✅ <code>__add__</code>、<code>__eq__</code> 等</td><td>❌ 不支持</td><td>✅ 运算符函数</td></tr><tr><td><strong>属性访问</strong></td><td><code>@property</code> 装饰器</td><td><code>get</code>&#x2F;<code>set</code> 方法或 Lombok</td><td>手动编写 getter&#x2F;setter</td></tr><tr><td><strong>元编程</strong></td><td>元类、<code>__init_subclass__</code></td><td>注解 + 反射</td><td>模板元编程</td></tr><tr><td><strong>数据类</strong></td><td><code>@dataclass</code>（自动生成方法）</td><td><code>record</code>（Java 14+）</td><td>无内置支持</td></tr><tr><td><strong>鸭子类型</strong></td><td>✅ 核心特性</td><td>❌ 强类型</td><td>部分支持（模板）</td></tr></tbody></table><h3 id="访问控制对比"><a href="#访问控制对比" class="headerlink" title="访问控制对比"></a>访问控制对比</h3><p><strong>Java&#x2F;C++：强制访问控制</strong></p><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// Java</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">User</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> String name;        <span class="comment">// 只能在类内访问</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="type">int</span> age;          <span class="comment">// 子类可访问</span></span><br><span class="line">    <span class="keyword">public</span> String email;        <span class="comment">// 公开访问</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">getName</span><span class="params">()</span> &#123;   <span class="comment">// 通过 getter 访问</span></span><br><span class="line">        <span class="keyword">return</span> name;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>Python：约定式访问控制</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><span class="line"><span class="comment"># Python</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">User</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="variable language_">self</span>.__<span class="built_in">id</span> = <span class="number">0</span>           <span class="comment"># 名称修饰（实际是 _User__id），但仍然可访问</span></span><br><span class="line">        <span class="variable language_">self</span>._internal = <span class="literal">None</span>   <span class="comment"># 约定：视为私有，但技术上可访问</span></span><br><span class="line">        <span class="variable language_">self</span>.name = <span class="string">&quot;&quot;</span>          <span class="comment"># 公开属性</span></span><br><span class="line">    </span><br><span class="line"><span class="meta">    @property</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">id</span>(<span class="params">self</span>):               <span class="comment"># 通过 property 控制访问</span></span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>.__<span class="built_in">id</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Python 的哲学：We are all consenting adults here（我们都是成年人）</span></span><br><span class="line"><span class="comment"># 相信使用者不会滥用，而不是强制限制</span></span><br></pre></td></tr></table></figure><h3 id="多重继承对比"><a href="#多重继承对比" class="headerlink" title="多重继承对比"></a>多重继承对比</h3><p><strong>Java：接口多继承</strong></p><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// Java - 只能继承一个类，但可实现多个接口</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Dog</span> <span class="keyword">extends</span> <span class="title class_">Animal</span> <span class="keyword">implements</span> <span class="title class_">Runnable</span>, Serializable &#123;</span><br><span class="line">    <span class="comment">// 必须实现接口的所有方法</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>Python：真·多重继承 + MRO</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></pre></td><td class="code"><pre><span class="line"><span class="comment"># Python - 可直接多继承</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Dog</span>(Animal, Runnable, Serializable):</span><br><span class="line">    <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># MRO（方法解析顺序）解决钻石问题</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">A</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">method</span>(<span class="params">self</span>): <span class="built_in">print</span>(<span class="string">&quot;A&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">B</span>(<span class="title class_ inherited__">A</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">method</span>(<span class="params">self</span>): <span class="built_in">print</span>(<span class="string">&quot;B&quot;</span>); <span class="built_in">super</span>().method()</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">C</span>(<span class="title class_ inherited__">A</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">method</span>(<span class="params">self</span>): <span class="built_in">print</span>(<span class="string">&quot;C&quot;</span>); <span class="built_in">super</span>().method()</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">D</span>(B, C):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">method</span>(<span class="params">self</span>): <span class="built_in">print</span>(<span class="string">&quot;D&quot;</span>); <span class="built_in">super</span>().method()</span><br><span class="line"></span><br><span class="line"><span class="comment"># MRO: D → B → C → A</span></span><br><span class="line"><span class="comment"># 调用链: D.method() → B.method() → C.method() → A.method()</span></span><br></pre></td></tr></table></figure><h3 id="运算符重载对比"><a href="#运算符重载对比" class="headerlink" title="运算符重载对比"></a>运算符重载对比</h3><p><strong>Java：不支持运算符重载</strong></p><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// Java - 必须使用方法</span></span><br><span class="line"><span class="type">Vector2D</span> <span class="variable">a</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Vector2D</span>(<span class="number">1</span>, <span class="number">2</span>);</span><br><span class="line"><span class="type">Vector2D</span> <span class="variable">b</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Vector2D</span>(<span class="number">3</span>, <span class="number">4</span>);</span><br><span class="line"><span class="type">Vector2D</span> <span class="variable">c</span> <span class="operator">=</span> a.add(b);  <span class="comment">// 不能写 a + b</span></span><br></pre></td></tr></table></figure><p><strong>Python：运算符重载</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><span class="line"><span class="comment"># Python - 可以重载运算符</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Vector2D</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, x, y</span>):</span><br><span class="line">        <span class="variable language_">self</span>.x = x</span><br><span class="line">        <span class="variable language_">self</span>.y = y</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__add__</span>(<span class="params">self, other</span>):</span><br><span class="line">        <span class="keyword">return</span> Vector2D(<span class="variable language_">self</span>.x + other.x, <span class="variable language_">self</span>.y + other.y)</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__repr__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="string">f&quot;Vector2D(<span class="subst">&#123;self.x&#125;</span>, <span class="subst">&#123;self.y&#125;</span>)&quot;</span></span><br><span class="line"></span><br><span class="line">a = Vector2D(<span class="number">1</span>, <span class="number">2</span>)</span><br><span class="line">b = Vector2D(<span class="number">3</span>, <span class="number">4</span>)</span><br><span class="line">c = a + b  <span class="comment"># 自然直观</span></span><br></pre></td></tr></table></figure><h3 id="设计哲学差异"><a href="#设计哲学差异" class="headerlink" title="设计哲学差异"></a>设计哲学差异</h3><table><thead><tr><th>方面</th><th>Python</th><th>Java&#x2F;C++</th></tr></thead><tbody><tr><td><strong>核心理念</strong></td><td>“We are all consenting adults”</td><td>“安全优于灵活”</td></tr><tr><td><strong>类型检查</strong></td><td>运行时（动态类型）</td><td>编译时（静态类型）</td></tr><tr><td><strong>隐式行为</strong></td><td>魔术方法（协议）</td><td>显式关键字</td></tr><tr><td><strong>复杂性</strong></td><td>简单优先</td><td>严格控制</td></tr><tr><td><strong>错误发现</strong></td><td>运行时异常</td><td>编译时错误</td></tr></tbody></table><hr><h2 id="常见面试题汇总"><a href="#常见面试题汇总" class="headerlink" title="常见面试题汇总"></a>常见面试题汇总</h2><h3 id="Q1：-new-和-init-的区别？"><a href="#Q1：-new-和-init-的区别？" class="headerlink" title="Q1：__new__ 和 __init__ 的区别？"></a>Q1：<code>__new__</code> 和 <code>__init__</code> 的区别？</h3><p><strong>答案</strong>：</p><ul><li><code>__new__</code>：类方法，负责<strong>创建</strong>对象实例（分配内存），返回实例</li><li><code>__init__</code>：实例方法，负责<strong>初始化</strong>对象属性，不返回值</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Singleton</span>:</span><br><span class="line">    _instance = <span class="literal">None</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__new__</span>(<span class="params">cls, *args, **kwargs</span>):</span><br><span class="line">        <span class="keyword">if</span> cls._instance <span class="keyword">is</span> <span class="literal">None</span>:</span><br><span class="line">            cls._instance = <span class="built_in">super</span>().__new__(cls)</span><br><span class="line">        <span class="keyword">return</span> cls._instance</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, value</span>):</span><br><span class="line">        <span class="comment"># 每次调用都会执行！所以单例模式要小心</span></span><br><span class="line">        <span class="variable language_">self</span>.value = value</span><br></pre></td></tr></table></figure><h3 id="Q2：Python-的-MRO-是什么？如何计算？"><a href="#Q2：Python-的-MRO-是什么？如何计算？" class="headerlink" title="Q2：Python 的 MRO 是什么？如何计算？"></a>Q2：Python 的 MRO 是什么？如何计算？</h3><p><strong>答案</strong>：MRO（Method Resolution Order）是方法解析顺序，使用 C3 线性化算法计算。</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">A</span>: <span class="keyword">pass</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">B</span>(<span class="title class_ inherited__">A</span>): <span class="keyword">pass</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">C</span>(<span class="title class_ inherited__">A</span>): <span class="keyword">pass</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">D</span>(B, C): <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(D.__mro__)  <span class="comment"># (D, B, C, A, object)</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># C3 算法规则：</span></span><br><span class="line"><span class="comment"># 1. 子类优先于父类</span></span><br><span class="line"><span class="comment"># 2. 左边父类优先于右边父类</span></span><br><span class="line"><span class="comment"># 3. 每个类只出现一次</span></span><br></pre></td></tr></table></figure><h3 id="Q3：-classmethod、-staticmethod-和实例方法的区别？"><a href="#Q3：-classmethod、-staticmethod-和实例方法的区别？" class="headerlink" title="Q3：@classmethod、@staticmethod 和实例方法的区别？"></a>Q3：<code>@classmethod</code>、<code>@staticmethod</code> 和实例方法的区别？</h3><p><strong>答案</strong>：</p><table><thead><tr><th>类型</th><th>第一个参数</th><th>调用方式</th><th>用途</th></tr></thead><tbody><tr><td>实例方法</td><td><code>self</code>（实例）</td><td><code>obj.method()</code></td><td>操作实例数据</td></tr><tr><td>类方法</td><td><code>cls</code>（类）</td><td><code>Class.method()</code> 或 <code>obj.method()</code></td><td>工厂方法、操作类数据</td></tr><tr><td>静态方法</td><td>无</td><td><code>Class.method()</code></td><td>工具函数，与类相关但不需要类&#x2F;实例</td></tr></tbody></table><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><span class="line"><span class="keyword">class</span> <span class="title class_">Date</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, year, month, day</span>):</span><br><span class="line">        <span class="variable language_">self</span>.year = year</span><br><span class="line">        <span class="variable language_">self</span>.month = month</span><br><span class="line">        <span class="variable language_">self</span>.day = day</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">display</span>(<span class="params">self</span>):  <span class="comment"># 实例方法</span></span><br><span class="line">        <span class="keyword">return</span> <span class="string">f&quot;<span class="subst">&#123;self.year&#125;</span>-<span class="subst">&#123;self.month&#125;</span>-<span class="subst">&#123;self.day&#125;</span>&quot;</span></span><br><span class="line">    </span><br><span class="line"><span class="meta">    @classmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">from_string</span>(<span class="params">cls, date_str</span>):  <span class="comment"># 类方法（工厂）</span></span><br><span class="line">        y, m, d = <span class="built_in">map</span>(<span class="built_in">int</span>, date_str.split(<span class="string">&#x27;-&#x27;</span>))</span><br><span class="line">        <span class="keyword">return</span> cls(y, m, d)</span><br><span class="line">    </span><br><span class="line"><span class="meta">    @staticmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">is_valid_date</span>(<span class="params">date_str</span>):  <span class="comment"># 静态方法（验证工具）</span></span><br><span class="line">        <span class="keyword">try</span>:</span><br><span class="line">            y, m, d = <span class="built_in">map</span>(<span class="built_in">int</span>, date_str.split(<span class="string">&#x27;-&#x27;</span>))</span><br><span class="line">            <span class="keyword">return</span> <span class="number">1</span> &lt;= m &lt;= <span class="number">12</span> <span class="keyword">and</span> <span class="number">1</span> &lt;= d &lt;= <span class="number">31</span></span><br><span class="line">        <span class="keyword">except</span>:</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">False</span></span><br></pre></td></tr></table></figure><h3 id="Q4：为什么实现了-eq-后对象就不可哈希了？"><a href="#Q4：为什么实现了-eq-后对象就不可哈希了？" class="headerlink" title="Q4：为什么实现了 __eq__ 后对象就不可哈希了？"></a>Q4：为什么实现了 <code>__eq__</code> 后对象就不可哈希了？</h3><p><strong>答案</strong>：Python 默认认为相等的对象应该有相同的哈希值。如果只实现 <code>__eq__</code> 而不实现 <code>__hash__</code>，Python 会将 <code>__hash__</code> 设为 <code>None</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Point</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, x, y</span>):</span><br><span class="line">        <span class="variable language_">self</span>.x = x</span><br><span class="line">        <span class="variable language_">self</span>.y = y</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__eq__</span>(<span class="params">self, other</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>.x == other.x <span class="keyword">and</span> <span class="variable language_">self</span>.y == other.y</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__hash__</span>(<span class="params">self</span>):  <span class="comment"># 必须同时实现</span></span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">hash</span>((<span class="variable language_">self</span>.x, <span class="variable language_">self</span>.y))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 现在可以用作 dict 键或放入 set</span></span><br><span class="line">p1 = Point(<span class="number">1</span>, <span class="number">2</span>)</span><br><span class="line">p2 = Point(<span class="number">1</span>, <span class="number">2</span>)</span><br><span class="line"><span class="built_in">print</span>(p1 == p2)  <span class="comment"># True</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">hash</span>(p1) == <span class="built_in">hash</span>(p2))  <span class="comment"># True</span></span><br></pre></td></tr></table></figure><h3 id="Q5：-property-的作用是什么？"><a href="#Q5：-property-的作用是什么？" class="headerlink" title="Q5：@property 的作用是什么？"></a>Q5：<code>@property</code> 的作用是什么？</h3><p><strong>答案</strong>：<code>@property</code> 将方法变成属性访问，用于：</p><ol><li>实现只读属性</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><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><span class="line"><span class="keyword">class</span> <span class="title class_">Circle</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, radius</span>):</span><br><span class="line">        <span class="variable language_">self</span>._radius = radius</span><br><span class="line">    </span><br><span class="line"><span class="meta">    @property</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">radius</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>._radius</span><br><span class="line">    </span><br><span class="line"><span class="meta">    @radius.setter</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">radius</span>(<span class="params">self, value</span>):</span><br><span class="line">        <span class="keyword">if</span> value &lt;= <span class="number">0</span>:</span><br><span class="line">            <span class="keyword">raise</span> ValueError(<span class="string">&quot;半径必须为正数&quot;</span>)</span><br><span class="line">        <span class="variable language_">self</span>._radius = value</span><br><span class="line">    </span><br><span class="line"><span class="meta">    @property</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">area</span>(<span class="params">self</span>):  <span class="comment"># 计算属性，只读</span></span><br><span class="line">        <span class="keyword">return</span> <span class="number">3.14159</span> * <span class="variable language_">self</span>._radius ** <span class="number">2</span></span><br><span class="line"></span><br><span class="line">c = Circle(<span class="number">5</span>)</span><br><span class="line"><span class="built_in">print</span>(c.area)  <span class="comment"># 78.54（像属性一样访问）</span></span><br><span class="line">c.radius = <span class="number">10</span>  <span class="comment"># 带验证的设置</span></span><br></pre></td></tr></table></figure><h3 id="Q6：什么是描述符？"><a href="#Q6：什么是描述符？" class="headerlink" title="Q6：什么是描述符？"></a>Q6：什么是描述符？</h3><p><strong>答案</strong>：描述符是实现了 <code>__get__</code>、<code>__set__</code>、<code>__delete__</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Validator</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;验证描述符&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, min_value, max_value</span>):</span><br><span class="line">        <span class="variable language_">self</span>.min_value = min_value</span><br><span class="line">        <span class="variable language_">self</span>.max_value = max_value</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__set_name__</span>(<span class="params">self, owner, name</span>):</span><br><span class="line">        <span class="variable language_">self</span>.name = name</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__get__</span>(<span class="params">self, obj, owner</span>):</span><br><span class="line">        <span class="keyword">return</span> obj.__dict__.get(<span class="variable language_">self</span>.name)</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__set__</span>(<span class="params">self, obj, value</span>):</span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> (<span class="variable language_">self</span>.min_value &lt;= value &lt;= <span class="variable language_">self</span>.max_value):</span><br><span class="line">            <span class="keyword">raise</span> ValueError(<span class="string">f&quot;<span class="subst">&#123;self.name&#125;</span> 必须在 <span class="subst">&#123;self.min_value&#125;</span>-<span class="subst">&#123;self.max_value&#125;</span> 之间&quot;</span>)</span><br><span class="line">        obj.__dict__[<span class="variable language_">self</span>.name] = value</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Person</span>:</span><br><span class="line">    age = Validator(<span class="number">0</span>, <span class="number">150</span>)</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, age</span>):</span><br><span class="line">        <span class="variable language_">self</span>.age = age</span><br><span class="line"></span><br><span class="line">p = Person(<span class="number">25</span>)  <span class="comment"># OK</span></span><br><span class="line">p.age = <span class="number">200</span>     <span class="comment"># ValueError</span></span><br></pre></td></tr></table></figure><h3 id="Q7：-dataclass-的优势是什么？"><a href="#Q7：-dataclass-的优势是什么？" class="headerlink" title="Q7：@dataclass 的优势是什么？"></a>Q7：<code>@dataclass</code> 的优势是什么？</h3><p><strong>答案</strong>：<code>@dataclass</code> 自动生成 <code>__init__</code>、<code>__repr__</code>、<code>__eq__</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> dataclasses <span class="keyword">import</span> dataclass, field</span><br><span class="line"><span class="keyword">from</span> typing <span class="keyword">import</span> <span class="type">List</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@dataclass</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Student</span>:</span><br><span class="line">    name: <span class="built_in">str</span></span><br><span class="line">    age: <span class="built_in">int</span></span><br><span class="line">    grades: <span class="type">List</span>[<span class="built_in">int</span>] = field(default_factory=<span class="built_in">list</span>)</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">average_grade</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">sum</span>(<span class="variable language_">self</span>.grades) / <span class="built_in">len</span>(<span class="variable language_">self</span>.grades) <span class="keyword">if</span> <span class="variable language_">self</span>.grades <span class="keyword">else</span> <span class="number">0</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 自动生成：</span></span><br><span class="line"><span class="comment"># __init__(self, name: str, age: int, grades: List[int] = ...)</span></span><br><span class="line"><span class="comment"># __repr__(self): return f&quot;Student(name=&#123;self.name&#125;, ...)&quot;</span></span><br><span class="line"><span class="comment"># __eq__(self, other): return self.name == other.name and ...</span></span><br><span class="line"></span><br><span class="line">s1 = Student(<span class="string">&quot;Alice&quot;</span>, <span class="number">20</span>, [<span class="number">90</span>, <span class="number">85</span>, <span class="number">92</span>])</span><br><span class="line">s2 = Student(<span class="string">&quot;Alice&quot;</span>, <span class="number">20</span>, [<span class="number">90</span>, <span class="number">85</span>, <span class="number">92</span>])</span><br><span class="line"><span class="built_in">print</span>(s1 == s2)  <span class="comment"># True（自动比较所有字段）</span></span><br></pre></td></tr></table></figure><h3 id="Q8：元类的作用和使用场景？"><a href="#Q8：元类的作用和使用场景？" class="headerlink" title="Q8：元类的作用和使用场景？"></a>Q8：元类的作用和使用场景？</h3><p><strong>答案</strong>：元类是”类的类”，控制类的创建过程。常见用途：</p><ol><li>自动注册子类（工厂模式）</li><li>添加类属性&#x2F;方法</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><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 简化方案：__init_subclass__（Python 3.6+）</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">PluginBase</span>:</span><br><span class="line">    _registry = &#123;&#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init_subclass__</span>(<span class="params">cls, **kwargs</span>):</span><br><span class="line">        <span class="built_in">super</span>().__init_subclass__(**kwargs)</span><br><span class="line">        PluginBase._registry[cls.__name__] = cls</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">SpamFilter</span>(<span class="title class_ inherited__">PluginBase</span>):</span><br><span class="line">    <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">VirusScanner</span>(<span class="title class_ inherited__">PluginBase</span>):</span><br><span class="line">    <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(PluginBase._registry)</span><br><span class="line"><span class="comment"># &#123;&#x27;SpamFilter&#x27;: &lt;class SpamFilter&gt;, &#x27;VirusScanner&#x27;: &lt;class VirusScanner&gt;&#125;</span></span><br></pre></td></tr></table></figure><hr><h2 id="实战项目：简易-ORM-框架"><a href="#实战项目：简易-ORM-框架" class="headerlink" title="实战项目：简易 ORM 框架"></a>实战项目：简易 ORM 框架</h2><p>通过实现一个迷你 ORM（对象关系映射），综合运用 OOP 核心知识。</p><h3 id="项目目标"><a href="#项目目标" class="headerlink" title="项目目标"></a>项目目标</h3><p>创建一个支持以下功能的 ORM：</p><ul><li>类到表的映射</li><li>字段类型验证</li><li>简单查询 API</li><li>关联关系</li></ul><h3 id="完整代码"><a href="#完整代码" class="headerlink" title="完整代码"></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><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><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#!/usr/bin/env python3</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">迷你 ORM 框架 - 第三阶段实战项目</span></span><br><span class="line"><span class="string">知识点：描述符、元类、魔术方法、继承、属性装饰器</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> typing <span class="keyword">import</span> <span class="type">Any</span>, <span class="type">Dict</span>, <span class="type">List</span>, <span class="type">Optional</span>, <span class="type">Type</span>, get_type_hints</span><br><span class="line"><span class="keyword">from</span> dataclasses <span class="keyword">import</span> dataclass</span><br><span class="line"><span class="keyword">from</span> abc <span class="keyword">import</span> ABC, abstractmethod</span><br><span class="line"><span class="keyword">import</span> sqlite3</span><br><span class="line"></span><br><span class="line"><span class="comment"># ============== 字段描述符 ==============</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Field</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;字段基类描述符&quot;&quot;&quot;</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, column_type: <span class="built_in">str</span>, primary_key: <span class="built_in">bool</span> = <span class="literal">False</span>, </span></span><br><span class="line"><span class="params">                 nullable: <span class="built_in">bool</span> = <span class="literal">True</span>, default: <span class="type">Any</span> = <span class="literal">None</span></span>):</span><br><span class="line">        <span class="variable language_">self</span>.column_type = column_type</span><br><span class="line">        <span class="variable language_">self</span>.primary_key = primary_key</span><br><span class="line">        <span class="variable language_">self</span>.nullable = nullable</span><br><span class="line">        <span class="variable language_">self</span>.default = default</span><br><span class="line">        <span class="variable language_">self</span>.name = <span class="literal">None</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__set_name__</span>(<span class="params">self, owner, name</span>):</span><br><span class="line">        <span class="variable language_">self</span>.name = name</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__get__</span>(<span class="params">self, obj, owner</span>):</span><br><span class="line">        <span class="keyword">if</span> obj <span class="keyword">is</span> <span class="literal">None</span>:</span><br><span class="line">            <span class="keyword">return</span> <span class="variable language_">self</span></span><br><span class="line">        <span class="keyword">return</span> obj.__dict__.get(<span class="variable language_">self</span>.name, <span class="variable language_">self</span>.default)</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__set__</span>(<span class="params">self, obj, value</span>):</span><br><span class="line">        <span class="variable language_">self</span>._validate(value)</span><br><span class="line">        obj.__dict__[<span class="variable language_">self</span>.name] = value</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">_validate</span>(<span class="params">self, value</span>):</span><br><span class="line">        <span class="keyword">if</span> value <span class="keyword">is</span> <span class="literal">None</span>:</span><br><span class="line">            <span class="keyword">if</span> <span class="keyword">not</span> <span class="variable language_">self</span>.nullable <span class="keyword">and</span> <span class="keyword">not</span> <span class="variable language_">self</span>.primary_key:</span><br><span class="line">                <span class="keyword">raise</span> ValueError(<span class="string">f&quot;<span class="subst">&#123;self.name&#125;</span> 不能为空&quot;</span>)</span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            <span class="comment"># 类型检查（简化版）</span></span><br><span class="line">            type_map = &#123;</span><br><span class="line">                <span class="string">&#x27;INTEGER&#x27;</span>: <span class="built_in">int</span>,</span><br><span class="line">                <span class="string">&#x27;TEXT&#x27;</span>: <span class="built_in">str</span>,</span><br><span class="line">                <span class="string">&#x27;REAL&#x27;</span>: <span class="built_in">float</span>,</span><br><span class="line">            &#125;</span><br><span class="line">            expected_type = type_map.get(<span class="variable language_">self</span>.column_type)</span><br><span class="line">            <span class="keyword">if</span> expected_type <span class="keyword">and</span> <span class="keyword">not</span> <span class="built_in">isinstance</span>(value, expected_type):</span><br><span class="line">                <span class="keyword">raise</span> TypeError(<span class="string">f&quot;<span class="subst">&#123;self.name&#125;</span> 应为 <span class="subst">&#123;expected_type.__name__&#125;</span> 类型&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">IntegerField</span>(<span class="title class_ inherited__">Field</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;整型字段&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, primary_key: <span class="built_in">bool</span> = <span class="literal">False</span>, nullable: <span class="built_in">bool</span> = <span class="literal">True</span>, default: <span class="built_in">int</span> = <span class="literal">None</span></span>):</span><br><span class="line">        <span class="built_in">super</span>().__init__(<span class="string">&#x27;INTEGER&#x27;</span>, primary_key, nullable, default)</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">TextField</span>(<span class="title class_ inherited__">Field</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;文本字段&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, primary_key: <span class="built_in">bool</span> = <span class="literal">False</span>, nullable: <span class="built_in">bool</span> = <span class="literal">True</span>, default: <span class="built_in">str</span> = <span class="literal">None</span></span>):</span><br><span class="line">        <span class="built_in">super</span>().__init__(<span class="string">&#x27;TEXT&#x27;</span>, primary_key, nullable, default)</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">FloatField</span>(<span class="title class_ inherited__">Field</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;浮点字段&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, primary_key: <span class="built_in">bool</span> = <span class="literal">False</span>, nullable: <span class="built_in">bool</span> = <span class="literal">True</span>, default: <span class="built_in">float</span> = <span class="literal">None</span></span>):</span><br><span class="line">        <span class="built_in">super</span>().__init__(<span class="string">&#x27;REAL&#x27;</span>, primary_key, nullable, default)</span><br><span class="line"></span><br><span class="line"><span class="comment"># ============== Model 元类 ==============</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">ModelMeta</span>(<span class="title class_ inherited__">type</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;模型元类：自动收集字段信息&quot;&quot;&quot;</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__new__</span>(<span class="params">mcs, name, bases, namespace</span>):</span><br><span class="line">        cls = <span class="built_in">super</span>().__new__(mcs, name, bases, namespace)</span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 收集字段</span></span><br><span class="line">        fields = &#123;&#125;</span><br><span class="line">        <span class="keyword">for</span> key, value <span class="keyword">in</span> namespace.items():</span><br><span class="line">            <span class="keyword">if</span> <span class="built_in">isinstance</span>(value, Field):</span><br><span class="line">                fields[key] = value</span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 设置类属性</span></span><br><span class="line">        cls._fields = fields</span><br><span class="line">        cls._table_name = name.lower() + <span class="string">&#x27;s&#x27;</span></span><br><span class="line">        cls._primary_key = <span class="literal">None</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 找主键</span></span><br><span class="line">        <span class="keyword">for</span> field_name, field <span class="keyword">in</span> fields.items():</span><br><span class="line">            <span class="keyword">if</span> field.primary_key:</span><br><span class="line">                cls._primary_key = field_name</span><br><span class="line">                <span class="keyword">break</span></span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> cls</span><br><span class="line"></span><br><span class="line"><span class="comment"># ============== Model 基类 ==============</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Model</span>(metaclass=ModelMeta):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;模型基类&quot;&quot;&quot;</span></span><br><span class="line">    </span><br><span class="line">    _fields: <span class="type">Dict</span>[<span class="built_in">str</span>, Field] = &#123;&#125;</span><br><span class="line">    _table_name: <span class="built_in">str</span> = <span class="string">&quot;&quot;</span></span><br><span class="line">    _primary_key: <span class="type">Optional</span>[<span class="built_in">str</span>] = <span class="literal">None</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, **kwargs</span>):</span><br><span class="line">        <span class="keyword">for</span> field_name <span class="keyword">in</span> <span class="variable language_">self</span>._fields:</span><br><span class="line">            value = kwargs.get(field_name, <span class="variable language_">self</span>._fields[field_name].default)</span><br><span class="line">            <span class="built_in">setattr</span>(<span class="variable language_">self</span>, field_name, value)</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__repr__</span>(<span class="params">self</span>):</span><br><span class="line">        attrs = <span class="string">&#x27;, &#x27;</span>.join(<span class="string">f&quot;<span class="subst">&#123;k&#125;</span>=<span class="subst">&#123;v&#125;</span>&quot;</span> <span class="keyword">for</span> k, v <span class="keyword">in</span> <span class="variable language_">self</span>.__dict__.items())</span><br><span class="line">        <span class="keyword">return</span> <span class="string">f&quot;<span class="subst">&#123;self.__class__.__name__&#125;</span>(<span class="subst">&#123;attrs&#125;</span>)&quot;</span></span><br><span class="line">    </span><br><span class="line"><span class="meta">    @classmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_create_sql</span>(<span class="params">cls</span>) -&gt; <span class="built_in">str</span>:</span><br><span class="line">        <span class="string">&quot;&quot;&quot;生成建表 SQL&quot;&quot;&quot;</span></span><br><span class="line">        columns = []</span><br><span class="line">        <span class="keyword">for</span> name, field <span class="keyword">in</span> cls._fields.items():</span><br><span class="line">            col_def = <span class="string">f&quot;<span class="subst">&#123;name&#125;</span> <span class="subst">&#123;field.column_type&#125;</span>&quot;</span></span><br><span class="line">            <span class="keyword">if</span> field.primary_key:</span><br><span class="line">                col_def += <span class="string">&quot; PRIMARY KEY&quot;</span></span><br><span class="line">            <span class="keyword">if</span> <span class="keyword">not</span> field.nullable:</span><br><span class="line">                col_def += <span class="string">&quot; NOT NULL&quot;</span></span><br><span class="line">            columns.append(col_def)</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> <span class="string">f&quot;CREATE TABLE IF NOT EXISTS <span class="subst">&#123;cls._table_name&#125;</span> (<span class="subst">&#123;<span class="string">&#x27;, &#x27;</span>.join(columns)&#125;</span>)&quot;</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_insert_sql</span>(<span class="params">self</span>) -&gt; <span class="built_in">tuple</span>:</span><br><span class="line">        <span class="string">&quot;&quot;&quot;生成插入 SQL&quot;&quot;&quot;</span></span><br><span class="line">        columns = <span class="built_in">list</span>(<span class="variable language_">self</span>._fields.keys())</span><br><span class="line">        values = [<span class="built_in">getattr</span>(<span class="variable language_">self</span>, col) <span class="keyword">for</span> col <span class="keyword">in</span> columns]</span><br><span class="line">        placeholders = <span class="string">&#x27;, &#x27;</span>.join([<span class="string">&#x27;?&#x27;</span> <span class="keyword">for</span> _ <span class="keyword">in</span> columns])</span><br><span class="line">        </span><br><span class="line">        sql = <span class="string">f&quot;INSERT INTO <span class="subst">&#123;self._table_name&#125;</span> (<span class="subst">&#123;<span class="string">&#x27;, &#x27;</span>.join(columns)&#125;</span>) VALUES (<span class="subst">&#123;placeholders&#125;</span>)&quot;</span></span><br><span class="line">        <span class="keyword">return</span> sql, values</span><br><span class="line">    </span><br><span class="line"><span class="meta">    @classmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_select_sql</span>(<span class="params">cls, where: <span class="built_in">str</span> = <span class="literal">None</span></span>) -&gt; <span class="built_in">str</span>:</span><br><span class="line">        <span class="string">&quot;&quot;&quot;生成查询 SQL&quot;&quot;&quot;</span></span><br><span class="line">        sql = <span class="string">f&quot;SELECT * FROM <span class="subst">&#123;cls._table_name&#125;</span>&quot;</span></span><br><span class="line">        <span class="keyword">if</span> where:</span><br><span class="line">            sql += <span class="string">f&quot; WHERE <span class="subst">&#123;where&#125;</span>&quot;</span></span><br><span class="line">        <span class="keyword">return</span> sql</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">save</span>(<span class="params">self, conn</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;保存到数据库&quot;&quot;&quot;</span></span><br><span class="line">        sql, values = <span class="variable language_">self</span>.get_insert_sql()</span><br><span class="line">        cursor = conn.cursor()</span><br><span class="line">        cursor.execute(sql, values)</span><br><span class="line">        conn.commit()</span><br><span class="line">        <span class="keyword">return</span> cursor.lastrowid</span><br><span class="line">    </span><br><span class="line"><span class="meta">    @classmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">find_by_id</span>(<span class="params">cls, conn, pk_value</span>) -&gt; <span class="type">Optional</span>[<span class="string">&#x27;Model&#x27;</span>]:</span><br><span class="line">        <span class="string">&quot;&quot;&quot;按主键查询&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> cls._primary_key:</span><br><span class="line">            <span class="keyword">raise</span> ValueError(<span class="string">&quot;未定义主键&quot;</span>)</span><br><span class="line">        </span><br><span class="line">        sql = cls.get_select_sql(<span class="string">f&quot;<span class="subst">&#123;cls._primary_key&#125;</span> = ?&quot;</span>)</span><br><span class="line">        cursor = conn.cursor()</span><br><span class="line">        cursor.execute(sql, (pk_value,))</span><br><span class="line">        row = cursor.fetchone()</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">if</span> row:</span><br><span class="line">            columns = [desc[<span class="number">0</span>] <span class="keyword">for</span> desc <span class="keyword">in</span> cursor.description]</span><br><span class="line">            <span class="keyword">return</span> cls(**<span class="built_in">dict</span>(<span class="built_in">zip</span>(columns, row)))</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">None</span></span><br><span class="line">    </span><br><span class="line"><span class="meta">    @classmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">find_all</span>(<span class="params">cls, conn</span>) -&gt; <span class="type">List</span>[<span class="string">&#x27;Model&#x27;</span>]:</span><br><span class="line">        <span class="string">&quot;&quot;&quot;查询所有&quot;&quot;&quot;</span></span><br><span class="line">        sql = cls.get_select_sql()</span><br><span class="line">        cursor = conn.cursor()</span><br><span class="line">        cursor.execute(sql)</span><br><span class="line">        rows = cursor.fetchall()</span><br><span class="line">        </span><br><span class="line">        columns = [desc[<span class="number">0</span>] <span class="keyword">for</span> desc <span class="keyword">in</span> cursor.description]</span><br><span class="line">        <span class="keyword">return</span> [cls(**<span class="built_in">dict</span>(<span class="built_in">zip</span>(columns, row))) <span class="keyword">for</span> row <span class="keyword">in</span> rows]</span><br><span class="line"></span><br><span class="line"><span class="comment"># ============== 模型定义 ==============</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">User</span>(<span class="title class_ inherited__">Model</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;用户模型&quot;&quot;&quot;</span></span><br><span class="line">    <span class="built_in">id</span> = IntegerField(primary_key=<span class="literal">True</span>)</span><br><span class="line">    name = TextField(nullable=<span class="literal">False</span>)</span><br><span class="line">    email = TextField(nullable=<span class="literal">False</span>)</span><br><span class="line">    age = IntegerField(default=<span class="number">0</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Product</span>(<span class="title class_ inherited__">Model</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;产品模型&quot;&quot;&quot;</span></span><br><span class="line">    <span class="built_in">id</span> = IntegerField(primary_key=<span class="literal">True</span>)</span><br><span class="line">    name = TextField(nullable=<span class="literal">False</span>)</span><br><span class="line">    price = FloatField(nullable=<span class="literal">False</span>)</span><br><span class="line">    stock = IntegerField(default=<span class="number">0</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># ============== 使用示例 ==============</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">main</span>():</span><br><span class="line">    <span class="string">&quot;&quot;&quot;演示 ORM 使用&quot;&quot;&quot;</span></span><br><span class="line">    <span class="comment"># 创建内存数据库</span></span><br><span class="line">    conn = sqlite3.connect(<span class="string">&#x27;:memory:&#x27;</span>)</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 建表</span></span><br><span class="line">    conn.execute(User.get_create_sql())</span><br><span class="line">    conn.execute(Product.get_create_sql())</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;✓ 表创建成功\n&quot;</span>)</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 插入数据</span></span><br><span class="line">    user1 = User(<span class="built_in">id</span>=<span class="number">1</span>, name=<span class="string">&quot;Alice&quot;</span>, email=<span class="string">&quot;alice@example.com&quot;</span>, age=<span class="number">25</span>)</span><br><span class="line">    user2 = User(<span class="built_in">id</span>=<span class="number">2</span>, name=<span class="string">&quot;Bob&quot;</span>, email=<span class="string">&quot;bob@example.com&quot;</span>, age=<span class="number">30</span>)</span><br><span class="line">    user1.save(conn)</span><br><span class="line">    user2.save(conn)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;✓ 用户插入成功: <span class="subst">&#123;user1&#125;</span>, <span class="subst">&#123;user2&#125;</span>\n&quot;</span>)</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 插入产品</span></span><br><span class="line">    product1 = Product(<span class="built_in">id</span>=<span class="number">1</span>, name=<span class="string">&quot;Python Book&quot;</span>, price=<span class="number">59.9</span>, stock=<span class="number">100</span>)</span><br><span class="line">    product2 = Product(<span class="built_in">id</span>=<span class="number">2</span>, name=<span class="string">&quot;Docker Guide&quot;</span>, price=<span class="number">39.9</span>, stock=<span class="number">50</span>)</span><br><span class="line">    product1.save(conn)</span><br><span class="line">    product2.save(conn)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;✓ 产品插入成功: <span class="subst">&#123;product1&#125;</span>, <span class="subst">&#123;product2&#125;</span>\n&quot;</span>)</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 查询单个</span></span><br><span class="line">    found_user = User.find_by_id(conn, <span class="number">1</span>)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;✓ 查询用户: <span class="subst">&#123;found_user&#125;</span>\n&quot;</span>)</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 查询所有</span></span><br><span class="line">    all_users = User.find_all(conn)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;✓ 所有用户: <span class="subst">&#123;all_users&#125;</span>\n&quot;</span>)</span><br><span class="line">    </span><br><span class="line">    all_products = Product.find_all(conn)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;✓ 所有产品: <span class="subst">&#123;all_products&#125;</span>\n&quot;</span>)</span><br><span class="line">    </span><br><span class="line">    conn.close()</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    main()</span><br></pre></td></tr></table></figure><h3 id="运行示例"><a href="#运行示例" class="headerlink" title="运行示例"></a>运行示例</h3><figure class="highlight plaintext"><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><span class="line">✓ 表创建成功</span><br><span class="line"></span><br><span class="line">✓ 用户插入成功: User(id=1, name=Alice, email=alice@example.com, age=25), User(id=2, name=Bob, email=bob@example.com, age=30)</span><br><span class="line"></span><br><span class="line">✓ 产品插入成功: Product(id=1, name=Python Book, price=59.9, stock=100), Product(id=2, name=Docker Guide, price=39.9, stock=50)</span><br><span class="line"></span><br><span class="line">✓ 查询用户: User(id=1, name=Alice, email=alice@example.com, age=25)</span><br><span class="line"></span><br><span class="line">✓ 所有用户: [User(id=1, name=Alice, email=alice@example.com, age=25), User(id=2, name=Bob, email=bob@example.com, age=30)]</span><br><span class="line"></span><br><span class="line">✓ 所有产品: [Product(id=1, name=Python Book, price=59.9, stock=100), Product(id=2, name=Docker Guide, price=39.9, stock=50)]</span><br></pre></td></tr></table></figure><h3 id="知识点覆盖"><a href="#知识点覆盖" class="headerlink" title="知识点覆盖"></a>知识点覆盖</h3><table><thead><tr><th>功能</th><th>涉及知识点</th></tr></thead><tbody><tr><td>字段类型</td><td>描述符（<code>__get__</code>、<code>__set__</code>）</td></tr><tr><td>模型元信息</td><td>元类（<code>__new__</code>）、类属性收集</td></tr><tr><td>数据库操作</td><td>魔术方法（<code>__init__</code>、<code>__repr__</code>）</td></tr><tr><td>类型验证</td><td>描述符中的验证逻辑</td></tr><tr><td>继承体系</td><td>基类设计、子类继承</td></tr></tbody></table><h3 id="进阶练习"><a href="#进阶练习" class="headerlink" title="进阶练习"></a>进阶练习</h3><ol><li>添加 <code>update()</code> 和 <code>delete()</code> 方法</li><li>支持外键关联（一对多、多对多）</li><li>添加查询构建器（链式调用）</li><li>支持迁移（自动检测模型变化）</li><li>添加事务支持</li></ol><hr><h2 id="附录：第三阶段自检清单"><a href="#附录：第三阶段自检清单" class="headerlink" title="附录：第三阶段自检清单"></a>附录：第三阶段自检清单</h2><p>在继续学习第四阶段之前，请确保你能：</p><ul><li><input disabled="" type="checkbox"> 解释 <code>__new__</code> 和 <code>__init__</code> 的区别，以及各自的调用时机</li><li><input disabled="" type="checkbox"> 区分实例方法、类方法和静态方法，并说出各自的使用场景</li><li><input disabled="" type="checkbox"> 用 <code>@property</code> 实现带验证的属性访问</li><li><input disabled="" type="checkbox"> 解释 MRO 和 C3 线性化，画出多继承的 MRO 顺序</li><li><input disabled="" type="checkbox"> 说明 <code>super()</code> 在多继承中的行为</li><li><input disabled="" type="checkbox"> 解释鸭子类型和抽象基类的区别，以及各自的使用场景</li><li><input disabled="" type="checkbox"> 实现 <code>__str__</code>、<code>__repr__</code>、<code>__len__</code>、<code>__getitem__</code>、<code>__iter__</code> 等常用魔术方法</li><li><input disabled="" type="checkbox"> 用 <code>__enter__</code> 和 <code>__exit__</code> 实现自定义上下文管理器</li><li><input disabled="" type="checkbox"> 理解元类的作用，并能用 <code>__init_subclass__</code> 替代常见元类场景</li><li><input disabled="" type="checkbox"> 用 <code>dataclass</code> 替代手写 <code>__init__</code> 和 <code>__repr__</code></li><li><input disabled="" type="checkbox"> 手写单例模式、工厂模式和策略模式</li><li><input disabled="" type="checkbox"> 解释 <code>__hash__</code> 和 <code>__eq__</code> 的关系，以及为什么实现 <code>__eq__</code> 后对象不可哈希</li><li><input disabled="" type="checkbox"> 使用 <code>__slots__</code> 优化内存，并说明其限制</li><li><input disabled="" type="checkbox"> 使用 <code>Enum</code> 替代魔术数字</li><li><input disabled="" type="checkbox"> 用描述符实现自定义验证属性</li></ul><hr><blockquote><p><strong>工程师寄语</strong>：Python 的 OOP 是”实用主义”的——它给你足够的工具，但不过度约束。理解魔术方法和协议比记住所有设计模式更重要。当你发现自己在重复写样板代码时，停下来想一想：是不是可以用 <code>dataclass</code>、描述符或元类来简化？</p></blockquote>]]>
    </content>
    <id>https://itdxb.cn/2026/07/22/python/document/%E3%80%90Python%E3%80%91%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E7%BC%96%E7%A8%8B%EF%BC%88%E7%AC%AC%E4%B8%89%E9%98%B6%E6%AE%B5%EF%BC%89/</id>
    <link href="https://itdxb.cn/2026/07/22/python/document/%E3%80%90Python%E3%80%91%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E7%BC%96%E7%A8%8B%EF%BC%88%E7%AC%AC%E4%B8%89%E9%98%B6%E6%AE%B5%EF%BC%89/"/>
    <published>2026-07-22T04:00:00.000Z</published>
    <summary>系统掌握 Python 面向对象编程——类与对象、封装继承多态、魔术方法协议、元类与描述符、dataclass 与经典设计模式。</summary>
    <title>【Python】面向对象编程（第三阶段）</title>
    <updated>2026-07-24T03:19:35.198Z</updated>
  </entry>
  <entry>
    <author>
      <name>Super Bing</name>
    </author>
    <category term="Python" scheme="https://itdxb.cn/categories/Python/"/>
    <category term="教程" scheme="https://itdxb.cn/tags/%E6%95%99%E7%A8%8B/"/>
    <category term="Python" scheme="https://itdxb.cn/tags/Python/"/>
    <category term="函数式编程" scheme="https://itdxb.cn/tags/%E5%87%BD%E6%95%B0%E5%BC%8F%E7%BC%96%E7%A8%8B/"/>
    <content>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><h1 id="二、函数与函数式编程"><a href="#二、函数与函数式编程" class="headerlink" title="二、函数与函数式编程"></a>二、函数与函数式编程</h1><blockquote><p><strong>阶段定位</strong>：函数是 Python 中”一等公民”（First-Class Citizen），理解函数的本质是掌握 Python 高级特性的基石。本阶段从函数定义延伸到函数式编程范式，帮你建立”以函数组合解决问题”的编程思维。</p></blockquote><hr><h2 id="0-本章知识地图"><a href="#0-本章知识地图" class="headerlink" title="0. 本章知识地图"></a>0. 本章知识地图</h2><p>在深入学习之前，先用一张图看清本章的知识脉络：</p><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><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><span class="line">函数基础</span><br><span class="line">├── 函数是对象（赋值、传递、返回、存储）</span><br><span class="line">├── 定义与 return</span><br><span class="line">└── 多返回值与解包</span><br><span class="line"></span><br><span class="line">参数系统</span><br><span class="line">├── 传对象引用</span><br><span class="line">├── 位置参数 / 关键字参数</span><br><span class="line">├── 默认参数陷阱</span><br><span class="line">├── *args / **kwargs</span><br><span class="line">├── 参数顺序规则</span><br><span class="line">└── 类型注解</span><br><span class="line"></span><br><span class="line">作用域</span><br><span class="line">├── LEGB 规则</span><br><span class="line">├── global / nonlocal</span><br><span class="line">└── 为闭包做准备</span><br><span class="line"></span><br><span class="line">函数式工具</span><br><span class="line">├── lambda</span><br><span class="line">├── 高阶函数 map / filter / reduce</span><br><span class="line">├── 闭包</span><br><span class="line">├── 装饰器</span><br><span class="line">├── 递归</span><br><span class="line">└── 生成器</span><br><span class="line"></span><br><span class="line">工程实践</span><br><span class="line">├── 纯函数与副作用</span><br><span class="line">├── functools 模块</span><br><span class="line">└── itertools 模块</span><br></pre></td></tr></table></figure><blockquote><p>配图占位：建议在这里放一张”Python 函数与函数式编程知识地图”思维导图，文件路径 <code>/images/python/phase2_mindmap.png</code>。</p></blockquote><hr><h2 id="1-函数定义与调用"><a href="#1-函数定义与调用" class="headerlink" title="1. 函数定义与调用"></a>1. 函数定义与调用</h2><h3 id="1-1-函数的本质：一切都是对象"><a href="#1-1-函数的本质：一切都是对象" class="headerlink" title="1.1 函数的本质：一切都是对象"></a>1.1 函数的本质：一切都是对象</h3><p>在 Python 中，<strong>函数是对象</strong>。这意味着函数可以：</p><ul><li>赋值给变量</li><li>作为参数传递给其他函数</li><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">greet</span>(<span class="params">name</span>):</span><br><span class="line">    <span class="keyword">return</span> <span class="string">f&quot;Hello, <span class="subst">&#123;name&#125;</span>!&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 函数是对象，可以赋值</span></span><br><span class="line">say_hi = greet</span><br><span class="line"><span class="built_in">print</span>(say_hi(<span class="string">&quot;Alice&quot;</span>))   <span class="comment"># Hello, Alice!</span></span><br><span class="line"><span class="built_in">print</span>(say_hi <span class="keyword">is</span> greet)   <span class="comment"># True —— 指向同一个函数对象</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 函数可以存储在字典中（策略模式的基础）</span></span><br><span class="line">operations = &#123;</span><br><span class="line">    <span class="string">&quot;add&quot;</span>: <span class="keyword">lambda</span> a, b: a + b,</span><br><span class="line">    <span class="string">&quot;mul&quot;</span>: <span class="keyword">lambda</span> a, b: a * b,</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">print</span>(operations[<span class="string">&quot;add&quot;</span>](<span class="number">3</span>, <span class="number">4</span>))   <span class="comment"># 7</span></span><br></pre></td></tr></table></figure><p><strong>函数对象的重要属性</strong>：</p><table><thead><tr><th>属性</th><th>含义</th><th>示例</th></tr></thead><tbody><tr><td><code>__name__</code></td><td>函数名</td><td><code>greet.__name__</code> → <code>&#39;greet&#39;</code></td></tr><tr><td><code>__doc__</code></td><td>文档字符串</td><td><code>greet.__doc__</code></td></tr><tr><td><code>__defaults__</code></td><td>默认参数值元组</td><td>用于调试</td></tr><tr><td><code>__code__</code></td><td>代码对象</td><td>包含字节码、局部变量名等</td></tr><tr><td><code>__call__</code></td><td>使对象可被调用</td><td><code>greet()</code> 等价于 <code>greet.__call__(...)</code></td></tr></tbody></table><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> inspect</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">sample</span>(<span class="params">a, b=<span class="number">10</span>, *args, c=<span class="number">20</span>, **kwargs</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;示例函数。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(sample.__name__)              <span class="comment"># sample</span></span><br><span class="line"><span class="built_in">print</span>(sample.__doc__)               <span class="comment"># 示例函数。</span></span><br><span class="line"><span class="built_in">print</span>(sample.__defaults__)          <span class="comment"># (10,)</span></span><br><span class="line"><span class="built_in">print</span>(sample.__kwdefaults__)        <span class="comment"># &#123;&#x27;c&#x27;: 20&#125;</span></span><br><span class="line"><span class="built_in">print</span>(inspect.signature(sample))    <span class="comment"># (a, b=10, *args, c=20, **kwargs)</span></span><br></pre></td></tr></table></figure><p><strong>面试真题</strong>：Python 中函数是”一等公民”体现在哪些方面？</p><h3 id="1-2-函数定义与-return"><a href="#1-2-函数定义与-return" class="headerlink" title="1.2 函数定义与 return"></a>1.2 函数定义与 return</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><span class="line"><span class="keyword">def</span> <span class="title function_">calculate</span>(<span class="params">x, y, op=<span class="string">&quot;add&quot;</span></span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;执行基础运算。</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">    Args:</span></span><br><span class="line"><span class="string">        x: 左操作数</span></span><br><span class="line"><span class="string">        y: 右操作数</span></span><br><span class="line"><span class="string">        op: 操作类型，可选 &quot;add&quot; | &quot;sub&quot; | &quot;mul&quot; | &quot;div&quot;</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">    Returns:</span></span><br><span class="line"><span class="string">        运算结果</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">    Raises:</span></span><br><span class="line"><span class="string">        ValueError: 当 op 不支持时</span></span><br><span class="line"><span class="string">        ZeroDivisionError: 除零时</span></span><br><span class="line"><span class="string">    &quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">if</span> op == <span class="string">&quot;add&quot;</span>:</span><br><span class="line">        <span class="keyword">return</span> x + y</span><br><span class="line">    <span class="keyword">if</span> op == <span class="string">&quot;sub&quot;</span>:</span><br><span class="line">        <span class="keyword">return</span> x - y</span><br><span class="line">    <span class="keyword">if</span> op == <span class="string">&quot;mul&quot;</span>:</span><br><span class="line">        <span class="keyword">return</span> x * y</span><br><span class="line">    <span class="keyword">if</span> op == <span class="string">&quot;div&quot;</span>:</span><br><span class="line">        <span class="keyword">return</span> x / y</span><br><span class="line">    <span class="keyword">raise</span> ValueError(<span class="string">f&quot;不支持的操作: <span class="subst">&#123;op&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 没有显式 return 的函数，默认返回 None</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">log_message</span>(<span class="params">msg</span>):</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;[LOG] <span class="subst">&#123;msg&#125;</span>&quot;</span>)</span><br><span class="line">    <span class="comment"># 隐式 return None</span></span><br><span class="line"></span><br><span class="line">result = log_message(<span class="string">&quot;test&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(result)   <span class="comment"># None</span></span><br></pre></td></tr></table></figure><p><strong>文档字符串规范（Google Style）</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">fetch_user</span>(<span class="params">user_id, include_deleted=<span class="literal">False</span></span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;根据用户 ID 获取用户信息。</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">    Args:</span></span><br><span class="line"><span class="string">        user_id: 用户唯一标识，正整数。</span></span><br><span class="line"><span class="string">        include_deleted: 是否包含已删除用户，默认为 False。</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">    Returns:</span></span><br><span class="line"><span class="string">        dict: 用户信息字典；未找到时返回 None。</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">    Raises:</span></span><br><span class="line"><span class="string">        ValueError: user_id 不是正整数时。</span></span><br><span class="line"><span class="string">    &quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> <span class="built_in">isinstance</span>(user_id, <span class="built_in">int</span>) <span class="keyword">or</span> user_id &lt;= <span class="number">0</span>:</span><br><span class="line">        <span class="keyword">raise</span> ValueError(<span class="string">&quot;user_id 必须是正整数&quot;</span>)</span><br><span class="line">    <span class="comment"># ...</span></span><br></pre></td></tr></table></figure><p><strong>工程建议</strong>：</p><ul><li>函数只做一件事（Single Responsibility），过长时考虑拆分。</li><li>函数名应当是动词或动词短语：<code>get_user()</code>、<code>validate_email()</code>、<code>send_notification()</code>。</li><li>返回 <code>None</code> 要明确，不要依赖隐式返回。</li><li>避免使用隐式返回作为有意义的业务结果。</li></ul><h3 id="1-3-多返回值与解包"><a href="#1-3-多返回值与解包" class="headerlink" title="1.3 多返回值与解包"></a>1.3 多返回值与解包</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">get_min_max</span>(<span class="params">numbers</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;同时返回最小值和最大值。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> numbers:</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">None</span>, <span class="literal">None</span>      <span class="comment"># 多返回值本质是返回一个元组</span></span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">min</span>(numbers), <span class="built_in">max</span>(numbers)</span><br><span class="line"></span><br><span class="line">minimum, maximum = get_min_max([<span class="number">3</span>, <span class="number">1</span>, <span class="number">4</span>, <span class="number">1</span>, <span class="number">5</span>, <span class="number">9</span>])</span><br><span class="line"><span class="built_in">print</span>(minimum, maximum)   <span class="comment"># 1 9</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 也可以只取其中一个</span></span><br><span class="line">min_val, _ = get_min_max([<span class="number">3</span>, <span class="number">1</span>, <span class="number">4</span>])   <span class="comment"># _ 表示&quot;忽略这个值&quot;</span></span><br></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><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><span class="line"><span class="comment"># 扩展解包（3.0+）</span></span><br><span class="line">first, *middle, last = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>]</span><br><span class="line"><span class="built_in">print</span>(first, middle, last)   <span class="comment"># 1 [2, 3, 4] 5</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 嵌套解包</span></span><br><span class="line">(a, b), (c, d) = (<span class="number">1</span>, <span class="number">2</span>), (<span class="number">3</span>, <span class="number">4</span>)</span><br><span class="line"><span class="built_in">print</span>(a, b, c, d)            <span class="comment"># 1 2 3 4</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用 namedtuple 让多返回值更可读</span></span><br><span class="line"><span class="keyword">from</span> collections <span class="keyword">import</span> namedtuple</span><br><span class="line"></span><br><span class="line">Point = namedtuple(<span class="string">&quot;Point&quot;</span>, [<span class="string">&quot;x&quot;</span>, <span class="string">&quot;y&quot;</span>])</span><br><span class="line"><span class="keyword">def</span> <span class="title function_">get_origin</span>():</span><br><span class="line">    <span class="keyword">return</span> Point(<span class="number">0</span>, <span class="number">0</span>)</span><br><span class="line"></span><br><span class="line">origin = get_origin()</span><br><span class="line"><span class="built_in">print</span>(origin.x, origin.y)    <span class="comment"># 0 0</span></span><br></pre></td></tr></table></figure><hr><h2 id="2-参数系统（面试高频）"><a href="#2-参数系统（面试高频）" class="headerlink" title="2. 参数系统（面试高频）"></a>2. 参数系统（面试高频）</h2><h3 id="2-1-Python-的传参机制：传对象引用"><a href="#2-1-Python-的传参机制：传对象引用" class="headerlink" title="2.1 Python 的传参机制：传对象引用"></a>2.1 Python 的传参机制：传对象引用</h3><p>Python 的参数传递机制是**”传对象引用”**（Pass by Object Reference）—— 传入的是对象的引用，而非值本身。</p><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></pre></td><td class="code"><pre><span class="line">调用函数时：</span><br><span class="line">  实参变量 ──→ 对象 ←── 形参变量</span><br><span class="line">              （同一个对象）</span><br></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><span class="line"><span class="keyword">def</span> <span class="title function_">try_change</span>(<span class="params">x</span>):</span><br><span class="line">    x = <span class="number">100</span>          <span class="comment"># 让 x 指向新的整数对象 100</span></span><br><span class="line"></span><br><span class="line">a = <span class="number">1</span></span><br><span class="line">try_change(a)</span><br><span class="line"><span class="built_in">print</span>(a)             <span class="comment"># 1 —— 不变，因为整数不可变</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">append_one</span>(<span class="params">lst</span>):</span><br><span class="line">    lst.append(<span class="number">1</span>)    <span class="comment"># 在原地修改列表对象</span></span><br><span class="line"></span><br><span class="line">my_list = [<span class="number">0</span>]</span><br><span class="line">append_one(my_list)</span><br><span class="line"><span class="built_in">print</span>(my_list)       <span class="comment"># [0, 1] —— 变了，因为列表可变</span></span><br></pre></td></tr></table></figure><p><strong>判断口诀</strong>：</p><ul><li>函数内对参数<strong>重新赋值</strong>，不影响外部（指向了新对象）。</li><li>函数内对参数<strong>原地修改</strong>，会影响外部（同一对象被修改）。</li></ul><p><strong>面试真题</strong>：Python 是值传递还是引用传递？</p><blockquote><p>答：都不是，Python 是”传对象引用”。对于不可变对象（int、str、tuple），效果类似值传递；对于可变对象（list、dict），效果类似引用传递。</p></blockquote><h3 id="2-2-参数类型总览"><a href="#2-2-参数类型总览" class="headerlink" title="2.2 参数类型总览"></a>2.2 参数类型总览</h3><p>Python 3.8+ 支持完整的参数分类：</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><span class="line"><span class="keyword">def</span> <span class="title function_">demo</span>(<span class="params">pos, /, pos_or_kw, *, kw_only, **kwargs</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;参数分类演示（3.8+ 语法）。</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">    pos:        仅限位置参数（/ 左侧）</span></span><br><span class="line"><span class="string">    pos_or_kw:  位置或关键字参数（/ 和 * 之间）</span></span><br><span class="line"><span class="string">    kw_only:    仅限关键字参数（* 右侧）</span></span><br><span class="line"><span class="string">    **kwargs:   接收多余关键字参数</span></span><br><span class="line"><span class="string">    &quot;&quot;&quot;</span></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;pos=<span class="subst">&#123;pos&#125;</span>, pos_or_kw=<span class="subst">&#123;pos_or_kw&#125;</span>, kw_only=<span class="subst">&#123;kw_only&#125;</span>, kwargs=<span class="subst">&#123;kwargs&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 正确调用</span></span><br><span class="line">demo(<span class="number">1</span>, <span class="number">2</span>, kw_only=<span class="number">3</span>, extra=<span class="string">&quot;x&quot;</span>)</span><br><span class="line"><span class="comment"># pos=1, pos_or_kw=2, kw_only=3, kwargs=&#123;&#x27;extra&#x27;: &#x27;x&#x27;&#125;</span></span><br><span class="line"></span><br><span class="line">demo(<span class="number">1</span>, pos_or_kw=<span class="number">2</span>, kw_only=<span class="number">3</span>)</span><br><span class="line"><span class="comment"># pos=1, pos_or_kw=2, kw_only=3, kwargs=&#123;&#125;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 错误调用</span></span><br><span class="line">demo(pos=<span class="number">1</span>, pos_or_kw=<span class="number">2</span>, kw_only=<span class="number">3</span>)</span><br><span class="line"><span class="comment"># TypeError: pos 是仅限位置参数</span></span><br></pre></td></tr></table></figure><p><strong>参数分类速查表</strong>：</p><table><thead><tr><th>参数类型</th><th>语法位置</th><th>调用方式</th><th>典型用途</th></tr></thead><tbody><tr><td>仅限位置</td><td><code>/</code> 左侧</td><td>只能位置</td><td>内部参数、避免破坏兼容性</td></tr><tr><td>位置或关键字</td><td><code>/</code> 和 <code>*</code> 之间</td><td>任意</td><td>最常见的参数</td></tr><tr><td>仅限关键字</td><td><code>*</code> 右侧</td><td>只能关键字</td><td>提高可读性、明确语义</td></tr><tr><td><code>*args</code></td><td>接收多余位置</td><td>自动打包</td><td>变长参数、转发</td></tr><tr><td><code>**kwargs</code></td><td>接收多余关键字</td><td>自动打包</td><td>变长参数、配置转发</td></tr></tbody></table><blockquote><p>配图占位：建议放一张”Python 参数类型示意图”，文件路径 <code>/images/python/phase2_parameters.png</code>。</p></blockquote><h3 id="2-3-位置参数与关键字参数"><a href="#2-3-位置参数与关键字参数" class="headerlink" title="2.3 位置参数与关键字参数"></a>2.3 位置参数与关键字参数</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><span class="line"><span class="keyword">def</span> <span class="title function_">create_user</span>(<span class="params">name, age, active=<span class="literal">True</span>, role=<span class="string">&quot;user&quot;</span></span>):</span><br><span class="line">    <span class="keyword">return</span> &#123;<span class="string">&quot;name&quot;</span>: name, <span class="string">&quot;age&quot;</span>: age, <span class="string">&quot;active&quot;</span>: active, <span class="string">&quot;role&quot;</span>: role&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 位置调用（顺序必须正确）</span></span><br><span class="line">create_user(<span class="string">&quot;Alice&quot;</span>, <span class="number">30</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 关键字调用（顺序无关，可读性更好）</span></span><br><span class="line">create_user(name=<span class="string">&quot;Bob&quot;</span>, age=<span class="number">25</span>, role=<span class="string">&quot;admin&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 混合调用：位置参数必须在关键字参数之前</span></span><br><span class="line">create_user(<span class="string">&quot;Carol&quot;</span>, <span class="number">28</span>, role=<span class="string">&quot;moderator&quot;</span>)   <span class="comment"># 正确</span></span><br><span class="line">create_user(name=<span class="string">&quot;Dave&quot;</span>, <span class="number">35</span>)                  <span class="comment"># 正确（但可读性一般，不推荐）</span></span><br><span class="line"><span class="comment"># create_user(age=40, &quot;Eve&quot;)                  # SyntaxError！位置参数不能在关键字参数后</span></span><br></pre></td></tr></table></figure><p><strong>工程建议</strong>：调用函数时，超过 2-3 个参数尽量使用关键字参数，提高可读性：</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><span class="line"><span class="comment"># 糟糕：不知道每个数字的含义</span></span><br><span class="line">set_timeout(<span class="number">30</span>, <span class="literal">True</span>, <span class="literal">False</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 优秀：一目了然</span></span><br><span class="line">set_timeout(seconds=<span class="number">30</span>, retry=<span class="literal">True</span>, fail_silent=<span class="literal">False</span>)</span><br></pre></td></tr></table></figure><h3 id="2-4-默认参数值"><a href="#2-4-默认参数值" class="headerlink" title="2.4 默认参数值"></a>2.4 默认参数值</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">connect</span>(<span class="params">host, port=<span class="number">3306</span>, timeout=<span class="number">30</span></span>):</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;Connecting to <span class="subst">&#123;host&#125;</span>:<span class="subst">&#123;port&#125;</span>, timeout=<span class="subst">&#123;timeout&#125;</span>s&quot;</span>)</span><br><span class="line"></span><br><span class="line">connect(<span class="string">&quot;localhost&quot;</span>)                    <span class="comment"># 使用全部默认值</span></span><br><span class="line">connect(<span class="string">&quot;localhost&quot;</span>, <span class="number">5432</span>)              <span class="comment"># 覆盖 port</span></span><br><span class="line">connect(<span class="string">&quot;localhost&quot;</span>, timeout=<span class="number">60</span>)        <span class="comment"># 只覆盖 timeout（关键字参数）</span></span><br></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><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><span class="line"><span class="comment"># 错误示范（面试必考）</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">append_item</span>(<span class="params">item, items=[]</span>):</span><br><span class="line">    items.append(item)</span><br><span class="line">    <span class="keyword">return</span> items</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(append_item(<span class="number">1</span>))   <span class="comment"># [1]</span></span><br><span class="line"><span class="built_in">print</span>(append_item(<span class="number">2</span>))   <span class="comment"># [1, 2] —— 默认参数在函数定义时求值，items 被共享了！</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 正确做法</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">append_item_safe</span>(<span class="params">item, items=<span class="literal">None</span></span>):</span><br><span class="line">    <span class="keyword">if</span> items <span class="keyword">is</span> <span class="literal">None</span>:</span><br><span class="line">        items = []</span><br><span class="line">    items.append(item)</span><br><span class="line">    <span class="keyword">return</span> items</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(append_item_safe(<span class="number">1</span>))   <span class="comment"># [1]</span></span><br><span class="line"><span class="built_in">print</span>(append_item_safe(<span class="number">2</span>))   <span class="comment"># [2] —— 每次调用都是独立的列表</span></span><br></pre></td></tr></table></figure><p><strong>原理剖析</strong>：</p><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></pre></td><td class="code"><pre><span class="line">函数定义时：</span><br><span class="line">  def append_item(item, items=[]) -&gt; 创建一个空列表对象 L</span><br><span class="line"></span><br><span class="line">第一次 append_item(1)：</span><br><span class="line">  items 指向 L，L 变为 [1]</span><br><span class="line"></span><br><span class="line">第二次 append_item(2)：</span><br><span class="line">  items 仍然指向 L，L 变为 [1, 2]</span><br></pre></td></tr></table></figure><p>默认参数值在函数定义时（模块加载时）求值一次，后续调用复用同一个对象。不可变类型（<code>int</code>、<code>str</code>、<code>tuple</code>）作为默认值是安全的，但可变类型（<code>list</code>、<code>dict</code>、<code>set</code>）必须使用 <code>None</code> 作为占位符。</p><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">add_end</span>(<span class="params">L=[]</span>):</span><br><span class="line">    L.append(<span class="string">&quot;END&quot;</span>)</span><br><span class="line">    <span class="keyword">return</span> L</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(add_end())</span><br><span class="line"><span class="built_in">print</span>(add_end())</span><br></pre></td></tr></table></figure><blockquote><p>答：两次都输出 <code>[&#39;END&#39;, &#39;END&#39;]</code>，因为默认参数在定义时求值，列表被所有调用共享。</p></blockquote><h3 id="2-5-可变参数：-args-与-kwargs"><a href="#2-5-可变参数：-args-与-kwargs" class="headerlink" title="2.5 可变参数：*args 与 **kwargs"></a>2.5 可变参数：<code>*args</code> 与 <code>**kwargs</code></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><span class="line"><span class="keyword">def</span> <span class="title function_">flexible</span>(<span class="params">*args, **kwargs</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;*args 接收多余的位置参数，打包成元组。</span></span><br><span class="line"><span class="string">       **kwargs 接收多余的关键字参数，打包成字典。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;位置参数: <span class="subst">&#123;args&#125;</span>&quot;</span>)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;关键字参数: <span class="subst">&#123;kwargs&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line">flexible(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, name=<span class="string">&quot;Alice&quot;</span>, age=<span class="number">30</span>)</span><br><span class="line"><span class="comment"># 位置参数: (1, 2, 3)</span></span><br><span class="line"><span class="comment"># 关键字参数: &#123;&#x27;name&#x27;: &#x27;Alice&#x27;, &#x27;age&#x27;: 30&#125;</span></span><br></pre></td></tr></table></figure><p><strong>拆包（Unpacking）调用</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><span class="line"><span class="keyword">def</span> <span class="title function_">sum_three</span>(<span class="params">a, b, c</span>):</span><br><span class="line">    <span class="keyword">return</span> a + b + c</span><br><span class="line"></span><br><span class="line">nums = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]</span><br><span class="line"><span class="built_in">print</span>(sum_three(*nums))   <span class="comment"># 6，等价于 sum_three(1, 2, 3)</span></span><br><span class="line"></span><br><span class="line">config = &#123;<span class="string">&quot;a&quot;</span>: <span class="number">1</span>, <span class="string">&quot;b&quot;</span>: <span class="number">2</span>, <span class="string">&quot;c&quot;</span>: <span class="number">3</span>&#125;</span><br><span class="line"><span class="built_in">print</span>(sum_three(**config))   <span class="comment"># 6，等价于 sum_three(a=1, b=2, c=3)</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 组合拆包</span></span><br><span class="line">data = [<span class="number">1</span>, <span class="number">2</span>]</span><br><span class="line"><span class="built_in">print</span>(sum_three(*data, c=<span class="number">3</span>))   <span class="comment"># 6</span></span><br></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><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><span class="line"><span class="keyword">import</span> logging</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">logged</span>(<span class="params">func</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;装饰器：记录函数调用日志（后续会详细讲解装饰器）。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">wrapper</span>(<span class="params">*args, **kwargs</span>):</span><br><span class="line">        logging.info(<span class="string">f&quot;Calling <span class="subst">&#123;func.__name__&#125;</span> with args=<span class="subst">&#123;args&#125;</span>, kwargs=<span class="subst">&#123;kwargs&#125;</span>&quot;</span>)</span><br><span class="line">        <span class="keyword">return</span> func(*args, **kwargs)</span><br><span class="line">    <span class="keyword">return</span> wrapper</span><br><span class="line"></span><br><span class="line"><span class="meta">@logged</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">compute</span>(<span class="params">x, y, operation=<span class="string">&quot;add&quot;</span></span>):</span><br><span class="line">    <span class="keyword">if</span> operation == <span class="string">&quot;add&quot;</span>:</span><br><span class="line">        <span class="keyword">return</span> x + y</span><br><span class="line">    <span class="keyword">return</span> x - y</span><br><span class="line"></span><br><span class="line">compute(<span class="number">10</span>, <span class="number">5</span>, operation=<span class="string">&quot;sub&quot;</span>)</span><br><span class="line"><span class="comment"># 日志: Calling compute with args=(10, 5), kwargs=&#123;&#x27;operation&#x27;: &#x27;sub&#x27;&#125;</span></span><br></pre></td></tr></table></figure><h3 id="2-6-参数顺序规则（必须遵守）"><a href="#2-6-参数顺序规则（必须遵守）" class="headerlink" title="2.6 参数顺序规则（必须遵守）"></a>2.6 参数顺序规则（必须遵守）</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">correct</span>(<span class="params">a, b=<span class="number">2</span>, /, c=<span class="number">3</span>, d=<span class="number">4</span>, *, e=<span class="number">5</span>, f=<span class="number">6</span></span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;正确顺序：</span></span><br><span class="line"><span class="string">    1. 仅限位置参数（可有默认值）</span></span><br><span class="line"><span class="string">    2. / 分隔符</span></span><br><span class="line"><span class="string">    3. 位置或关键字参数（可有默认值）</span></span><br><span class="line"><span class="string">    4. * 分隔符</span></span><br><span class="line"><span class="string">    5. 仅限关键字参数（可有默认值）</span></span><br><span class="line"><span class="string">    6. **kwargs</span></span><br><span class="line"><span class="string">    &quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 常见错误</span></span><br><span class="line"><span class="comment"># def wrong(a=1, b):        # SyntaxError: 有默认值的参数不能放在无默认值参数前</span></span><br><span class="line"><span class="comment"># def wrong2(*args, a):     # SyntaxError: *args 后的普通参数必须是仅限关键字</span></span><br></pre></td></tr></table></figure><h3 id="2-7-类型注解（Type-Hints）"><a href="#2-7-类型注解（Type-Hints）" class="headerlink" title="2.7 类型注解（Type Hints）"></a>2.7 类型注解（Type Hints）</h3><p>Python 3.5+ 支持类型注解，虽然运行时不强制，但能显著提升代码可维护性。</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><span class="line"><span class="keyword">from</span> typing <span class="keyword">import</span> <span class="type">List</span>, <span class="type">Dict</span>, <span class="type">Optional</span>, <span class="type">Union</span>, <span class="type">Callable</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">greet</span>(<span class="params">name: <span class="built_in">str</span>, times: <span class="built_in">int</span> = <span class="number">1</span></span>) -&gt; <span class="built_in">str</span>:</span><br><span class="line">    <span class="keyword">return</span> (<span class="string">f&quot;Hello, <span class="subst">&#123;name&#125;</span>!\n&quot;</span>) * times</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">process_users</span>(<span class="params">users: <span class="type">List</span>[<span class="type">Dict</span>[<span class="built_in">str</span>, <span class="type">Union</span>[<span class="built_in">str</span>, <span class="built_in">int</span>]]]</span>) -&gt; <span class="type">List</span>[<span class="built_in">str</span>]:</span><br><span class="line">    <span class="keyword">return</span> [user[<span class="string">&quot;name&quot;</span>] <span class="keyword">for</span> user <span class="keyword">in</span> users <span class="keyword">if</span> user.get(<span class="string">&quot;active&quot;</span>)]</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">find_user</span>(<span class="params">user_id: <span class="built_in">int</span></span>) -&gt; <span class="type">Optional</span>[<span class="type">Dict</span>]:</span><br><span class="line">    <span class="comment"># 可能返回 None</span></span><br><span class="line">    <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">apply_operation</span>(<span class="params">a: <span class="built_in">int</span>, b: <span class="built_in">int</span>, op: <span class="type">Callable</span>[[<span class="built_in">int</span>, <span class="built_in">int</span>], <span class="built_in">int</span>]</span>) -&gt; <span class="built_in">int</span>:</span><br><span class="line">    <span class="keyword">return</span> op(a, b)</span><br></pre></td></tr></table></figure><p><strong>常用类型注解速查</strong>：</p><table><thead><tr><th>类型</th><th>含义</th><th>示例</th></tr></thead><tbody><tr><td><code>int</code>, <code>str</code>, <code>float</code>, <code>bool</code></td><td>基本类型</td><td><code>x: int</code></td></tr><tr><td><code>List[T]</code></td><td>列表</td><td><code>items: List[int]</code></td></tr><tr><td><code>Dict[K, V]</code></td><td>字典</td><td><code>config: Dict[str, Any]</code></td></tr><tr><td><code>Optional[T]</code></td><td>T 或 None</td><td><code>name: Optional[str]</code></td></tr><tr><td><code>Union[T1, T2]</code></td><td>多种类型之一</td><td><code>value: Union[int, str]</code></td></tr><tr><td><code>Callable[[Arg], Ret]</code></td><td>可调用对象</td><td><code>fn: Callable[[int], str]</code></td></tr><tr><td><code>Any</code></td><td>任意类型</td><td><code>data: Any</code></td></tr></tbody></table><p><strong>面试真题</strong>：Python 类型注解在运行时会检查吗？</p><blockquote><p>答：不会。类型注解主要用于静态类型检查工具（如 mypy）、IDE 提示和文档。运行时不会进行类型强制校验。</p></blockquote><hr><h2 id="3-作用域与-LEGB-规则"><a href="#3-作用域与-LEGB-规则" class="headerlink" title="3. 作用域与 LEGB 规则"></a>3. 作用域与 LEGB 规则</h2><h3 id="3-1-命名空间与作用域层级"><a href="#3-1-命名空间与作用域层级" class="headerlink" title="3.1 命名空间与作用域层级"></a>3.1 命名空间与作用域层级</h3><p>Python 查找变量的顺序遵循 <strong>LEGB 规则</strong>：</p><table><thead><tr><th>层级</th><th>含义</th><th>范围</th></tr></thead><tbody><tr><td><strong>L</strong>ocal</td><td>局部作用域</td><td>当前函数内部</td></tr><tr><td><strong>E</strong>nclosing</td><td>嵌套作用域</td><td>外层（闭包）函数</td></tr><tr><td><strong>G</strong>lobal</td><td>全局作用域</td><td>当前模块顶层</td></tr><tr><td><strong>B</strong>uilt-in</td><td>内置作用域</td><td><code>builtins</code> 模块</td></tr></tbody></table><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><span class="line">x = <span class="string">&quot;global&quot;</span>          <span class="comment"># G: 全局作用域</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">outer</span>():</span><br><span class="line">    x = <span class="string">&quot;enclosing&quot;</span>   <span class="comment"># E: 嵌套作用域</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">inner</span>():</span><br><span class="line">        x = <span class="string">&quot;local&quot;</span>   <span class="comment"># L: 局部作用域</span></span><br><span class="line">        <span class="built_in">print</span>(x)      <span class="comment"># 查找顺序: L -&gt; E -&gt; G -&gt; B，输出 &quot;local&quot;</span></span><br><span class="line"></span><br><span class="line">    inner()</span><br><span class="line"></span><br><span class="line">outer()</span><br></pre></td></tr></table></figure><blockquote><p>配图占位：建议放一张”LEGB 作用域查找顺序”示意图，文件路径 <code>/images/python/phase2_legb.png</code>。</p></blockquote><h3 id="3-2-global-声明"><a href="#3-2-global-声明" class="headerlink" title="3.2 global 声明"></a>3.2 <code>global</code> 声明</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></pre></td><td class="code"><pre><span class="line">counter = <span class="number">0</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">increment</span>():</span><br><span class="line">    <span class="keyword">global</span> counter      <span class="comment"># 声明：我要修改全局变量 counter</span></span><br><span class="line">    counter += <span class="number">1</span></span><br><span class="line"></span><br><span class="line">increment()</span><br><span class="line"><span class="built_in">print</span>(counter)          <span class="comment"># 1</span></span><br></pre></td></tr></table></figure><p><strong>工程建议</strong>：</p><ul><li>尽量避免使用 <code>global</code>，它破坏函数的纯粹性和可测试性。</li><li>需要共享状态时，考虑使用类封装或使用闭包。</li></ul><h3 id="3-3-nonlocal-声明"><a href="#3-3-nonlocal-声明" class="headerlink" title="3.3 nonlocal 声明"></a>3.3 <code>nonlocal</code> 声明</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><span class="line"><span class="keyword">def</span> <span class="title function_">make_counter</span>():</span><br><span class="line">    count = <span class="number">0</span>           <span class="comment"># E: 外层（闭包）变量</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">increment</span>():</span><br><span class="line">        <span class="keyword">nonlocal</span> count  <span class="comment"># 声明：我要修改外层（非全局）变量</span></span><br><span class="line">        count += <span class="number">1</span></span><br><span class="line">        <span class="keyword">return</span> count</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> increment</span><br><span class="line"></span><br><span class="line">counter_a = make_counter()</span><br><span class="line"><span class="built_in">print</span>(counter_a())      <span class="comment"># 1</span></span><br><span class="line"><span class="built_in">print</span>(counter_a())      <span class="comment"># 2</span></span><br><span class="line"></span><br><span class="line">counter_b = make_counter()</span><br><span class="line"><span class="built_in">print</span>(counter_b())      <span class="comment"># 1 —— 独立的计数器</span></span><br></pre></td></tr></table></figure><p><strong><code>global</code> vs <code>nonlocal</code> 对比</strong>：</p><table><thead><tr><th>特性</th><th><code>global</code></th><th><code>nonlocal</code></th></tr></thead><tbody><tr><td>修改目标</td><td>模块级全局变量</td><td>外层（闭包）函数的局部变量</td></tr><tr><td>使用场景</td><td>全局状态管理（不推荐）</td><td>闭包状态保持</td></tr><tr><td>报错条件</td><td>变量不存在时创建</td><td>变量必须已存在在外层作用域</td></tr></tbody></table><h3 id="3-4-locals-与-globals"><a href="#3-4-locals-与-globals" class="headerlink" title="3.4 locals() 与 globals()"></a>3.4 <code>locals()</code> 与 <code>globals()</code></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></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">show_scope</span>():</span><br><span class="line">    x = <span class="number">10</span></span><br><span class="line">    <span class="built_in">print</span>(<span class="built_in">locals</span>())    <span class="comment"># &#123;&#x27;x&#x27;: 10&#125;</span></span><br><span class="line"></span><br><span class="line">show_scope()</span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">globals</span>().keys())  <span class="comment"># 包含全局变量、导入的模块等</span></span><br></pre></td></tr></table></figure><p><strong>注意事项</strong>：</p><ul><li><code>locals()</code> 返回的字典不要修改，行为不可靠。</li><li><code>globals()</code> 返回的字典可以修改，但不推荐。</li></ul><hr><h2 id="4-匿名函数-lambda"><a href="#4-匿名函数-lambda" class="headerlink" title="4. 匿名函数 lambda"></a>4. 匿名函数 <code>lambda</code></h2><h3 id="4-1-基本语法与使用场景"><a href="#4-1-基本语法与使用场景" class="headerlink" title="4.1 基本语法与使用场景"></a>4.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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 基本语法：lambda 参数: 表达式</span></span><br><span class="line">square = <span class="keyword">lambda</span> x: x ** <span class="number">2</span></span><br><span class="line"><span class="built_in">print</span>(square(<span class="number">5</span>))        <span class="comment"># 25</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 多参数</span></span><br><span class="line">add = <span class="keyword">lambda</span> x, y: x + y</span><br><span class="line"><span class="built_in">print</span>(add(<span class="number">3</span>, <span class="number">4</span>))        <span class="comment"># 7</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 实际场景：作为排序/映射的短回调</span></span><br><span class="line">users = [</span><br><span class="line">    &#123;<span class="string">&quot;name&quot;</span>: <span class="string">&quot;Alice&quot;</span>, <span class="string">&quot;age&quot;</span>: <span class="number">30</span>&#125;,</span><br><span class="line">    &#123;<span class="string">&quot;name&quot;</span>: <span class="string">&quot;Bob&quot;</span>, <span class="string">&quot;age&quot;</span>: <span class="number">25</span>&#125;,</span><br><span class="line">    &#123;<span class="string">&quot;name&quot;</span>: <span class="string">&quot;Carol&quot;</span>, <span class="string">&quot;age&quot;</span>: <span class="number">35</span>&#125;,</span><br><span class="line">]</span><br><span class="line">users.sort(key=<span class="keyword">lambda</span> u: u[<span class="string">&quot;age&quot;</span>])   <span class="comment"># 按年龄排序</span></span><br><span class="line"><span class="built_in">print</span>([u[<span class="string">&quot;name&quot;</span>] <span class="keyword">for</span> u <span class="keyword">in</span> users])    <span class="comment"># [&#x27;Bob&#x27;, &#x27;Alice&#x27;, &#x27;Carol&#x27;]</span></span><br></pre></td></tr></table></figure><h3 id="4-2-lambda-的限制"><a href="#4-2-lambda-的限制" class="headerlink" title="4.2 lambda 的限制"></a>4.2 lambda 的限制</h3><ul><li>只能包含一个表达式，不能写语句（如赋值、循环）。</li><li>没有文档字符串，调试时显示为 <code>&lt;lambda&gt;</code>。</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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 错误的 lambda</span></span><br><span class="line"><span class="comment"># lambda x: if x &gt; 0: return x   # SyntaxError</span></span><br><span class="line"><span class="comment"># lambda x: y = x + 1            # SyntaxError，不能赋值</span></span><br></pre></td></tr></table></figure><h3 id="4-3-lambda-vs-def"><a href="#4-3-lambda-vs-def" class="headerlink" title="4.3 lambda vs def"></a>4.3 lambda vs def</h3><table><thead><tr><th>特性</th><th>lambda</th><th>def</th></tr></thead><tbody><tr><td>名称</td><td>匿名</td><td>有名称</td></tr><tr><td>复杂度</td><td>单行表达式</td><td>任意复杂</td></tr><tr><td>可读性</td><td>简单场景好</td><td>复杂场景好</td></tr><tr><td>调试</td><td>显示 <code>&lt;lambda&gt;</code></td><td>显示真实函数名</td></tr><tr><td>文档字符串</td><td>无</td><td>有</td></tr></tbody></table><p><strong>工程建议</strong>：</p><ul><li>简单的一行表达式用 <code>lambda</code>，复杂逻辑用 <code>def</code> 定义命名函数。</li><li>若 <code>lambda</code> 超过一行，说明它不应该用 <code>lambda</code>。</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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 不好的 lambda</span></span><br><span class="line">process = <span class="keyword">lambda</span> data: [x.strip().lower() <span class="keyword">for</span> x <span class="keyword">in</span> data <span class="keyword">if</span> x.startswith(<span class="string">&quot;http&quot;</span>)]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 好的命名函数</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">extract_urls</span>(<span class="params">data</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;从数据中筛选并处理 URL。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">return</span> [x.strip().lower() <span class="keyword">for</span> x <span class="keyword">in</span> data <span class="keyword">if</span> x.startswith(<span class="string">&quot;http&quot;</span>)]</span><br></pre></td></tr></table></figure><p><strong>面试真题</strong>：lambda 能包含多个语句吗？</p><blockquote><p>答：不能。lambda 只能包含一个表达式，不能包含语句（如赋值、循环、try&#x2F;except）。</p></blockquote><hr><h2 id="5-高阶函数"><a href="#5-高阶函数" class="headerlink" title="5. 高阶函数"></a>5. 高阶函数</h2><p><strong>高阶函数</strong>：接收函数作为参数，或返回函数的函数。</p><h3 id="5-1-map、filter、reduce"><a href="#5-1-map、filter、reduce" class="headerlink" title="5.1 map、filter、reduce"></a>5.1 <code>map</code>、<code>filter</code>、<code>reduce</code></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><span class="line"><span class="keyword">from</span> functools <span class="keyword">import</span> reduce</span><br><span class="line"></span><br><span class="line">nums = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>]</span><br><span class="line"></span><br><span class="line"><span class="comment"># map: 对每个元素应用函数</span></span><br><span class="line">squares = <span class="built_in">list</span>(<span class="built_in">map</span>(<span class="keyword">lambda</span> x: x ** <span class="number">2</span>, nums))</span><br><span class="line"><span class="built_in">print</span>(squares)          <span class="comment"># [1, 4, 9, 16, 25]</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 更 Pythonic 的方式：列表推导式</span></span><br><span class="line">squares = [x ** <span class="number">2</span> <span class="keyword">for</span> x <span class="keyword">in</span> nums]   <span class="comment"># 推荐！可读性更好</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># filter: 筛选满足条件的元素</span></span><br><span class="line">evens = <span class="built_in">list</span>(<span class="built_in">filter</span>(<span class="keyword">lambda</span> x: x % <span class="number">2</span> == <span class="number">0</span>, nums))</span><br><span class="line"><span class="built_in">print</span>(evens)            <span class="comment"># [2, 4]</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 更 Pythonic 的方式</span></span><br><span class="line">evens = [x <span class="keyword">for</span> x <span class="keyword">in</span> nums <span class="keyword">if</span> x % <span class="number">2</span> == <span class="number">0</span>]   <span class="comment"># 推荐！</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># reduce: 累积计算</span></span><br><span class="line">product = reduce(<span class="keyword">lambda</span> acc, x: acc * x, nums, <span class="number">1</span>)</span><br><span class="line"><span class="built_in">print</span>(product)          <span class="comment"># 120（1*2*3*4*5）</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># reduce 的替代方案</span></span><br><span class="line"><span class="keyword">from</span> operator <span class="keyword">import</span> mul</span><br><span class="line">product = <span class="number">1</span></span><br><span class="line"><span class="keyword">for</span> x <span class="keyword">in</span> nums:</span><br><span class="line">    product *= x</span><br><span class="line"><span class="comment"># 或</span></span><br><span class="line"><span class="keyword">import</span> math</span><br><span class="line">product = math.prod(nums)   <span class="comment"># 3.8+，最简洁</span></span><br></pre></td></tr></table></figure><p><strong>工程建议</strong>：</p><ul><li>优先使用<strong>列表推导式</strong>和<strong>生成器表达式</strong>替代 <code>map</code>&#x2F;<code>filter</code>，可读性更好。</li><li><code>reduce</code> 的使用场景较少，能不用就不用。</li></ul><h3 id="5-2-使用-operator-模块避免-lambda"><a href="#5-2-使用-operator-模块避免-lambda" class="headerlink" title="5.2 使用 operator 模块避免 lambda"></a>5.2 使用 operator 模块避免 lambda</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><span class="line"><span class="keyword">from</span> operator <span class="keyword">import</span> add, mul, itemgetter, attrgetter</span><br><span class="line"></span><br><span class="line"><span class="comment"># 替代 lambda x, y: x + y</span></span><br><span class="line"><span class="built_in">print</span>(reduce(add, [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>]))    <span class="comment"># 10</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 替代 lambda u: u[&quot;age&quot;]</span></span><br><span class="line">users = [&#123;<span class="string">&quot;name&quot;</span>: <span class="string">&quot;Alice&quot;</span>, <span class="string">&quot;age&quot;</span>: <span class="number">30</span>&#125;, &#123;<span class="string">&quot;name&quot;</span>: <span class="string">&quot;Bob&quot;</span>, <span class="string">&quot;age&quot;</span>: <span class="number">25</span>&#125;]</span><br><span class="line">users.sort(key=itemgetter(<span class="string">&quot;age&quot;</span>))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 替代 lambda obj: obj.name</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Person</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, name, age</span>):</span><br><span class="line">        <span class="variable language_">self</span>.name = name</span><br><span class="line">        <span class="variable language_">self</span>.age = age</span><br><span class="line"></span><br><span class="line">people = [Person(<span class="string">&quot;Alice&quot;</span>, <span class="number">30</span>), Person(<span class="string">&quot;Bob&quot;</span>, <span class="number">25</span>)]</span><br><span class="line">people.sort(key=attrgetter(<span class="string">&quot;age&quot;</span>))</span><br></pre></td></tr></table></figure><h3 id="5-3-自定义高阶函数"><a href="#5-3-自定义高阶函数" class="headerlink" title="5.3 自定义高阶函数"></a>5.3 自定义高阶函数</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">create_multiplier</span>(<span class="params">factor</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;返回一个乘以 factor 的函数。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">multiply</span>(<span class="params">x</span>):</span><br><span class="line">        <span class="keyword">return</span> x * factor</span><br><span class="line">    <span class="keyword">return</span> multiply</span><br><span class="line"></span><br><span class="line">triple = create_multiplier(<span class="number">3</span>)</span><br><span class="line"><span class="built_in">print</span>(triple(<span class="number">5</span>))        <span class="comment"># 15</span></span><br><span class="line"></span><br><span class="line">double = create_multiplier(<span class="number">2</span>)</span><br><span class="line"><span class="built_in">print</span>(double(<span class="number">7</span>))        <span class="comment"># 14</span></span><br></pre></td></tr></table></figure><h3 id="5-4-函数组合"><a href="#5-4-函数组合" class="headerlink" title="5.4 函数组合"></a>5.4 函数组合</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><span class="line"><span class="keyword">from</span> functools <span class="keyword">import</span> reduce</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">compose</span>(<span class="params">*functions</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;函数组合：compose(f, g, h)(x) == f(g(h(x)))&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">composed</span>(<span class="params">value</span>):</span><br><span class="line">        <span class="keyword">return</span> reduce(<span class="keyword">lambda</span> acc, fn: fn(acc), <span class="built_in">reversed</span>(functions), value)</span><br><span class="line">    <span class="keyword">return</span> composed</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用示例</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">add_one</span>(<span class="params">x</span>): <span class="keyword">return</span> x + <span class="number">1</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">double</span>(<span class="params">x</span>): <span class="keyword">return</span> x * <span class="number">2</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">to_str</span>(<span class="params">x</span>): <span class="keyword">return</span> <span class="built_in">str</span>(x)</span><br><span class="line"></span><br><span class="line">transform = compose(to_str, double, add_one)</span><br><span class="line"><span class="built_in">print</span>(transform(<span class="number">3</span>))   <span class="comment"># &quot;8&quot;  -&gt; (3 + 1) * 2 -&gt; &quot;8&quot;</span></span><br></pre></td></tr></table></figure><blockquote><p>配图占位：建议放一张”函数组合流程图”，文件路径 <code>/images/python/phase2_compose.png</code>。</p></blockquote><hr><h2 id="6-闭包（Closure）"><a href="#6-闭包（Closure）" class="headerlink" title="6. 闭包（Closure）"></a>6. 闭包（Closure）</h2><h3 id="6-1-闭包的定义"><a href="#6-1-闭包的定义" class="headerlink" title="6.1 闭包的定义"></a>6.1 闭包的定义</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">make_power</span>(<span class="params">exponent</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;创建计算 x^n 的函数。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">power</span>(<span class="params">base</span>):</span><br><span class="line">        <span class="keyword">return</span> base ** exponent    <span class="comment"># power 记住了外层变量 exponent</span></span><br><span class="line">    <span class="keyword">return</span> power</span><br><span class="line"></span><br><span class="line">square = make_power(<span class="number">2</span>)   <span class="comment"># exponent=2 被&quot;捕获&quot;</span></span><br><span class="line">cube = make_power(<span class="number">3</span>)     <span class="comment"># exponent=3 被&quot;捕获&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(square(<span class="number">4</span>))         <span class="comment"># 16</span></span><br><span class="line"><span class="built_in">print</span>(cube(<span class="number">3</span>))           <span class="comment"># 27</span></span><br></pre></td></tr></table></figure><p><strong>闭包的本质</strong>：</p><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></pre></td><td class="code"><pre><span class="line">make_power(2) 调用时：</span><br><span class="line">  创建局部变量 exponent = 2</span><br><span class="line">  创建函数 power</span><br><span class="line">  power.__closure__ 中保存了对 exponent 的引用</span><br><span class="line"></span><br><span class="line">make_power 返回后：</span><br><span class="line">  局部变量 exponent 本应被销毁</span><br><span class="line">  但因为闭包引用了它，所以被保留</span><br><span class="line"></span><br><span class="line">square(4) 调用时：</span><br><span class="line">  power 通过闭包找到 exponent = 2</span><br><span class="line">  计算 4 ** 2 = 16</span><br></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></pre></td><td class="code"><pre><span class="line"><span class="built_in">print</span>(square.__closure__)           <span class="comment"># (&lt;cell at ...: int object at ...&gt;,)</span></span><br><span class="line"><span class="built_in">print</span>(square.__closure__[<span class="number">0</span>].cell_contents)  <span class="comment"># 2</span></span><br></pre></td></tr></table></figure><h3 id="6-2-闭包的实际应用"><a href="#6-2-闭包的实际应用" class="headerlink" title="6.2 闭包的实际应用"></a>6.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></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">make_logger</span>(<span class="params">level</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;创建带有固定日志级别的日志函数。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">log</span>(<span class="params">message</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;[<span class="subst">&#123;level.upper()&#125;</span>] <span class="subst">&#123;message&#125;</span>&quot;</span>)</span><br><span class="line">    <span class="keyword">return</span> log</span><br><span class="line"></span><br><span class="line">info_log = make_logger(<span class="string">&quot;info&quot;</span>)</span><br><span class="line">error_log = make_logger(<span class="string">&quot;error&quot;</span>)</span><br><span class="line"></span><br><span class="line">info_log(<span class="string">&quot;服务启动成功&quot;</span>)     <span class="comment"># [INFO] 服务启动成功</span></span><br><span class="line">error_log(<span class="string">&quot;数据库连接失败&quot;</span>)   <span class="comment"># [ERROR] 数据库连接失败</span></span><br></pre></td></tr></table></figure><h3 id="6-3-闭包的陷阱：延迟绑定"><a href="#6-3-闭包的陷阱：延迟绑定" class="headerlink" title="6.3 闭包的陷阱：延迟绑定"></a>6.3 闭包的陷阱：延迟绑定</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><span class="line"><span class="comment"># 经典的闭包陷阱（面试高频）</span></span><br><span class="line">functions = []</span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">3</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">func</span>():</span><br><span class="line">        <span class="keyword">return</span> i          <span class="comment"># 闭包捕获的是变量 i，不是值 0/1/2</span></span><br><span class="line">    functions.append(func)</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(functions[<span class="number">0</span>]())    <span class="comment"># 2（不是 0！）</span></span><br><span class="line"><span class="built_in">print</span>(functions[<span class="number">1</span>]())    <span class="comment"># 2</span></span><br><span class="line"><span class="built_in">print</span>(functions[<span class="number">2</span>]())    <span class="comment"># 2</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 原因：循环结束时 i=2，所有 func 都引用了同一个 i</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 修复方法：使用默认参数绑定当前值</span></span><br><span class="line">functions = []</span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">3</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">func</span>(<span class="params">x=i</span>):        <span class="comment"># x 在定义时绑定当前 i 的值</span></span><br><span class="line">        <span class="keyword">return</span> x</span><br><span class="line">    functions.append(func)</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(functions[<span class="number">0</span>]())    <span class="comment"># 0</span></span><br><span class="line"><span class="built_in">print</span>(functions[<span class="number">1</span>]())    <span class="comment"># 1</span></span><br><span class="line"><span class="built_in">print</span>(functions[<span class="number">2</span>]())    <span class="comment"># 2</span></span><br></pre></td></tr></table></figure><p><strong>面试真题</strong>：为什么下面的 lambda 列表输出都是 4？</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><span class="line">funcs = [(<span class="keyword">lambda</span>: i) <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">5</span>)]</span><br><span class="line"><span class="built_in">print</span>([f() <span class="keyword">for</span> f <span class="keyword">in</span> funcs])   <span class="comment"># [4, 4, 4, 4, 4]</span></span><br></pre></td></tr></table></figure><blockquote><p>答：与闭包延迟绑定同理，所有 lambda 都引用了同一个 <code>i</code>，循环结束时 <code>i=4</code>。</p></blockquote><hr><h2 id="7-装饰器（Decorator）"><a href="#7-装饰器（Decorator）" class="headerlink" title="7. 装饰器（Decorator）"></a>7. 装饰器（Decorator）</h2><p>装饰器是闭包的高级应用，用于<strong>在不修改原函数代码的前提下，增强函数功能</strong>。</p><blockquote><p>配图占位：建议放一张”装饰器工作原理”示意图，文件路径 <code>/images/python/phase2_decorator.png</code>。</p></blockquote><h3 id="7-1-装饰器基础"><a href="#7-1-装饰器基础" class="headerlink" title="7.1 装饰器基础"></a>7.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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> functools</span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">timing</span>(<span class="params">func</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;计算函数执行时间的装饰器。&quot;&quot;&quot;</span></span><br><span class="line"><span class="meta">    @functools.wraps(<span class="params">func</span>)      </span><span class="comment"># 保留原函数的元数据（__name__、__doc__ 等）</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">wrapper</span>(<span class="params">*args, **kwargs</span>):</span><br><span class="line">        start = time.perf_counter()</span><br><span class="line">        result = func(*args, **kwargs)</span><br><span class="line">        elapsed = time.perf_counter() - start</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;<span class="subst">&#123;func.__name__&#125;</span> 耗时: <span class="subst">&#123;elapsed:<span class="number">.4</span>f&#125;</span>s&quot;</span>)</span><br><span class="line">        <span class="keyword">return</span> result</span><br><span class="line">    <span class="keyword">return</span> wrapper</span><br><span class="line"></span><br><span class="line"><span class="meta">@timing</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">slow_function</span>(<span class="params">n</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;模拟耗时操作。&quot;&quot;&quot;</span></span><br><span class="line">    time.sleep(<span class="number">0.1</span>)</span><br><span class="line">    <span class="keyword">return</span> n * <span class="number">2</span></span><br><span class="line"></span><br><span class="line">result = slow_function(<span class="number">5</span>)</span><br><span class="line"><span class="comment"># 输出: slow_function 耗时: 0.1002s</span></span><br></pre></td></tr></table></figure><p><strong>装饰器执行过程</strong>：</p><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></pre></td><td class="code"><pre><span class="line">@timing</span><br><span class="line"> def slow_function(n): ...</span><br><span class="line"></span><br><span class="line">等价于：</span><br><span class="line"> slow_function = timing(slow_function)</span><br><span class="line"></span><br><span class="line">调用 slow_function(5) 时：</span><br><span class="line"> 实际调用 wrapper(5)</span><br><span class="line"> wrapper 记录开始时间</span><br><span class="line"> wrapper 调用原 slow_function(5)</span><br><span class="line"> wrapper 记录结束时间并打印</span><br><span class="line"> wrapper 返回结果</span><br></pre></td></tr></table></figure><h3 id="7-2-functools-wraps-的作用"><a href="#7-2-functools-wraps-的作用" class="headerlink" title="7.2 @functools.wraps 的作用"></a>7.2 <code>@functools.wraps</code> 的作用</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><span class="line"><span class="comment"># 不使用 wraps</span></span><br><span class="line"><span class="meta">@timing</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">my_func</span>():</span><br><span class="line">    <span class="string">&quot;&quot;&quot;我的函数。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(my_func.__name__)   <span class="comment"># &quot;wrapper&quot; —— 元数据丢失！</span></span><br><span class="line"><span class="built_in">print</span>(my_func.__doc__)    <span class="comment"># None</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用 wraps 后</span></span><br><span class="line"><span class="built_in">print</span>(my_func.__name__)   <span class="comment"># &quot;my_func&quot; —— 元数据保留</span></span><br><span class="line"><span class="built_in">print</span>(my_func.__doc__)    <span class="comment"># &quot;我的函数。&quot;</span></span><br></pre></td></tr></table></figure><p><strong><code>wraps</code> 到底做了什么？</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><span class="line"><span class="comment"># @functools.wraps(func) 等价于：</span></span><br><span class="line">wrapper.__name__ = func.__name__</span><br><span class="line">wrapper.__doc__ = func.__doc__</span><br><span class="line">wrapper.__module__ = func.__module__</span><br><span class="line">wrapper.__qualname__ = func.__qualname__</span><br><span class="line">wrapper.__dict__.update(func.__dict__)</span><br><span class="line">wrapper.__wrapped__ = func</span><br></pre></td></tr></table></figure><h3 id="7-3-带参数的装饰器"><a href="#7-3-带参数的装饰器" class="headerlink" title="7.3 带参数的装饰器"></a>7.3 带参数的装饰器</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><span class="line"><span class="keyword">def</span> <span class="title function_">retry</span>(<span class="params">max_attempts=<span class="number">3</span>, delay=<span class="number">1</span></span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;带参数的装饰器：失败时自动重试。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">decorator</span>(<span class="params">func</span>):</span><br><span class="line"><span class="meta">        @functools.wraps(<span class="params">func</span>)</span></span><br><span class="line">        <span class="keyword">def</span> <span class="title function_">wrapper</span>(<span class="params">*args, **kwargs</span>):</span><br><span class="line">            <span class="keyword">for</span> attempt <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">1</span>, max_attempts + <span class="number">1</span>):</span><br><span class="line">                <span class="keyword">try</span>:</span><br><span class="line">                    <span class="keyword">return</span> func(*args, **kwargs)</span><br><span class="line">                <span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">                    <span class="built_in">print</span>(<span class="string">f&quot;第 <span class="subst">&#123;attempt&#125;</span> 次尝试失败: <span class="subst">&#123;e&#125;</span>&quot;</span>)</span><br><span class="line">                    <span class="keyword">if</span> attempt &lt; max_attempts:</span><br><span class="line">                        time.sleep(delay)</span><br><span class="line">            <span class="keyword">raise</span> Exception(<span class="string">f&quot;<span class="subst">&#123;max_attempts&#125;</span> 次尝试全部失败&quot;</span>)</span><br><span class="line">        <span class="keyword">return</span> wrapper</span><br><span class="line">    <span class="keyword">return</span> decorator</span><br><span class="line"></span><br><span class="line"><span class="meta">@retry(<span class="params">max_attempts=<span class="number">3</span>, delay=<span class="number">0.5</span></span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">fetch_data</span>():</span><br><span class="line">    <span class="string">&quot;&quot;&quot;模拟可能失败的请求。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">import</span> random</span><br><span class="line">    <span class="keyword">if</span> random.random() &lt; <span class="number">0.7</span>:</span><br><span class="line">        <span class="keyword">raise</span> ConnectionError(<span class="string">&quot;连接失败&quot;</span>)</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;数据&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 调用</span></span><br><span class="line"><span class="comment"># data = fetch_data()</span></span><br></pre></td></tr></table></figure><p><strong>带参数装饰器的执行过程</strong>：</p><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></pre></td><td class="code"><pre><span class="line">@retry(max_attempts=3)</span><br><span class="line"> def fetch_data(): ...</span><br><span class="line"></span><br><span class="line">等价于：</span><br><span class="line"> fetch_data = retry(max_attempts=3)(fetch_data)</span><br><span class="line"></span><br><span class="line">retry(max_attempts=3) 返回 decorator</span><br><span class="line">decorator(fetch_data) 返回 wrapper</span><br><span class="line">fetch_data 指向 wrapper</span><br></pre></td></tr></table></figure><h3 id="7-4-类装饰器"><a href="#7-4-类装饰器" class="headerlink" title="7.4 类装饰器"></a>7.4 类装饰器</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><span class="line"><span class="keyword">class</span> <span class="title class_">CountCalls</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;统计函数被调用次数的类装饰器。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, func</span>):</span><br><span class="line">        functools.update_wrapper(<span class="variable language_">self</span>, func)</span><br><span class="line">        <span class="variable language_">self</span>.func = func</span><br><span class="line">        <span class="variable language_">self</span>.count = <span class="number">0</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__call__</span>(<span class="params">self, *args, **kwargs</span>):</span><br><span class="line">        <span class="variable language_">self</span>.count += <span class="number">1</span></span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;<span class="subst">&#123;self.func.__name__&#125;</span> 被调用了 <span class="subst">&#123;self.count&#125;</span> 次&quot;</span>)</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>.func(*args, **kwargs)</span><br><span class="line"></span><br><span class="line"><span class="meta">@CountCalls</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">say_hello</span>():</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;Hello!&quot;</span>)</span><br><span class="line"></span><br><span class="line">say_hello()   <span class="comment"># say_hello 被调用了 1 次 / Hello!</span></span><br><span class="line">say_hello()   <span class="comment"># say_hello 被调用了 2 次 / Hello!</span></span><br></pre></td></tr></table></figure><h3 id="7-5-多个装饰器堆叠"><a href="#7-5-多个装饰器堆叠" class="headerlink" title="7.5 多个装饰器堆叠"></a>7.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></pre></td><td class="code"><pre><span class="line"><span class="meta">@decorator_a</span></span><br><span class="line"><span class="meta">@decorator_b</span></span><br><span class="line"><span class="meta">@decorator_c</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">my_func</span>():</span><br><span class="line">    <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 等价于：my_func = decorator_a(decorator_b(decorator_c(my_func)))</span></span><br><span class="line"><span class="comment"># 执行顺序：从内到外（c -&gt; b -&gt; a）</span></span><br></pre></td></tr></table></figure><blockquote><p>配图占位：建议放一张”多个装饰器堆叠执行顺序”示意图，文件路径 <code>/images/python/phase2_decorators_stack.png</code>。</p></blockquote><h3 id="7-6-常用装饰器模式"><a href="#7-6-常用装饰器模式" class="headerlink" title="7.6 常用装饰器模式"></a>7.6 常用装饰器模式</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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 1. 权限检查</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">require_login</span>(<span class="params">func</span>):</span><br><span class="line"><span class="meta">    @functools.wraps(<span class="params">func</span>)</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">wrapper</span>(<span class="params">user, *args, **kwargs</span>):</span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> user.is_authenticated:</span><br><span class="line">            <span class="keyword">raise</span> PermissionError(<span class="string">&quot;请先登录&quot;</span>)</span><br><span class="line">        <span class="keyword">return</span> func(user, *args, **kwargs)</span><br><span class="line">    <span class="keyword">return</span> wrapper</span><br><span class="line"></span><br><span class="line"><span class="comment"># 2. 缓存（简单版，生产环境用 functools.lru_cache）</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">simple_cache</span>(<span class="params">func</span>):</span><br><span class="line">    cache = &#123;&#125;</span><br><span class="line"><span class="meta">    @functools.wraps(<span class="params">func</span>)</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">wrapper</span>(<span class="params">*args</span>):</span><br><span class="line">        <span class="keyword">if</span> args <span class="keyword">not</span> <span class="keyword">in</span> cache:</span><br><span class="line">            cache[args] = func(*args)</span><br><span class="line">        <span class="keyword">return</span> cache[args]</span><br><span class="line">    <span class="keyword">return</span> wrapper</span><br><span class="line"></span><br><span class="line"><span class="comment"># 3. 参数验证</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">validate_types</span>(<span class="params">**types</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">decorator</span>(<span class="params">func</span>):</span><br><span class="line"><span class="meta">        @functools.wraps(<span class="params">func</span>)</span></span><br><span class="line">        <span class="keyword">def</span> <span class="title function_">wrapper</span>(<span class="params">*args, **kwargs</span>):</span><br><span class="line">            bound = inspect.signature(func).bind(*args, **kwargs)</span><br><span class="line">            bound.apply_defaults()</span><br><span class="line">            <span class="keyword">for</span> name, expected_type <span class="keyword">in</span> types.items():</span><br><span class="line">                <span class="keyword">if</span> name <span class="keyword">in</span> bound.arguments:</span><br><span class="line">                    value = bound.arguments[name]</span><br><span class="line">                    <span class="keyword">if</span> <span class="keyword">not</span> <span class="built_in">isinstance</span>(value, expected_type):</span><br><span class="line">                        <span class="keyword">raise</span> TypeError(<span class="string">f&quot;<span class="subst">&#123;name&#125;</span> 期望 <span class="subst">&#123;expected_type&#125;</span>，实际 <span class="subst">&#123;<span class="built_in">type</span>(value)&#125;</span>&quot;</span>)</span><br><span class="line">            <span class="keyword">return</span> func(*args, **kwargs)</span><br><span class="line">        <span class="keyword">return</span> wrapper</span><br><span class="line">    <span class="keyword">return</span> decorator</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> inspect</span><br><span class="line"></span><br><span class="line"><span class="meta">@validate_types(<span class="params">name=<span class="built_in">str</span>, age=<span class="built_in">int</span></span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">register</span>(<span class="params">name, age</span>):</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;注册用户: <span class="subst">&#123;name&#125;</span>, <span class="subst">&#123;age&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># register(&quot;Alice&quot;, &quot;30&quot;)   # TypeError: age 期望 &lt;class &#x27;int&#x27;&gt;，实际 &lt;class &#x27;str&#x27;&gt;</span></span><br></pre></td></tr></table></figure><h3 id="7-7-内置装饰器"><a href="#7-7-内置装饰器" class="headerlink" title="7.7 内置装饰器"></a>7.7 内置装饰器</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><span class="line"><span class="comment"># @staticmethod —— 静态方法，不接收 self</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Math</span>:</span><br><span class="line"><span class="meta">    @staticmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">add</span>(<span class="params">a, b</span>):</span><br><span class="line">        <span class="keyword">return</span> a + b</span><br><span class="line"></span><br><span class="line"><span class="comment"># @classmethod —— 类方法，接收 cls</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">User</span>:</span><br><span class="line">    count = <span class="number">0</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, name</span>):</span><br><span class="line">        <span class="variable language_">self</span>.name = name</span><br><span class="line">        User.count += <span class="number">1</span></span><br><span class="line"></span><br><span class="line"><span class="meta">    @classmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_count</span>(<span class="params">cls</span>):</span><br><span class="line">        <span class="keyword">return</span> cls.count</span><br><span class="line"></span><br><span class="line"><span class="comment"># @property —— 把方法变成属性</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Circle</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, radius</span>):</span><br><span class="line">        <span class="variable language_">self</span>.radius = radius</span><br><span class="line"></span><br><span class="line"><span class="meta">    @property</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">area</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="number">3.14159</span> * <span class="variable language_">self</span>.radius ** <span class="number">2</span></span><br><span class="line"></span><br><span class="line">c = Circle(<span class="number">5</span>)</span><br><span class="line"><span class="built_in">print</span>(c.area)   <span class="comment"># 78.53975，不需要加括号</span></span><br></pre></td></tr></table></figure><hr><h2 id="8-递归"><a href="#8-递归" class="headerlink" title="8. 递归"></a>8. 递归</h2><h3 id="8-1-递归基础"><a href="#8-1-递归基础" class="headerlink" title="8.1 递归基础"></a>8.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></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">factorial</span>(<span class="params">n</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;计算阶乘（递归版）。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">if</span> n &lt;= <span class="number">1</span>:</span><br><span class="line">        <span class="keyword">return</span> <span class="number">1</span></span><br><span class="line">    <span class="keyword">return</span> n * factorial(n - <span class="number">1</span>)</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(factorial(<span class="number">5</span>))     <span class="comment"># 120</span></span><br></pre></td></tr></table></figure><p><strong>递归三要素</strong>：</p><ol><li><strong>终止条件</strong>：<code>n &lt;= 1</code> 时返回 1。</li><li><strong>递归调用</strong>：<code>factorial(n - 1)</code>。</li><li><strong>状态传递</strong>：通过参数 <code>n</code> 传递状态。</li></ol><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></pre></td><td class="code"><pre><span class="line">factorial(5)</span><br><span class="line">  └── 5 * factorial(4)</span><br><span class="line">        └── 4 * factorial(3)</span><br><span class="line">              └── 3 * factorial(2)</span><br><span class="line">                    └── 2 * factorial(1)</span><br><span class="line">                          └── 1</span><br></pre></td></tr></table></figure><h3 id="8-2-递归的局限"><a href="#8-2-递归的局限" class="headerlink" title="8.2 递归的局限"></a>8.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></pre></td><td class="code"><pre><span class="line"><span class="comment"># Python 默认递归深度限制为 1000</span></span><br><span class="line"><span class="keyword">import</span> sys</span><br><span class="line"><span class="built_in">print</span>(sys.getrecursionlimit())   <span class="comment"># 1000</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 深度递归会导致栈溢出</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">infinite_recursion</span>(<span class="params">n</span>):</span><br><span class="line">    <span class="keyword">return</span> infinite_recursion(n + <span class="number">1</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># infinite_recursion(1)   # RecursionError: maximum recursion depth exceeded</span></span><br></pre></td></tr></table></figure><p><strong>工程建议</strong>：</p><ul><li>Python 不擅长深度递归，能用循环解决的不要用递归。</li><li>树&#x2F;图的遍历等天然递归结构，考虑使用栈模拟递归或改用迭代。</li></ul><h3 id="8-3-尾递归（了解即可）"><a href="#8-3-尾递归（了解即可）" class="headerlink" title="8.3 尾递归（了解即可）"></a>8.3 尾递归（了解即可）</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><span class="line"><span class="keyword">def</span> <span class="title function_">factorial_tail</span>(<span class="params">n, acc=<span class="number">1</span></span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;尾递归版阶乘（acc 为累积器）。</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">    注意：CPython 不支持尾递归优化，这仍然会使用调用栈！</span></span><br><span class="line"><span class="string">    但在支持尾递归优化的语言（如 Scheme、Erlang）中，这不会增加栈深度。</span></span><br><span class="line"><span class="string">    &quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">if</span> n &lt;= <span class="number">1</span>:</span><br><span class="line">        <span class="keyword">return</span> acc</span><br><span class="line">    <span class="keyword">return</span> factorial_tail(n - <span class="number">1</span>, n * acc)</span><br></pre></td></tr></table></figure><h3 id="8-4-记忆化递归"><a href="#8-4-记忆化递归" class="headerlink" title="8.4 记忆化递归"></a>8.4 记忆化递归</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><span class="line"><span class="keyword">from</span> functools <span class="keyword">import</span> lru_cache</span><br><span class="line"></span><br><span class="line"><span class="meta">@lru_cache(<span class="params">maxsize=<span class="literal">None</span></span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">fibonacci</span>(<span class="params">n</span>):</span><br><span class="line">    <span class="keyword">if</span> n &lt; <span class="number">2</span>:</span><br><span class="line">        <span class="keyword">return</span> n</span><br><span class="line">    <span class="keyword">return</span> fibonacci(n - <span class="number">1</span>) + fibonacci(n - <span class="number">2</span>)</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(fibonacci(<span class="number">100</span>))   <span class="comment"># 快速计算出结果</span></span><br></pre></td></tr></table></figure><p><strong>记忆化的作用</strong>：</p><ul><li>避免重复计算，将时间复杂度从 O(2^n) 降到 O(n)。</li><li>使用 <code>lru_cache</code> 自动缓存结果。</li></ul><hr><h2 id="9-生成器（Generator）"><a href="#9-生成器（Generator）" class="headerlink" title="9. 生成器（Generator）"></a>9. 生成器（Generator）</h2><h3 id="9-1-生成器函数与-yield"><a href="#9-1-生成器函数与-yield" class="headerlink" title="9.1 生成器函数与 yield"></a>9.1 生成器函数与 <code>yield</code></h3><p>生成器是一种特殊的迭代器，使用 <code>yield</code> 关键字可以<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></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">count_up_to</span>(<span class="params">n</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;生成 1 到 n 的整数。&quot;&quot;&quot;</span></span><br><span class="line">    count = <span class="number">1</span></span><br><span class="line">    <span class="keyword">while</span> count &lt;= n:</span><br><span class="line">        <span class="keyword">yield</span> count</span><br><span class="line">        count += <span class="number">1</span></span><br><span class="line"></span><br><span class="line">counter = count_up_to(<span class="number">3</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">type</span>(counter))     <span class="comment"># &lt;class &#x27;generator&#x27;&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">next</span>(counter))     <span class="comment"># 1</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">next</span>(counter))     <span class="comment"># 2</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">next</span>(counter))     <span class="comment"># 3</span></span><br><span class="line"><span class="comment"># print(next(counter))   # StopIteration 异常</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 更常用的方式：for 循环自动处理 StopIteration</span></span><br><span class="line"><span class="keyword">for</span> num <span class="keyword">in</span> count_up_to(<span class="number">5</span>):</span><br><span class="line">    <span class="built_in">print</span>(num, end=<span class="string">&quot; &quot;</span>)  <span class="comment"># 1 2 3 4 5</span></span><br></pre></td></tr></table></figure><blockquote><p>配图占位：建议放一张”生成器 yield 暂停与恢复”示意图，文件路径 <code>/images/python/phase2_generator.png</code>。</p></blockquote><h3 id="9-2-生成器-vs-列表"><a href="#9-2-生成器-vs-列表" class="headerlink" title="9.2 生成器 vs 列表"></a>9.2 生成器 vs 列表</h3><table><thead><tr><th>特性</th><th>列表</th><th>生成器</th></tr></thead><tbody><tr><td>内存占用</td><td>存储所有元素</td><td>惰性计算，每次只存一个</td></tr><tr><td>可迭代次数</td><td>无限次</td><td>只能迭代一次</td></tr><tr><td>随机访问</td><td>支持 <code>lst[i]</code></td><td>不支持，只能顺序访问</td></tr><tr><td>长度</td><td><code>len()</code> 可知</td><td>不知道总长度</td></tr></tbody></table><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><span class="line"><span class="comment"># 列表：占用大量内存</span></span><br><span class="line">squares_list = [x ** <span class="number">2</span> <span class="keyword">for</span> x <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">10_000_000</span>)]   <span class="comment"># 约 400MB+</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 生成器：几乎不占内存</span></span><br><span class="line">squares_gen = (x ** <span class="number">2</span> <span class="keyword">for</span> x <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">10_000_000</span>))    <span class="comment"># 几乎为 0</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 逐个消费</span></span><br><span class="line"><span class="keyword">for</span> sq <span class="keyword">in</span> squares_gen:</span><br><span class="line">    <span class="keyword">if</span> sq &gt; <span class="number">100</span>:</span><br><span class="line">        <span class="keyword">break</span></span><br></pre></td></tr></table></figure><h3 id="9-3-yield-from（3-3-）"><a href="#9-3-yield-from（3-3-）" class="headerlink" title="9.3 yield from（3.3+）"></a>9.3 <code>yield from</code>（3.3+）</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">flatten</span>(<span class="params">nested</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;展平嵌套的可迭代对象。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">for</span> item <span class="keyword">in</span> nested:</span><br><span class="line">        <span class="keyword">if</span> <span class="built_in">isinstance</span>(item, (<span class="built_in">list</span>, <span class="built_in">tuple</span>)):</span><br><span class="line">            <span class="keyword">yield</span> <span class="keyword">from</span> flatten(item)    <span class="comment"># 委托给子生成器</span></span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            <span class="keyword">yield</span> item</span><br><span class="line"></span><br><span class="line">nested = [<span class="number">1</span>, [<span class="number">2</span>, [<span class="number">3</span>, <span class="number">4</span>]], <span class="number">5</span>]</span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">list</span>(flatten(nested)))   <span class="comment"># [1, 2, 3, 4, 5]</span></span><br></pre></td></tr></table></figure><p><strong><code>yield from</code> 的作用</strong>：</p><ul><li>将子生成器的值直接转发给调用方。</li><li>简化了嵌套生成器的写法。</li><li>在协程中还可以转发 <code>send()</code> 和 <code>throw()</code>。</li></ul><h3 id="9-4-生成器的双向通信：send-方法"><a href="#9-4-生成器的双向通信：send-方法" class="headerlink" title="9.4 生成器的双向通信：send() 方法"></a>9.4 生成器的双向通信：<code>send()</code> 方法</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><span class="line"><span class="keyword">def</span> <span class="title function_">running_average</span>():</span><br><span class="line">    <span class="string">&quot;&quot;&quot;动态计算平均值。&quot;&quot;&quot;</span></span><br><span class="line">    total = <span class="number">0</span></span><br><span class="line">    count = <span class="number">0</span></span><br><span class="line">    average = <span class="literal">None</span></span><br><span class="line">    <span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">        value = <span class="keyword">yield</span> average      <span class="comment"># 接收外部发送的值</span></span><br><span class="line">        total += value</span><br><span class="line">        count += <span class="number">1</span></span><br><span class="line">        average = total / count</span><br><span class="line"></span><br><span class="line">avg = running_average()</span><br><span class="line"><span class="built_in">next</span>(avg)              <span class="comment"># 预激生成器（或使用 avg.send(None)）</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(avg.send(<span class="number">10</span>))    <span class="comment"># 10.0</span></span><br><span class="line"><span class="built_in">print</span>(avg.send(<span class="number">20</span>))    <span class="comment"># 15.0</span></span><br><span class="line"><span class="built_in">print</span>(avg.send(<span class="number">30</span>))    <span class="comment"># 20.0</span></span><br></pre></td></tr></table></figure><p><strong>生成器协议总结</strong>：</p><table><thead><tr><th>方法</th><th>作用</th></tr></thead><tbody><tr><td><code>next(g)</code></td><td>获取下一个值</td></tr><tr><td><code>g.send(value)</code></td><td>向生成器发送值，并获取下一个值</td></tr><tr><td><code>g.throw(exc)</code></td><td>向生成器抛异常</td></tr><tr><td><code>g.close()</code></td><td>关闭生成器</td></tr></tbody></table><h3 id="9-5-生成器表达式"><a href="#9-5-生成器表达式" class="headerlink" title="9.5 生成器表达式"></a>9.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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 列表推导式（生成完整列表）</span></span><br><span class="line">squares = [x ** <span class="number">2</span> <span class="keyword">for</span> x <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">100</span>)]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 生成器表达式（惰性求值，括号可省略在函数参数中）</span></span><br><span class="line">squares_gen = (x ** <span class="number">2</span> <span class="keyword">for</span> x <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">100</span>))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 直接在函数参数中使用，省略括号</span></span><br><span class="line">total = <span class="built_in">sum</span>(x ** <span class="number">2</span> <span class="keyword">for</span> x <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">100</span>))</span><br><span class="line"><span class="built_in">print</span>(total)   <span class="comment"># 328350</span></span><br></pre></td></tr></table></figure><hr><h2 id="10-函数式编程工程实践"><a href="#10-函数式编程工程实践" class="headerlink" title="10. 函数式编程工程实践"></a>10. 函数式编程工程实践</h2><h3 id="10-1-纯函数与副作用"><a href="#10-1-纯函数与副作用" class="headerlink" title="10.1 纯函数与副作用"></a>10.1 纯函数与副作用</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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 纯函数</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">add</span>(<span class="params">a, b</span>):</span><br><span class="line">    <span class="keyword">return</span> a + b</span><br><span class="line"></span><br><span class="line"><span class="comment"># 非纯函数（有副作用）</span></span><br><span class="line">total = <span class="number">0</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">add_to_total</span>(<span class="params">value</span>):</span><br><span class="line">    <span class="keyword">global</span> total</span><br><span class="line">    total += value</span><br><span class="line"></span><br><span class="line"><span class="comment"># 非纯函数（依赖外部状态）</span></span><br><span class="line"><span class="keyword">import</span> random</span><br><span class="line"><span class="keyword">def</span> <span class="title function_">random_add</span>(<span class="params">a</span>):</span><br><span class="line">    <span class="keyword">return</span> a + random.randint(<span class="number">1</span>, <span class="number">10</span>)</span><br></pre></td></tr></table></figure><p><strong>纯函数的优势</strong>：</p><ul><li>可预测、易测试。</li><li>可缓存。</li><li>可并行化。</li></ul><h3 id="10-2-functools-模块常用工具"><a href="#10-2-functools-模块常用工具" class="headerlink" title="10.2 functools 模块常用工具"></a>10.2 <code>functools</code> 模块常用工具</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><span class="line"><span class="keyword">from</span> functools <span class="keyword">import</span> partial, reduce, lru_cache, cmp_to_key</span><br><span class="line"></span><br><span class="line"><span class="comment"># partial: 冻结部分参数</span></span><br><span class="line">basetwo = partial(<span class="built_in">int</span>, base=<span class="number">2</span>)</span><br><span class="line"><span class="built_in">print</span>(basetwo(<span class="string">&quot;1010&quot;</span>))   <span class="comment"># 10</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># reduce: 累积</span></span><br><span class="line">product = reduce(<span class="keyword">lambda</span> x, y: x * y, [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>])   <span class="comment"># 24</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># lru_cache: 缓存</span></span><br><span class="line"><span class="meta">@lru_cache(<span class="params">maxsize=<span class="number">128</span></span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">expensive</span>(<span class="params">n</span>):</span><br><span class="line">    <span class="comment"># 耗时计算</span></span><br><span class="line">    <span class="keyword">return</span> n * n</span><br><span class="line"></span><br><span class="line"><span class="comment"># cmp_to_key: 将比较函数转为 key 函数</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">compare_length</span>(<span class="params">a, b</span>):</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">len</span>(a) - <span class="built_in">len</span>(b)</span><br><span class="line"></span><br><span class="line">words = [<span class="string">&quot;apple&quot;</span>, <span class="string">&quot;pie&quot;</span>, <span class="string">&quot;banana&quot;</span>]</span><br><span class="line">words.sort(key=cmp_to_key(compare_length))</span><br></pre></td></tr></table></figure><h3 id="10-3-itertools-模块常用工具"><a href="#10-3-itertools-模块常用工具" class="headerlink" title="10.3 itertools 模块常用工具"></a>10.3 <code>itertools</code> 模块常用工具</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><span class="line"><span class="keyword">import</span> itertools</span><br><span class="line"></span><br><span class="line"><span class="comment"># 无限迭代器</span></span><br><span class="line">count = itertools.count(<span class="number">1</span>, <span class="number">2</span>)        <span class="comment"># 1, 3, 5, 7...</span></span><br><span class="line">cycle = itertools.cycle(<span class="string">&quot;AB&quot;</span>)        <span class="comment"># A, B, A, B...</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 组合</span></span><br><span class="line">perms = <span class="built_in">list</span>(itertools.permutations([<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]))</span><br><span class="line">combs = <span class="built_in">list</span>(itertools.combinations([<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>], <span class="number">2</span>))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 分组</span></span><br><span class="line"><span class="keyword">for</span> key, group <span class="keyword">in</span> itertools.groupby(<span class="string">&quot;AAABBBCC&quot;</span>):</span><br><span class="line">    <span class="built_in">print</span>(key, <span class="built_in">list</span>(group))</span><br><span class="line"><span class="comment"># A [&#x27;A&#x27;, &#x27;A&#x27;, &#x27;A&#x27;]</span></span><br><span class="line"><span class="comment"># B [&#x27;B&#x27;, &#x27;B&#x27;, &#x27;B&#x27;]</span></span><br><span class="line"><span class="comment"># C [&#x27;C&#x27;, &#x27;C&#x27;]</span></span><br></pre></td></tr></table></figure><blockquote><p>配图占位：建议放一张”Python functools 与 itertools 工具速查”示意图，文件路径 <code>/images/python/phase2_functools_itertools.png</code>。</p></blockquote><hr><h2 id="附录-A：第二阶段自检清单"><a href="#附录-A：第二阶段自检清单" class="headerlink" title="附录 A：第二阶段自检清单"></a>附录 A：第二阶段自检清单</h2><p>在继续学习第三阶段之前，请确保你能：</p><ul><li><input disabled="" type="checkbox"> 解释 Python 的”传对象引用”机制，并判断函数内修改是否影响外部</li><li><input disabled="" type="checkbox"> 说明位置参数、关键字参数、仅限位置参数、仅限关键字参数的区别</li><li><input disabled="" type="checkbox"> 解释 <code>*args</code> 和 <code>**kwargs</code> 的区别与用法，并进行拆包调用</li><li><input disabled="" type="checkbox"> 解释可变默认参数的陷阱，并写出安全的替代方案</li><li><input disabled="" type="checkbox"> 说明 LEGB 规则，并正确使用 <code>global</code> 和 <code>nonlocal</code></li><li><input disabled="" type="checkbox"> 解释闭包的概念，并写出利用闭包实现状态保持的代码</li><li><input disabled="" type="checkbox"> 解释闭包延迟绑定问题及修复方法</li><li><input disabled="" type="checkbox"> 手写一个装饰器（不带参数和带参数两种），并解释 <code>@functools.wraps</code> 的作用</li><li><input disabled="" type="checkbox"> 解释生成器的工作原理，用 <code>yield</code> 实现一个简单的生成器函数</li><li><input disabled="" type="checkbox"> 说明列表推导式和生成器表达式的区别，以及各自的适用场景</li><li><input disabled="" type="checkbox"> 解释 <code>yield from</code> 的作用</li><li><input disabled="" type="checkbox"> 使用 <code>lambda</code> 配合 <code>sorted()</code>&#x2F;<code>map()</code>&#x2F;<code>filter()</code> 完成常见操作</li><li><input disabled="" type="checkbox"> 说明纯函数的优势和副作用的危害</li><li><input disabled="" type="checkbox"> 熟练使用 <code>functools</code> 和 <code>itertools</code> 中的常用工具</li></ul><hr><h2 id="附录-B：面试题精选"><a href="#附录-B：面试题精选" class="headerlink" title="附录 B：面试题精选"></a>附录 B：面试题精选</h2><ol><li>**Python 的传参机制是什么？**传对象引用。</li><li>**可变默认参数有什么陷阱？**默认参数在函数定义时求值，可变对象被所有调用共享。</li><li><strong><code>*args</code> 和 <code>**kwargs</code> 的星号分别做了什么？</strong><code>*</code> 拆包序列，<code>**</code> 拆包字典。</li><li><strong><code>global</code> 和 <code>nonlocal</code> 的区别？</strong><code>global</code> 修改全局变量，<code>nonlocal</code> 修改外层闭包变量。</li><li>**什么是闭包？**函数记住其创建时的环境。</li><li>**闭包延迟绑定问题如何修复？**使用默认参数 <code>x=i</code> 绑定当前值。</li><li>**装饰器的本质是什么？**返回函数的高阶函数。</li><li>**<code>@functools.wraps</code> 的作用？**保留原函数的元数据。</li><li>**生成器和列表的区别？**生成器惰性求值、省内存、只能迭代一次。</li><li>**<code>yield from</code> 有什么用？**委托子生成器，简化嵌套生成器。</li></ol><hr><h2 id="工作实战场景"><a href="#工作实战场景" class="headerlink" title="工作实战场景"></a>工作实战场景</h2><h3 id="场景-1：处理超大日志文件"><a href="#场景-1：处理超大日志文件" class="headerlink" title="场景 1：处理超大日志文件"></a>场景 1：处理超大日志文件</h3><p><strong>需求</strong>：分析 10GB 的日志文件，提取所有 ERROR 级别的日志行。</p><p><strong>问题</strong>：直接 <code>read()</code> 会撑爆内存。</p><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">parse_large_log</span>(<span class="params">filepath</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;生成器：逐行读取大文件，避免内存溢出&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">with</span> <span class="built_in">open</span>(filepath, <span class="string">&#x27;r&#x27;</span>, encoding=<span class="string">&#x27;utf-8&#x27;</span>) <span class="keyword">as</span> f:</span><br><span class="line">        <span class="keyword">for</span> line <span class="keyword">in</span> f:</span><br><span class="line">            <span class="keyword">if</span> <span class="string">&#x27;ERROR&#x27;</span> <span class="keyword">in</span> line:</span><br><span class="line">                <span class="keyword">yield</span> line.strip()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用：内存占用恒定，无论文件多大</span></span><br><span class="line"><span class="keyword">for</span> error_line <span class="keyword">in</span> parse_large_log(<span class="string">&#x27;/var/log/app.log&#x27;</span>):</span><br><span class="line">    process_error(error_line)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 统计错误数量（无需加载整个文件）</span></span><br><span class="line">error_count = <span class="built_in">sum</span>(<span class="number">1</span> <span class="keyword">for</span> _ <span class="keyword">in</span> parse_large_log(<span class="string">&#x27;/var/log/app.log&#x27;</span>))</span><br></pre></td></tr></table></figure><h3 id="场景-2：配置验证器"><a href="#场景-2：配置验证器" class="headerlink" title="场景 2：配置验证器"></a>场景 2：配置验证器</h3><p><strong>需求</strong>：验证用户配置，返回所有错误而非遇到第一个错误就停止。</p><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><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><span class="line"><span class="keyword">def</span> <span class="title function_">validate_config</span>(<span class="params">config</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;验证配置，返回所有错误&quot;&quot;&quot;</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">check_required_fields</span>():</span><br><span class="line">        required = [<span class="string">&#x27;host&#x27;</span>, <span class="string">&#x27;port&#x27;</span>, <span class="string">&#x27;database&#x27;</span>]</span><br><span class="line">        <span class="keyword">for</span> field <span class="keyword">in</span> required:</span><br><span class="line">            <span class="keyword">if</span> field <span class="keyword">not</span> <span class="keyword">in</span> config:</span><br><span class="line">                <span class="keyword">yield</span> <span class="string">f&quot;缺少必填字段: <span class="subst">&#123;field&#125;</span>&quot;</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">check_port_range</span>():</span><br><span class="line">        port = config.get(<span class="string">&#x27;port&#x27;</span>)</span><br><span class="line">        <span class="keyword">if</span> port <span class="keyword">and</span> <span class="keyword">not</span> (<span class="number">1</span> &lt;= port &lt;= <span class="number">65535</span>):</span><br><span class="line">            <span class="keyword">yield</span> <span class="string">f&quot;端口 <span class="subst">&#123;port&#125;</span> 超出有效范围 (1-65535)&quot;</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">check_timeout</span>():</span><br><span class="line">        timeout = config.get(<span class="string">&#x27;timeout&#x27;</span>, <span class="number">30</span>)</span><br><span class="line">        <span class="keyword">if</span> timeout &lt;= <span class="number">0</span>:</span><br><span class="line">            <span class="keyword">yield</span> <span class="string">f&quot;超时时间必须为正数: <span class="subst">&#123;timeout&#125;</span>&quot;</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 收集所有验证器的错误</span></span><br><span class="line">    <span class="keyword">yield</span> <span class="keyword">from</span> check_required_fields()</span><br><span class="line">    <span class="keyword">yield</span> <span class="keyword">from</span> check_port_range()</span><br><span class="line">    <span class="keyword">yield</span> <span class="keyword">from</span> check_timeout()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用</span></span><br><span class="line">config = &#123;<span class="string">&#x27;host&#x27;</span>: <span class="string">&#x27;localhost&#x27;</span>, <span class="string">&#x27;port&#x27;</span>: <span class="number">99999</span>, <span class="string">&#x27;timeout&#x27;</span>: -<span class="number">5</span>&#125;</span><br><span class="line">errors = <span class="built_in">list</span>(validate_config(config))</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> errors:</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;配置错误:&quot;</span>)</span><br><span class="line">    <span class="keyword">for</span> err <span class="keyword">in</span> errors:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;  - <span class="subst">&#123;err&#125;</span>&quot;</span>)</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;配置有效&quot;</span>)</span><br></pre></td></tr></table></figure><h3 id="场景-3：缓存装饰器"><a href="#场景-3：缓存装饰器" class="headerlink" title="场景 3：缓存装饰器"></a>场景 3：缓存装饰器</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><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><span class="line"><span class="keyword">import</span> time</span><br><span class="line"><span class="keyword">from</span> functools <span class="keyword">import</span> wraps</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">cache_result</span>(<span class="params">ttl_seconds=<span class="number">60</span></span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;带 TTL 的缓存装饰器&quot;&quot;&quot;</span></span><br><span class="line">    cache = &#123;&#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">decorator</span>(<span class="params">func</span>):</span><br><span class="line"><span class="meta">        @wraps(<span class="params">func</span>)</span></span><br><span class="line">        <span class="keyword">def</span> <span class="title function_">wrapper</span>(<span class="params">*args, **kwargs</span>):</span><br><span class="line">            <span class="comment"># 使用参数作为缓存键</span></span><br><span class="line">            key = (args, <span class="built_in">tuple</span>(<span class="built_in">sorted</span>(kwargs.items())))</span><br><span class="line">            </span><br><span class="line">            <span class="comment"># 检查缓存是否存在且未过期</span></span><br><span class="line">            <span class="keyword">if</span> key <span class="keyword">in</span> cache:</span><br><span class="line">                result, timestamp = cache[key]</span><br><span class="line">                <span class="keyword">if</span> time.time() - timestamp &lt; ttl_seconds:</span><br><span class="line">                    <span class="keyword">return</span> result</span><br><span class="line">            </span><br><span class="line">            <span class="comment"># 调用原函数并缓存结果</span></span><br><span class="line">            result = func(*args, **kwargs)</span><br><span class="line">            cache[key] = (result, time.time())</span><br><span class="line">            <span class="keyword">return</span> result</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> wrapper</span><br><span class="line">    <span class="keyword">return</span> decorator</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用示例</span></span><br><span class="line"><span class="meta">@cache_result(<span class="params">ttl_seconds=<span class="number">30</span></span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">get_user_info</span>(<span class="params">user_id</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;模拟数据库查询&quot;&quot;&quot;</span></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;查询数据库: user_id=<span class="subst">&#123;user_id&#125;</span>&quot;</span>)</span><br><span class="line">    <span class="keyword">return</span> &#123;<span class="string">&#x27;id&#x27;</span>: user_id, <span class="string">&#x27;name&#x27;</span>: <span class="string">f&#x27;User<span class="subst">&#123;user_id&#125;</span>&#x27;</span>&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 第一次调用：实际查询</span></span><br><span class="line">user1 = get_user_info(<span class="number">1</span>)  <span class="comment"># 输出: 查询数据库: user_id=1</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 30秒内再次调用：使用缓存</span></span><br><span class="line">user1_again = get_user_info(<span class="number">1</span>)  <span class="comment"># 无输出，使用缓存</span></span><br></pre></td></tr></table></figure><h3 id="场景-4：重试装饰器"><a href="#场景-4：重试装饰器" class="headerlink" title="场景 4：重试装饰器"></a>场景 4：重试装饰器</h3><p><strong>需求</strong>：为不稳定的外部 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><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><span class="line"><span class="keyword">import</span> time</span><br><span class="line"><span class="keyword">import</span> random</span><br><span class="line"><span class="keyword">from</span> functools <span class="keyword">import</span> wraps</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">retry</span>(<span class="params">max_attempts=<span class="number">3</span>, delay=<span class="number">1</span>, backoff=<span class="number">2</span>, exceptions=(<span class="params">Exception,</span>)</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;重试装饰器</span></span><br><span class="line"><span class="string">    </span></span><br><span class="line"><span class="string">    Args:</span></span><br><span class="line"><span class="string">        max_attempts: 最大重试次数</span></span><br><span class="line"><span class="string">        delay: 初始延迟（秒）</span></span><br><span class="line"><span class="string">        backoff: 延迟增长因子</span></span><br><span class="line"><span class="string">        exceptions: 捕获的异常类型</span></span><br><span class="line"><span class="string">    &quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">decorator</span>(<span class="params">func</span>):</span><br><span class="line"><span class="meta">        @wraps(<span class="params">func</span>)</span></span><br><span class="line">        <span class="keyword">def</span> <span class="title function_">wrapper</span>(<span class="params">*args, **kwargs</span>):</span><br><span class="line">            current_delay = delay</span><br><span class="line">            last_exception = <span class="literal">None</span></span><br><span class="line">            </span><br><span class="line">            <span class="keyword">for</span> attempt <span class="keyword">in</span> <span class="built_in">range</span>(max_attempts):</span><br><span class="line">                <span class="keyword">try</span>:</span><br><span class="line">                    <span class="keyword">return</span> func(*args, **kwargs)</span><br><span class="line">                <span class="keyword">except</span> exceptions <span class="keyword">as</span> e:</span><br><span class="line">                    last_exception = e</span><br><span class="line">                    <span class="keyword">if</span> attempt &lt; max_attempts - <span class="number">1</span>:</span><br><span class="line">                        time.sleep(current_delay)</span><br><span class="line">                        current_delay *= backoff</span><br><span class="line">            </span><br><span class="line">            <span class="keyword">raise</span> last_exception</span><br><span class="line">        <span class="keyword">return</span> wrapper</span><br><span class="line">    <span class="keyword">return</span> decorator</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用示例</span></span><br><span class="line"><span class="meta">@retry(<span class="params">max_attempts=<span class="number">3</span>, delay=<span class="number">0.5</span>, exceptions=(<span class="params">ConnectionError, TimeoutError</span>)</span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">call_external_api</span>(<span class="params">url</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;调用外部 API&quot;&quot;&quot;</span></span><br><span class="line">    <span class="comment"># 模拟 50% 概率失败</span></span><br><span class="line">    <span class="keyword">if</span> random.random() &lt; <span class="number">0.5</span>:</span><br><span class="line">        <span class="keyword">raise</span> ConnectionError(<span class="string">&quot;网络错误&quot;</span>)</span><br><span class="line">    <span class="keyword">return</span> &#123;<span class="string">&quot;status&quot;</span>: <span class="string">&quot;ok&quot;</span>&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 自动重试</span></span><br><span class="line"><span class="keyword">try</span>:</span><br><span class="line">    result = call_external_api(<span class="string">&quot;https://api.example.com/data&quot;</span>)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;成功: <span class="subst">&#123;result&#125;</span>&quot;</span>)</span><br><span class="line"><span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;最终失败: <span class="subst">&#123;e&#125;</span>&quot;</span>)</span><br></pre></td></tr></table></figure><hr><h2 id="实战项目：任务调度器"><a href="#实战项目：任务调度器" class="headerlink" title="实战项目：任务调度器"></a>实战项目：任务调度器</h2><p>通过一个完整的任务调度器项目，综合运用函数式编程知识。</p><h3 id="项目目标"><a href="#项目目标" class="headerlink" title="项目目标"></a>项目目标</h3><p>创建一个支持以下功能的任务调度器：</p><ul><li>定时执行任务</li><li>支持任务依赖</li><li>支持任务重试</li><li>记录任务状态</li></ul><h3 id="完整代码"><a href="#完整代码" class="headerlink" title="完整代码"></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><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><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#!/usr/bin/env python3</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">任务调度器 - 第二阶段实战项目</span></span><br><span class="line"><span class="string">知识点：闭包、装饰器、生成器、高阶函数</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"><span class="keyword">from</span> datetime <span class="keyword">import</span> datetime</span><br><span class="line"><span class="keyword">from</span> functools <span class="keyword">import</span> wraps</span><br><span class="line"><span class="keyword">from</span> typing <span class="keyword">import</span> <span class="type">Callable</span>, <span class="type">List</span>, <span class="type">Dict</span>, <span class="type">Any</span>, <span class="type">Optional</span></span><br><span class="line"><span class="keyword">from</span> dataclasses <span class="keyword">import</span> dataclass</span><br><span class="line"><span class="keyword">from</span> enum <span class="keyword">import</span> Enum</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">TaskStatus</span>(<span class="title class_ inherited__">Enum</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;任务状态&quot;&quot;&quot;</span></span><br><span class="line">    PENDING = <span class="string">&quot;pending&quot;</span></span><br><span class="line">    RUNNING = <span class="string">&quot;running&quot;</span></span><br><span class="line">    SUCCESS = <span class="string">&quot;success&quot;</span></span><br><span class="line">    FAILED = <span class="string">&quot;failed&quot;</span></span><br><span class="line">    RETRYING = <span class="string">&quot;retrying&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@dataclass</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">TaskResult</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;任务执行结果&quot;&quot;&quot;</span></span><br><span class="line">    task_name: <span class="built_in">str</span></span><br><span class="line">    status: TaskStatus</span><br><span class="line">    start_time: datetime</span><br><span class="line">    end_time: <span class="type">Optional</span>[datetime] = <span class="literal">None</span></span><br><span class="line">    result: <span class="type">Any</span> = <span class="literal">None</span></span><br><span class="line">    error: <span class="type">Optional</span>[<span class="built_in">str</span>] = <span class="literal">None</span></span><br><span class="line">    attempts: <span class="built_in">int</span> = <span class="number">1</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">timer</span>(<span class="params">func</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;计时装饰器&quot;&quot;&quot;</span></span><br><span class="line"><span class="meta">    @wraps(<span class="params">func</span>)</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">wrapper</span>(<span class="params">*args, **kwargs</span>):</span><br><span class="line">        start = time.time()</span><br><span class="line">        result = func(*args, **kwargs)</span><br><span class="line">        elapsed = time.time() - start</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;[Timer] <span class="subst">&#123;func.__name__&#125;</span> 执行耗时: <span class="subst">&#123;elapsed:<span class="number">.3</span>f&#125;</span>s&quot;</span>)</span><br><span class="line">        <span class="keyword">return</span> result</span><br><span class="line">    <span class="keyword">return</span> wrapper</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">retry_on_failure</span>(<span class="params">max_retries=<span class="number">3</span>, delay=<span class="number">1</span></span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;重试装饰器&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">decorator</span>(<span class="params">func</span>):</span><br><span class="line"><span class="meta">        @wraps(<span class="params">func</span>)</span></span><br><span class="line">        <span class="keyword">def</span> <span class="title function_">wrapper</span>(<span class="params">*args, **kwargs</span>):</span><br><span class="line">            last_error = <span class="literal">None</span></span><br><span class="line">            <span class="keyword">for</span> attempt <span class="keyword">in</span> <span class="built_in">range</span>(max_retries):</span><br><span class="line">                <span class="keyword">try</span>:</span><br><span class="line">                    <span class="keyword">return</span> func(*args, **kwargs)</span><br><span class="line">                <span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">                    last_error = e</span><br><span class="line">                    <span class="keyword">if</span> attempt &lt; max_retries - <span class="number">1</span>:</span><br><span class="line">                        <span class="built_in">print</span>(<span class="string">f&quot;[Retry] <span class="subst">&#123;func.__name__&#125;</span> 第<span class="subst">&#123;attempt + <span class="number">1</span>&#125;</span>次失败，<span class="subst">&#123;delay&#125;</span>秒后重试...&quot;</span>)</span><br><span class="line">                        time.sleep(delay)</span><br><span class="line">            <span class="keyword">raise</span> last_error</span><br><span class="line">        <span class="keyword">return</span> wrapper</span><br><span class="line">    <span class="keyword">return</span> decorator</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">TaskScheduler</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;任务调度器&quot;&quot;&quot;</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="variable language_">self</span>.tasks: <span class="type">Dict</span>[<span class="built_in">str</span>, <span class="type">Callable</span>] = &#123;&#125;</span><br><span class="line">        <span class="variable language_">self</span>.dependencies: <span class="type">Dict</span>[<span class="built_in">str</span>, <span class="type">List</span>[<span class="built_in">str</span>]] = &#123;&#125;</span><br><span class="line">        <span class="variable language_">self</span>.history: <span class="type">List</span>[TaskResult] = []</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">register</span>(<span class="params">self, name: <span class="built_in">str</span>, depends_on: <span class="type">List</span>[<span class="built_in">str</span>] = <span class="literal">None</span></span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;注册任务装饰器&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">def</span> <span class="title function_">decorator</span>(<span class="params">func</span>):</span><br><span class="line">            <span class="variable language_">self</span>.tasks[name] = func</span><br><span class="line">            <span class="variable language_">self</span>.dependencies[name] = depends_on <span class="keyword">or</span> []</span><br><span class="line">            <span class="keyword">return</span> func</span><br><span class="line">        <span class="keyword">return</span> decorator</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">_check_dependencies</span>(<span class="params">self, task_name: <span class="built_in">str</span></span>) -&gt; <span class="built_in">bool</span>:</span><br><span class="line">        <span class="string">&quot;&quot;&quot;检查任务依赖是否满足&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">for</span> dep <span class="keyword">in</span> <span class="variable language_">self</span>.dependencies.get(task_name, []):</span><br><span class="line">            dep_results = [r <span class="keyword">for</span> r <span class="keyword">in</span> <span class="variable language_">self</span>.history <span class="keyword">if</span> r.task_name == dep]</span><br><span class="line">            <span class="keyword">if</span> <span class="keyword">not</span> dep_results <span class="keyword">or</span> dep_results[-<span class="number">1</span>].status != TaskStatus.SUCCESS:</span><br><span class="line">                <span class="keyword">return</span> <span class="literal">False</span></span><br><span class="line">        <span class="keyword">return</span> <span class="literal">True</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">_get_execution_order</span>(<span class="params">self</span>) -&gt; <span class="type">List</span>[<span class="built_in">str</span>]:</span><br><span class="line">        <span class="string">&quot;&quot;&quot;获取拓扑排序后的执行顺序&quot;&quot;&quot;</span></span><br><span class="line">        visited = <span class="built_in">set</span>()</span><br><span class="line">        order = []</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">def</span> <span class="title function_">visit</span>(<span class="params">task_name</span>):</span><br><span class="line">            <span class="keyword">if</span> task_name <span class="keyword">in</span> visited:</span><br><span class="line">                <span class="keyword">return</span></span><br><span class="line">            visited.add(task_name)</span><br><span class="line">            <span class="keyword">for</span> dep <span class="keyword">in</span> <span class="variable language_">self</span>.dependencies.get(task_name, []):</span><br><span class="line">                visit(dep)</span><br><span class="line">            order.append(task_name)</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">for</span> task_name <span class="keyword">in</span> <span class="variable language_">self</span>.tasks:</span><br><span class="line">            visit(task_name)</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> order</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">run_task</span>(<span class="params">self, name: <span class="built_in">str</span></span>) -&gt; TaskResult:</span><br><span class="line">        <span class="string">&quot;&quot;&quot;执行单个任务&quot;&quot;&quot;</span></span><br><span class="line">        start_time = datetime.now()</span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 检查依赖</span></span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> <span class="variable language_">self</span>._check_dependencies(name):</span><br><span class="line">            <span class="keyword">return</span> TaskResult(</span><br><span class="line">                task_name=name,</span><br><span class="line">                status=TaskStatus.FAILED,</span><br><span class="line">                start_time=start_time,</span><br><span class="line">                end_time=datetime.now(),</span><br><span class="line">                error=<span class="string">&quot;依赖任务未完成&quot;</span></span><br><span class="line">            )</span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 执行任务</span></span><br><span class="line">        <span class="keyword">try</span>:</span><br><span class="line">            result = <span class="variable language_">self</span>.tasks[name]()</span><br><span class="line">            <span class="keyword">return</span> TaskResult(</span><br><span class="line">                task_name=name,</span><br><span class="line">                status=TaskStatus.SUCCESS,</span><br><span class="line">                start_time=start_time,</span><br><span class="line">                end_time=datetime.now(),</span><br><span class="line">                result=result</span><br><span class="line">            )</span><br><span class="line">        <span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">            <span class="keyword">return</span> TaskResult(</span><br><span class="line">                task_name=name,</span><br><span class="line">                status=TaskStatus.FAILED,</span><br><span class="line">                start_time=start_time,</span><br><span class="line">                end_time=datetime.now(),</span><br><span class="line">                error=<span class="built_in">str</span>(e)</span><br><span class="line">            )</span><br><span class="line">    </span><br><span class="line"><span class="meta">    @timer</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">run_all</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;执行所有任务（按依赖顺序）&quot;&quot;&quot;</span></span><br><span class="line">        order = <span class="variable language_">self</span>._get_execution_order()</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;\n[Scheduler] 执行顺序: <span class="subst">&#123;<span class="string">&#x27; → &#x27;</span>.join(order)&#125;</span>\n&quot;</span>)</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">for</span> task_name <span class="keyword">in</span> order:</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">f&quot;[Scheduler] 开始执行: <span class="subst">&#123;task_name&#125;</span>&quot;</span>)</span><br><span class="line">            result = <span class="variable language_">self</span>.run_task(task_name)</span><br><span class="line">            <span class="variable language_">self</span>.history.append(result)</span><br><span class="line">            </span><br><span class="line">            <span class="keyword">if</span> result.status == TaskStatus.SUCCESS:</span><br><span class="line">                <span class="built_in">print</span>(<span class="string">f&quot;[Scheduler] ✓ <span class="subst">&#123;task_name&#125;</span> 完成&quot;</span>)</span><br><span class="line">            <span class="keyword">else</span>:</span><br><span class="line">                <span class="built_in">print</span>(<span class="string">f&quot;[Scheduler] ✗ <span class="subst">&#123;task_name&#125;</span> 失败: <span class="subst">&#123;result.error&#125;</span>&quot;</span>)</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_summary</span>(<span class="params">self</span>) -&gt; <span class="built_in">str</span>:</span><br><span class="line">        <span class="string">&quot;&quot;&quot;获取执行摘要&quot;&quot;&quot;</span></span><br><span class="line">        success = <span class="built_in">sum</span>(<span class="number">1</span> <span class="keyword">for</span> r <span class="keyword">in</span> <span class="variable language_">self</span>.history <span class="keyword">if</span> r.status == TaskStatus.SUCCESS)</span><br><span class="line">        failed = <span class="built_in">sum</span>(<span class="number">1</span> <span class="keyword">for</span> r <span class="keyword">in</span> <span class="variable language_">self</span>.history <span class="keyword">if</span> r.status == TaskStatus.FAILED)</span><br><span class="line">        total_time = <span class="built_in">sum</span>(</span><br><span class="line">            (r.end_time - r.start_time).total_seconds()</span><br><span class="line">            <span class="keyword">for</span> r <span class="keyword">in</span> <span class="variable language_">self</span>.history</span><br><span class="line">            <span class="keyword">if</span> r.end_time</span><br><span class="line">        )</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> <span class="string">f&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">执行摘要:</span></span><br><span class="line"><span class="string">  总任务数: <span class="subst">&#123;<span class="built_in">len</span>(self.history)&#125;</span></span></span><br><span class="line"><span class="string">  成功: <span class="subst">&#123;success&#125;</span></span></span><br><span class="line"><span class="string">  失败: <span class="subst">&#123;failed&#125;</span></span></span><br><span class="line"><span class="string">  总耗时: <span class="subst">&#123;total_time:<span class="number">.2</span>f&#125;</span>s</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ============== 使用示例 ==============</span></span><br><span class="line"></span><br><span class="line">scheduler = TaskScheduler()</span><br><span class="line"></span><br><span class="line"><span class="meta">@scheduler.register(<span class="params"><span class="string">&quot;fetch_data&quot;</span></span>)</span></span><br><span class="line"><span class="meta">@retry_on_failure(<span class="params">max_retries=<span class="number">2</span></span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">fetch_data</span>():</span><br><span class="line">    <span class="string">&quot;&quot;&quot;获取数据&quot;&quot;&quot;</span></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;  → 从数据库获取数据...&quot;</span>)</span><br><span class="line">    time.sleep(<span class="number">0.5</span>)</span><br><span class="line">    <span class="keyword">return</span> &#123;<span class="string">&quot;users&quot;</span>: [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>], <span class="string">&quot;orders&quot;</span>: [<span class="number">100</span>, <span class="number">200</span>]&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@scheduler.register(<span class="params"><span class="string">&quot;process_data&quot;</span>, depends_on=[<span class="string">&quot;fetch_data&quot;</span>]</span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">process_data</span>():</span><br><span class="line">    <span class="string">&quot;&quot;&quot;处理数据&quot;&quot;&quot;</span></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;  → 处理数据...&quot;</span>)</span><br><span class="line">    time.sleep(<span class="number">0.3</span>)</span><br><span class="line">    <span class="keyword">return</span> &#123;<span class="string">&quot;processed&quot;</span>: <span class="number">6</span>&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@scheduler.register(<span class="params"><span class="string">&quot;generate_report&quot;</span>, depends_on=[<span class="string">&quot;process_data&quot;</span>]</span>)</span></span><br><span class="line"><span class="meta">@timer</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">generate_report</span>():</span><br><span class="line">    <span class="string">&quot;&quot;&quot;生成报告&quot;&quot;&quot;</span></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;  → 生成报告...&quot;</span>)</span><br><span class="line">    time.sleep(<span class="number">0.2</span>)</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;report_20240101.pdf&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@scheduler.register(<span class="params"><span class="string">&quot;send_notification&quot;</span>, depends_on=[<span class="string">&quot;generate_report&quot;</span>]</span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">send_notification</span>():</span><br><span class="line">    <span class="string">&quot;&quot;&quot;发送通知&quot;&quot;&quot;</span></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;  → 发送邮件通知...&quot;</span>)</span><br><span class="line">    time.sleep(<span class="number">0.1</span>)</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;sent&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;=&quot;</span> * <span class="number">50</span>)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;       任务调度器 v1.0&quot;</span>)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;=&quot;</span> * <span class="number">50</span>)</span><br><span class="line">    </span><br><span class="line">    scheduler.run_all()</span><br><span class="line">    <span class="built_in">print</span>(scheduler.get_summary())</span><br></pre></td></tr></table></figure><h3 id="运行示例"><a href="#运行示例" class="headerlink" title="运行示例"></a>运行示例</h3><figure class="highlight plaintext"><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><span class="line">==================================================</span><br><span class="line">       任务调度器 v1.0</span><br><span class="line">==================================================</span><br><span class="line"></span><br><span class="line">[Scheduler] 执行顺序: fetch_data → process_data → generate_report → send_notification</span><br><span class="line"></span><br><span class="line">[Scheduler] 开始执行: fetch_data</span><br><span class="line">  → 从数据库获取数据...</span><br><span class="line">[Scheduler] ✓ fetch_data 完成</span><br><span class="line">[Scheduler] 开始执行: process_data</span><br><span class="line">  → 处理数据...</span><br><span class="line">[Scheduler] ✓ process_data 完成</span><br><span class="line">[Scheduler] 开始执行: generate_report</span><br><span class="line">  → 生成报告...</span><br><span class="line">[Timer] generate_report 执行耗时: 0.201s</span><br><span class="line">[Scheduler] ✓ generate_report 完成</span><br><span class="line">[Scheduler] 开始执行: send_notification</span><br><span class="line">  → 发送邮件通知...</span><br><span class="line">[Scheduler] ✓ send_notification 完成</span><br><span class="line">[Timer] run_all 执行耗时: 1.104s</span><br><span class="line"></span><br><span class="line">执行摘要:</span><br><span class="line">  总任务数: 4</span><br><span class="line">  成功: 4</span><br><span class="line">  失败: 0</span><br><span class="line">  总耗时: 1.10s</span><br></pre></td></tr></table></figure><h3 id="知识点覆盖"><a href="#知识点覆盖" class="headerlink" title="知识点覆盖"></a>知识点覆盖</h3><table><thead><tr><th>功能</th><th>涉及知识点</th></tr></thead><tbody><tr><td>装饰器</td><td>参数化装饰器、<code>@wraps</code>、嵌套装饰器</td></tr><tr><td>闭包</td><td>装饰器内部状态保持</td></tr><tr><td>生成器</td><td>任务执行顺序生成</td></tr><tr><td>高阶函数</td><td><code>register</code> 返回装饰器</td></tr><tr><td>数据类</td><td><code>@dataclass</code> 定义结果对象</td></tr><tr><td>枚举</td><td><code>Enum</code> 定义状态</td></tr></tbody></table><h3 id="进阶练习"><a href="#进阶练习" class="headerlink" title="进阶练习"></a>进阶练习</h3><p>尝试为任务调度器添加以下功能：</p><ol><li>支持定时任务（cron 表达式）</li><li>支持并行执行无依赖的任务</li><li>添加任务进度追踪</li><li>支持任务超时设置</li><li>添加任务取消功能</li></ol><hr><blockquote><p><strong>工程师寄语</strong>：函数式编程的精髓不在于用了多少 <code>lambda</code> 或 <code>map</code>，而在于能否用<strong>纯函数</strong>和<strong>不可变数据</strong>来构建系统。在 Python 中，适度地借鉴函数式思想（如避免副作用、使用生成器处理大数据流），能让你的代码更健壮、更可测试。</p></blockquote>]]>
    </content>
    <id>https://itdxb.cn/2026/07/22/python/document/%E3%80%90Python%E3%80%91%E5%87%BD%E6%95%B0%E4%B8%8E%E5%87%BD%E6%95%B0%E5%BC%8F%E7%BC%96%E7%A8%8B%EF%BC%88%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%EF%BC%89/</id>
    <link href="https://itdxb.cn/2026/07/22/python/document/%E3%80%90Python%E3%80%91%E5%87%BD%E6%95%B0%E4%B8%8E%E5%87%BD%E6%95%B0%E5%BC%8F%E7%BC%96%E7%A8%8B%EF%BC%88%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%EF%BC%89/"/>
    <published>2026-07-22T03:00:00.000Z</published>
    <summary>从函数定义到函数式编程范式，系统掌握 Python 中&quot;一等公民&quot;函数、参数系统、作用域、闭包、装饰器与生成器。</summary>
    <title>【Python】函数与函数式编程（第二阶段）</title>
    <updated>2026-07-24T03:16:06.834Z</updated>
  </entry>
  <entry>
    <author>
      <name>Super Bing</name>
    </author>
    <category term="Python" scheme="https://itdxb.cn/categories/Python/"/>
    <category term="教程" scheme="https://itdxb.cn/tags/%E6%95%99%E7%A8%8B/"/>
    <category term="Python" scheme="https://itdxb.cn/tags/Python/"/>
    <category term="基础语法" scheme="https://itdxb.cn/tags/%E5%9F%BA%E7%A1%80%E8%AF%AD%E6%B3%95/"/>
    <content>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><h2 id="阶段定位"><a href="#阶段定位" class="headerlink" title="阶段定位"></a>阶段定位</h2><p>建立 Python 编程的根基，掌握语言核心机制与开发环境配置。此阶段的重点是”知其然且知其所以然”，养成 Pythonic 的编码直觉。</p><hr><h2 id="1-环境搭建与工具链"><a href="#1-环境搭建与工具链" class="headerlink" title="1. 环境搭建与工具链"></a>1. 环境搭建与工具链</h2><h3 id="1-1-Python-解释器"><a href="#1-1-Python-解释器" class="headerlink" title="1.1 Python 解释器"></a>1.1 Python 解释器</h3><p>Python 是解释型语言，代码由解释器逐行翻译执行。工作中常见的解释器实现有三种：</p><table><thead><tr><th>解释器</th><th>适用场景</th><th>特点</th></tr></thead><tbody><tr><td><strong>CPython</strong></td><td>通用开发、生产环境（最主流）</td><td>官方标准实现，C 语言编写，兼容性与生态最好</td></tr><tr><td><strong>PyPy</strong></td><td>计算密集型、长时运行的服务端</td><td>自带 JIT 编译器，纯 Python 代码可提速 5-10 倍，但启动慢、C 扩展兼容性有限</td></tr><tr><td><strong>Anaconda</strong></td><td>数据科学、机器学习</td><td>预装 150+ 科学计算包，自带 <code>conda</code> 包管理器</td></tr></tbody></table><p><strong>Python 执行流程详解</strong>：理解 Python 代码从编写到执行的完整过程，是深入理解语言机制的基础。</p><figure class="highlight plaintext"><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><span class="line">源代码 (.py)</span><br><span class="line">    ↓  编译（Compile）</span><br><span class="line">字节码 (.pyc)    ← 可被缓存，下次直接加载</span><br><span class="line">    ↓  解释执行（PVM）</span><br><span class="line">运行结果</span><br></pre></td></tr></table></figure><ol><li><strong>编译阶段</strong>：Python 解释器将 <code>.py</code> 源码编译为字节码（Bytecode），字节码是平台无关的中间代码，存储在 <code>__pycache__</code> 目录下的 <code>.pyc</code> 文件中。如果源码未修改且 <code>.pyc</code> 存在，则跳过编译直接加载字节码。</li><li><strong>执行阶段</strong>：Python 虚拟机（PVM, Python Virtual Machine）逐条读取字节码并执行。PVM 是 Python 运行的核心引擎，负责将字节码翻译为机器可执行的指令。</li></ol><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 查看字节码</span></span><br><span class="line">import dis</span><br><span class="line">dis.dis(<span class="string">&quot;x = 1; y = x + 2&quot;</span>)</span><br><span class="line"><span class="comment"># 输出：</span></span><br><span class="line"><span class="comment">#   0 LOAD_CONST               0 (1)</span></span><br><span class="line"><span class="comment">#   2 STORE_NAME               0 (x)</span></span><br><span class="line"><span class="comment">#   4 LOAD_NAME                0 (x)</span></span><br><span class="line"><span class="comment">#   6 LOAD_CONST               1 (2)</span></span><br><span class="line"><span class="comment">#   8 BINARY_ADD</span></span><br><span class="line"><span class="comment">#  10 STORE_NAME               1 (y)</span></span><br></pre></td></tr></table></figure><p><strong>CPython vs PyPy 性能对比数据</strong>：</p><table><thead><tr><th>基准测试</th><th>CPython 3.11</th><th>PyPy 7.3 (3.10 兼容)</th><th>提速比</th></tr></thead><tbody><tr><td>纯数值计算（斐波那契）</td><td>基准</td><td>约 5-8x 快</td><td>⬆</td></tr><tr><td>字符串处理</td><td>基准</td><td>约 3-5x 快</td><td>⬆</td></tr><tr><td>Django 请求处理</td><td>基准</td><td>约 2-3x 快</td><td>⬆</td></tr><tr><td>含 C 扩展的库（NumPy）</td><td>基准</td><td>可能更慢或兼容性问题</td><td>⬇&#x2F;➡</td></tr><tr><td>启动时间</td><td>快</td><td>较慢（JIT 预热）</td><td>⬇</td></tr></tbody></table><p><strong>Python 版本选择建议</strong>：</p><table><thead><tr><th>版本</th><th>发布时间</th><th>核心新特性</th><th>推荐场景</th></tr></thead><tbody><tr><td><strong>3.10</strong></td><td>2021.10</td><td><code>match/case</code> 模式匹配、结构模式匹配、精确错误定位</td><td>稳定生产环境</td></tr><tr><td><strong>3.11</strong></td><td>2022.10</td><td>速度提升 10-60%、ExceptionGroup&#x2F;except*、Self 类型</td><td>当前最推荐的生产版本</td></tr><tr><td><strong>3.12</strong></td><td>2023.10</td><td>更快的推导式、type 参数语法（PEP 695）、灵活的 f-string</td><td>新项目、尝鲜</td></tr><tr><td><strong>3.13</strong></td><td>2024.10</td><td>实验性 GIL-free 模式、改进交互解释器、JIT 实验性编译器</td><td>前沿探索</td></tr></tbody></table><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><span class="line"><span class="comment"># 查看当前解释器信息</span></span><br><span class="line">python -c <span class="string">&quot;import sys; print(sys.executable); print(sys.version)&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Python 3.11 的精确错误定位示例</span></span><br><span class="line"><span class="comment"># 旧版报错：SyntaxError: invalid syntax</span></span><br><span class="line"><span class="comment"># 3.11+ 报错：SyntaxError: &#x27;(&#x27; was never closed（精确指出未闭合的括号位置）</span></span><br></pre></td></tr></table></figure><blockquote><p><strong>面试真题</strong>：Python 是解释型语言还是编译型语言？请解释 Python 的执行流程。</p><p><strong>易错点</strong>：Python 并非纯解释型语言。它先将源码编译成字节码（.pyc），再由 PVM 解释执行字节码。因此 Python 兼具编译和解释的特征。说”Python 是纯解释型语言”是不准确的。</p></blockquote><p><img src="https://trae-api-cn.mchost.guru/api/ide/v1/text_to_image?prompt=Python%20interpreter%20architecture%20diagram%20showing%20CPython%20PyPy%20Anaconda%20comparison,%20clean%20technical%20illustration,%20blue%20and%20white%20color%20scheme,%20infographic%20style&image_size=landscape_16_9" alt="Python interpreter architecture diagram showing CPython PyPy Anaconda comparison, clean technical illustration, blue and white color scheme, infographic style"></p><h3 id="1-2-虚拟环境"><a href="#1-2-虚拟环境" class="headerlink" title="1.2 虚拟环境"></a>1.2 虚拟环境</h3><p>虚拟环境是 Python 项目的”隔离舱”，每个项目拥有独立的依赖空间。</p><p><strong>虚拟环境工作原理</strong>：虚拟环境通过修改 <code>sys.prefix</code> 和 <code>PATH</code> 环境变量来实现隔离，而非物理复制整个 Python 解释器。</p><figure class="highlight plaintext"><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><span class="line">系统 Python (/usr/bin/python3)</span><br><span class="line">    │</span><br><span class="line">    ├── site-packages/          ← 全局第三方包</span><br><span class="line">    │       ├── requests/</span><br><span class="line">    │       └── numpy/</span><br><span class="line">    │</span><br><span class="line">    ├── 项目A (.venv/)           ← 独立环境</span><br><span class="line">    │       ├── pyvenv.cfg       ← 指向基础 Python</span><br><span class="line">    │       ├── lib/</span><br><span class="line">    │       │   └── site-packages/</span><br><span class="line">    │       │       └── requests==2.28.0</span><br><span class="line">    │       └── bin/python → 系统Python（修改了sys.prefix）</span><br><span class="line">    │</span><br><span class="line">    └── 项目B (.venv/)           ← 独立环境</span><br><span class="line">            ├── pyvenv.cfg</span><br><span class="line">            └── lib/</span><br><span class="line">                └── site-packages/</span><br><span class="line">                    └── requests==2.31.0</span><br></pre></td></tr></table></figure><p>关键机制：</p><ul><li><strong>软链接</strong>：虚拟环境中的 Python 可执行文件是指向系统 Python 的软链接（或脚本），但 <code>sys.prefix</code> 被修改为虚拟环境目录。</li><li><strong>路径优先</strong>：激活虚拟环境后，<code>PATH</code> 中虚拟环境的 <code>bin/</code> 目录排在最前，确保优先使用虚拟环境中的 <code>pip</code> 和 <code>python</code>。</li><li><strong>隔离原理</strong>：<code>site-packages</code> 目录独立，每个虚拟环境有自己的第三方包目录。</li></ul><table><thead><tr><th>工具</th><th>命令示例</th><th>适用场景</th></tr></thead><tbody><tr><td><code>venv</code>（内置）</td><td><code>python -m venv .venv</code></td><td>最轻量、无额外依赖，日常开发首选</td></tr><tr><td><code>virtualenv</code></td><td><code>virtualenv -p python3.11 venv</code></td><td>需要指定不同 Python 版本时使用</td></tr><tr><td><code>conda</code></td><td><code>conda create -n myenv python=3.11</code></td><td>数据科学栈，管理非 Python 依赖（如 CUDA）</td></tr></tbody></table><p><strong>pip 配置文件</strong>（加速下载与镜像源配置）：</p><figure class="highlight ini"><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><span class="line"><span class="comment"># pip.conf（Linux/macOS: ~/.pip/pip.conf，Windows: %APPDATA%\pip\pip.ini）</span></span><br><span class="line"><span class="section">[global]</span></span><br><span class="line"><span class="attr">index-url</span> = https://pypi.tuna.tsinghua.edu.cn/simple</span><br><span class="line"><span class="attr">trusted-host</span> = pypi.tuna.tsinghua.edu.cn</span><br><span class="line"></span><br><span class="line"><span class="section">[install]</span></span><br><span class="line"><span class="comment"># 每次安装都生成 requirements 文件（可选）</span></span><br><span class="line"><span class="attr">require-virtualenv</span> = <span class="literal">true</span>    <span class="comment"># 禁止在全局环境安装包，强制使用虚拟环境</span></span><br></pre></td></tr></table></figure><p><strong>pyproject.toml 配置示例</strong>（现代 Python 项目标准）：</p><figure class="highlight toml"><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><span class="line"><span class="section">[project]</span></span><br><span class="line"><span class="attr">name</span> = <span class="string">&quot;my-project&quot;</span></span><br><span class="line"><span class="attr">version</span> = <span class="string">&quot;0.1.0&quot;</span></span><br><span class="line"><span class="attr">description</span> = <span class="string">&quot;A modern Python project&quot;</span></span><br><span class="line"><span class="attr">requires-python</span> = <span class="string">&quot;&gt;=3.10&quot;</span></span><br><span class="line"><span class="attr">dependencies</span> = [</span><br><span class="line">    <span class="string">&quot;requests&gt;=2.28&quot;</span>,</span><br><span class="line">    <span class="string">&quot;rich&gt;=13.0&quot;</span>,</span><br><span class="line">]</span><br><span class="line"></span><br><span class="line"><span class="section">[project.optional-dependencies]</span></span><br><span class="line"><span class="attr">dev</span> = [<span class="string">&quot;pytest&quot;</span>, <span class="string">&quot;black&quot;</span>, <span class="string">&quot;ruff&quot;</span>]</span><br><span class="line"></span><br><span class="line"><span class="section">[tool.ruff]</span></span><br><span class="line"><span class="attr">line-length</span> = <span class="number">88</span></span><br><span class="line"><span class="attr">select</span> = [<span class="string">&quot;E&quot;</span>, <span class="string">&quot;F&quot;</span>, <span class="string">&quot;W&quot;</span>]</span><br><span class="line"></span><br><span class="line"><span class="section">[tool.pytest.ini_options]</span></span><br><span class="line"><span class="attr">testpaths</span> = [<span class="string">&quot;tests&quot;</span>]</span><br></pre></td></tr></table></figure><p><strong>最佳实践</strong>：</p><ol><li>每个项目根目录创建 <code>.venv</code> 或 <code>venv</code> 目录，并加入 <code>.gitignore</code>。</li><li>养成”进入项目先激活环境”的肌肉记忆：<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></pre></td><td class="code"><pre><span class="line"><span class="comment"># Windows (PowerShell)</span></span><br><span class="line">.\.venv\Scripts\Activate.ps1</span><br><span class="line"><span class="comment"># macOS / Linux</span></span><br><span class="line"><span class="built_in">source</span> .venv/bin/activate</span><br></pre></td></tr></table></figure></li><li>导出依赖锁定文件，确保团队环境一致：<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></pre></td><td class="code"><pre><span class="line">pip freeze &gt; requirements.txt       <span class="comment"># 基础方式</span></span><br><span class="line">pip install pip-tools</span><br><span class="line">pip-compile requirements.in        <span class="comment"># 更严谨的依赖解析</span></span><br></pre></td></tr></table></figure></li></ol><p><strong>常见环境问题排查</strong>：</p><table><thead><tr><th>问题</th><th>原因</th><th>解决方案</th></tr></thead><tbody><tr><td><code>ModuleNotFoundError</code></td><td>未安装包 &#x2F; 未激活虚拟环境</td><td><code>pip install &lt;pkg&gt;</code> 或激活 <code>.venv</code></td></tr><tr><td><code>pip install</code> 安装到全局</td><td>未激活虚拟环境</td><td>先激活虚拟环境，或配置 <code>require-virtualenv = true</code></td></tr><tr><td>权限被拒（PowerShell）</td><td>执行策略限制</td><td><code>Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned</code></td></tr><tr><td><code>python</code> 命令找不到</td><td>Windows 未添加 PATH</td><td>安装时勾选 “Add Python to PATH”，或手动添加</td></tr><tr><td>版本冲突</td><td>多个 Python 版本混用</td><td>使用 <code>py -3.11</code>（Windows launcher）或 <code>python3.11</code> 指定版本</td></tr></tbody></table><blockquote><p><strong>面试真题</strong>：虚拟环境是如何实现隔离的？<code>venv</code> 和 <code>virtualenv</code> 有什么区别？</p><p><strong>易错点</strong>：虚拟环境不是完整复制 Python 解释器，而是通过修改 <code>sys.prefix</code> 和 <code>PATH</code> 实现隔离。<code>venv</code> 是 Python 3.3+ 内置模块，功能精简；<code>virtualenv</code> 是第三方包，支持更多特性（如指定不同 Python 版本、更快创建速度）。</p></blockquote><p><img src="https://trae-api-cn.mchost.guru/api/ide/v1/text_to_image?prompt=Python%20virtual%20environment%20isolation%20concept%20diagram%20showing%20multiple%20independent%20project%20environments%20with%20separate%20packages,%20clean%20infographic,%20green%20and%20white&image_size=landscape_16_9" alt="Python virtual environment isolation concept diagram showing multiple independent project environments with separate packages, clean infographic, green and white"></p><h3 id="1-3-IDE-编辑器选择"><a href="#1-3-IDE-编辑器选择" class="headerlink" title="1.3 IDE &#x2F; 编辑器选择"></a>1.3 IDE &#x2F; 编辑器选择</h3><table><thead><tr><th>工具</th><th>定位</th><th>核心优势</th></tr></thead><tbody><tr><td><strong>PyCharm</strong></td><td>专业级 IDE</td><td>代码分析、重构、调试、数据库工具集成最完善</td></tr><tr><td><strong>VS Code</strong></td><td>轻量编辑器 + 插件</td><td>启动快、插件丰富、多语言通用</td></tr><tr><td><strong>Jupyter</strong></td><td>交互式探索</td><td>数据分析、算法验证、教学演示的神器</td></tr></tbody></table><p><strong>各 IDE 推荐插件列表</strong>：</p><p><strong>PyCharm 插件</strong>：</p><ul><li><code>.ignore</code>：生成各类 <code>.gitignore</code> 模板</li><li><code>Key Promoter X</code>：快捷键提示，帮助脱离鼠标</li><li><code>Rainbow Brackets</code>：彩色括号匹配</li><li><code>String Manipulation</code>：字符串格式转换（驼峰&#x2F;下划线&#x2F;常量）</li><li><code>GitToolBox</code>：增强 Git 集成</li><li><code>.env files support</code>：环境变量文件高亮</li></ul><p><strong>VS Code 插件</strong>：</p><ul><li><code>Python</code>（Microsoft）：核心 Python 支持（IntelliSense、调试）</li><li><code>Pylance</code>：高性能类型检查和自动补全</li><li><code>Ruff</code>：极速 linter + formatter（替代 flake8 + isort + black）</li><li><code>Python Test Explorer</code>：测试可视化运行</li><li><code>Python Docstring Generator</code>：自动生成 docstring</li><li><code>Jupyter</code>：笔记本支持</li><li><code>Error Lens</code>：行内显示错误信息</li></ul><p><strong>调试器配置技巧</strong>：</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><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><span class="line"><span class="comment">// VS Code launch.json 配置示例</span></span><br><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;version&quot;</span><span class="punctuation">:</span> <span class="string">&quot;0.2.0&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;configurations&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">        <span class="punctuation">&#123;</span></span><br><span class="line">            <span class="attr">&quot;name&quot;</span><span class="punctuation">:</span> <span class="string">&quot;Python: Current File&quot;</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">&quot;type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;python&quot;</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">&quot;request&quot;</span><span class="punctuation">:</span> <span class="string">&quot;launch&quot;</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">&quot;program&quot;</span><span class="punctuation">:</span> <span class="string">&quot;$&#123;file&#125;&quot;</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">&quot;console&quot;</span><span class="punctuation">:</span> <span class="string">&quot;integratedTerminal&quot;</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">&quot;justMyCode&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">true</span></span>    <span class="comment">// 仅调试自己的代码，跳过库代码</span></span><br><span class="line">        <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="punctuation">&#123;</span></span><br><span class="line">            <span class="attr">&quot;name&quot;</span><span class="punctuation">:</span> <span class="string">&quot;Python: Flask&quot;</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">&quot;type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;python&quot;</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">&quot;request&quot;</span><span class="punctuation">:</span> <span class="string">&quot;launch&quot;</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">&quot;module&quot;</span><span class="punctuation">:</span> <span class="string">&quot;flask&quot;</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">&quot;env&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">                <span class="attr">&quot;FLASK_APP&quot;</span><span class="punctuation">:</span> <span class="string">&quot;app.py&quot;</span><span class="punctuation">,</span></span><br><span class="line">                <span class="attr">&quot;FLASK_DEBUG&quot;</span><span class="punctuation">:</span> <span class="string">&quot;1&quot;</span></span><br><span class="line">            <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">&quot;args&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span><span class="string">&quot;run&quot;</span><span class="punctuation">,</span> <span class="string">&quot;--port=5000&quot;</span><span class="punctuation">]</span></span><br><span class="line">        <span class="punctuation">&#125;</span></span><br><span class="line">    <span class="punctuation">]</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p><strong>调试高效技巧</strong>：</p><ul><li><strong>条件断点</strong>：右键断点 → 设置条件（如 <code>i == 100</code>），只在满足条件时暂停</li><li><strong>日志断点</strong>：不暂停执行，仅打印日志（右键断点 → 选择 “Logpoint”）</li><li><strong>调试时表达式求值</strong>：在 Debug Console 中可实时执行表达式</li><li><strong>远程调试</strong>：使用 <code>debugpy</code> 库进行远程调试</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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 使用 breakpoint()（Python 3.7+）—— 比手动 import pdb 更优雅</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">compute</span>(<span class="params">data</span>):</span><br><span class="line">    result = process(data)</span><br><span class="line">    <span class="built_in">breakpoint</span>()    <span class="comment"># 自动进入 pdb，可用 n(下一步) s(进入) c(继续) p(打印)</span></span><br><span class="line">    <span class="keyword">return</span> result</span><br></pre></td></tr></table></figure><p><strong>老工程师的选择逻辑</strong>：</p><ul><li>大型项目、Web 后端开发 → PyCharm Professional</li><li>全栈&#x2F;多语言开发、远程开发 → VS Code + Python 插件</li><li>数据探索、模型实验 → Jupyter Lab</li></ul><blockquote><p><strong>面试真题</strong>：你在项目中如何选择开发工具？如何提高调试效率？</p><p><strong>易错点</strong>：初学者常忽视 <code>breakpoint()</code> 的使用，仍然使用 <code>print()</code> 调试。在生产代码中应删除断点语句，或通过 <code>PYTHONBREAKPOINT=0</code> 环境变量禁用。</p></blockquote><h3 id="1-4-包管理"><a href="#1-4-包管理" class="headerlink" title="1.4 包管理"></a>1.4 包管理</h3><table><thead><tr><th>工具</th><th>设计哲学</th><th>核心文件</th><th>推荐度</th></tr></thead><tbody><tr><td><code>pip</code></td><td>基础安装器</td><td><code>requirements.txt</code></td><td>必会基础</td></tr><tr><td><code>poetry</code></td><td>现代全功能</td><td><code>pyproject.toml</code> + <code>poetry.lock</code></td><td>⭐ 新项目首选</td></tr><tr><td><code>pipenv</code></td><td>虚拟环境+包管理一体</td><td><code>Pipfile</code> + <code>Pipfile.lock</code></td><td>社区活跃度下降，新项目不推荐</td></tr></tbody></table><p><strong>pip 常用命令速查表</strong>：</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><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><span class="line"><span class="comment"># 安装</span></span><br><span class="line">pip install requests                    <span class="comment"># 安装最新版</span></span><br><span class="line">pip install requests==2.28.0           <span class="comment"># 指定版本</span></span><br><span class="line">pip install <span class="string">&quot;requests&gt;=2.28,&lt;3.0&quot;</span>      <span class="comment"># 版本范围</span></span><br><span class="line">pip install -r requirements.txt        <span class="comment"># 从文件批量安装</span></span><br><span class="line">pip install -e .                       <span class="comment"># 可编辑模式安装（开发本地包）</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看</span></span><br><span class="line">pip list                               <span class="comment"># 列出已安装包</span></span><br><span class="line">pip show requests                      <span class="comment"># 查看包详情</span></span><br><span class="line">pip outdated                           <span class="comment"># 查看过期包（需 pip-outdated）</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 卸载</span></span><br><span class="line">pip uninstall requests                 <span class="comment"># 卸载包</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 导出</span></span><br><span class="line">pip freeze &gt; requirements.txt          <span class="comment"># 导出依赖列表</span></span><br><span class="line">pip check                              <span class="comment"># 检查依赖冲突</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 清理</span></span><br><span class="line">pip cache purge                        <span class="comment"># 清除缓存</span></span><br><span class="line">pip autoremove                         <span class="comment"># 移除不再需要的依赖</span></span><br></pre></td></tr></table></figure><p><strong>requirements.txt vs pyproject.toml 对比</strong>：</p><table><thead><tr><th>特性</th><th><code>requirements.txt</code></th><th><code>pyproject.toml</code></th></tr></thead><tbody><tr><td>标准化</td><td>非官方约定</td><td>PEP 517&#x2F;518 官方标准</td></tr><tr><td>依赖分组</td><td>不支持（需多文件）</td><td>原生支持（optional-dependencies）</td></tr><tr><td>元数据</td><td>不支持</td><td>项目名称、版本、作者等完整元数据</td></tr><tr><td>构建系统</td><td>不涉及</td><td>可定义构建后端（setuptools&#x2F;flit&#x2F;poetry）</td></tr><tr><td>锁定版本</td><td>需手动 <code>pip freeze</code></td><td><code>poetry.lock</code> &#x2F; 精确依赖解析</td></tr><tr><td>生态兼容</td><td>pip 通用</td><td>需要构建工具（poetry&#x2F;build）</td></tr><tr><td>推荐场景</td><td>简单脚本、Docker 部署</td><td>正式项目、可发布包</td></tr></tbody></table><p><strong>Poetry 快速上手</strong>（2024 年最推荐的方案）：</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><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><span class="line"><span class="comment"># 安装</span></span><br><span class="line">pip install poetry</span><br><span class="line"></span><br><span class="line"><span class="comment"># 初始化项目</span></span><br><span class="line">poetry new my-project</span><br><span class="line"><span class="built_in">cd</span> my-project</span><br><span class="line"></span><br><span class="line"><span class="comment"># 添加依赖（自动写入 pyproject.toml 并锁定版本）</span></span><br><span class="line">poetry add requests</span><br><span class="line">poetry add pytest --group dev   <span class="comment"># 开发依赖</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 安装所有依赖（根据 poetry.lock 精确还原）</span></span><br><span class="line">poetry install</span><br><span class="line"></span><br><span class="line"><span class="comment"># 运行命令在虚拟环境中</span></span><br><span class="line">poetry run python main.py</span><br><span class="line">poetry shell                      <span class="comment"># 进入虚拟环境 shell</span></span><br></pre></td></tr></table></figure><p><strong>Poetry 项目结构</strong>：</p><figure class="highlight plaintext"><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><span class="line">my-project/</span><br><span class="line">├── pyproject.toml      ← 项目配置 + 依赖声明</span><br><span class="line">├── poetry.lock         ← 锁定的精确依赖版本（提交到 Git）</span><br><span class="line">├── README.md</span><br><span class="line">├── src/</span><br><span class="line">│   └── my_project/</span><br><span class="line">│       ├── __init__.py</span><br><span class="line">│       └── main.py</span><br><span class="line">└── tests/</span><br><span class="line">    ├── __init__.py</span><br><span class="line">    └── test_main.py</span><br></pre></td></tr></table></figure><blockquote><p><strong>面试真题</strong>：<code>requirements.txt</code> 和 <code>pyproject.toml</code> 有什么区别？如何管理项目的依赖？</p><p><strong>易错点</strong>：<code>pip freeze</code> 会导出所有包（包括间接依赖），导致 requirements.txt 臃肿。推荐使用 <code>pip-compile</code>（pip-tools）或 Poetry，只声明直接依赖，由工具自动解析间接依赖。</p></blockquote><hr><h2 id="2-基本语法"><a href="#2-基本语法" class="headerlink" title="2. 基本语法"></a>2. 基本语法</h2><h3 id="2-1-变量、标识符、关键字"><a href="#2-1-变量、标识符、关键字" class="headerlink" title="2.1 变量、标识符、关键字"></a>2.1 变量、标识符、关键字</h3><p><strong>变量本质</strong>：Python 中的变量是指向对象的”标签”（引用），而非存储数据的容器。这是理解 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><span class="line">a = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]</span><br><span class="line">b = a          <span class="comment"># b 和 a 指向同一个列表对象</span></span><br><span class="line">b.append(<span class="number">4</span>)</span><br><span class="line"><span class="built_in">print</span>(a)       <span class="comment"># [1, 2, 3, 4] —— 这是新人最常踩的坑</span></span><br></pre></td></tr></table></figure><p><strong>Python 变量内存模型图解</strong>：</p><figure class="highlight plaintext"><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><span class="line">变量名（标签）          内存中的对象</span><br><span class="line">   ┌──────┐         ┌──────────────┐</span><br><span class="line">   │  a   │────────→│ list [1,2,3] │  type: list</span><br><span class="line">   └──────┘         │   id: 140xxx │  value: [1,2,3,4]</span><br><span class="line">   ┌──────┐         │  refcnt: 2   │  ← 引用计数为2（a和b都指向它）</span><br><span class="line">   │  b   │────────→│              │</span><br><span class="line">   └──────┘         └──────────────┘</span><br></pre></td></tr></table></figure><p>Python 的变量赋值本质是”让一个名字指向一个对象”，而非”把数据装进盒子”。因此，多个变量可以指向同一个对象。</p><p><strong><code>id()</code> 函数和 <code>is</code> 运算符的关系</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><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><span class="line"><span class="comment"># id() 返回对象的唯一标识（CPython 中是内存地址）</span></span><br><span class="line">a = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]</span><br><span class="line">b = a</span><br><span class="line">c = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">id</span>(a))        <span class="comment"># 例如 140234567890752</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">id</span>(b))        <span class="comment"># 与 a 相同</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">id</span>(c))        <span class="comment"># 不同！c 是一个新对象</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># is 判断两个变量是否指向同一个对象（id 是否相同）</span></span><br><span class="line"><span class="built_in">print</span>(a <span class="keyword">is</span> b)       <span class="comment"># True —— 同一个对象</span></span><br><span class="line"><span class="built_in">print</span>(a <span class="keyword">is</span> c)       <span class="comment"># False —— 不同对象（即使值相同）</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># == 判断两个对象的值是否相等</span></span><br><span class="line"><span class="built_in">print</span>(a == c)       <span class="comment"># True —— 值相同</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 经典陷阱：小整数和字符串的 is 比较</span></span><br><span class="line">a = <span class="number">256</span></span><br><span class="line">b = <span class="number">256</span></span><br><span class="line"><span class="built_in">print</span>(a <span class="keyword">is</span> b)       <span class="comment"># True —— 小整数池范围内</span></span><br><span class="line"></span><br><span class="line">c = <span class="number">257</span></span><br><span class="line">d = <span class="number">257</span></span><br><span class="line"><span class="built_in">print</span>(c <span class="keyword">is</span> d)       <span class="comment"># 可能是 False！—— 超出小整数池范围</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 正确做法：比较值始终用 ==，只有判断 None/True/False 时用 is</span></span><br><span class="line">x = <span class="literal">None</span></span><br><span class="line"><span class="keyword">if</span> x <span class="keyword">is</span> <span class="literal">None</span>:       <span class="comment"># ✅ 正确</span></span><br><span class="line">    <span class="keyword">pass</span></span><br><span class="line"><span class="keyword">if</span> x == <span class="literal">None</span>:       <span class="comment"># ❌ 不推荐</span></span><br><span class="line">    <span class="keyword">pass</span></span><br></pre></td></tr></table></figure><p><strong>小整数池机制（-5 ~ 256）</strong>：</p><p>CPython 在启动时预创建 -5 到 256 的整数对象，这些对象在整个解释器生命周期内被缓存复用。因此，任何指向这些值的变量都会指向同一个对象。</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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 小整数池内的对象被缓存</span></span><br><span class="line">a = -<span class="number">5</span></span><br><span class="line">b = -<span class="number">5</span></span><br><span class="line"><span class="built_in">print</span>(a <span class="keyword">is</span> b)   <span class="comment"># True —— 指向同一个缓存对象</span></span><br><span class="line"></span><br><span class="line">a = <span class="number">256</span></span><br><span class="line">b = <span class="number">256</span></span><br><span class="line"><span class="built_in">print</span>(a <span class="keyword">is</span> b)   <span class="comment"># True —— 仍在缓存范围内</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 超出范围的对象每次创建新实例</span></span><br><span class="line">a = <span class="number">257</span></span><br><span class="line">b = <span class="number">257</span></span><br><span class="line"><span class="built_in">print</span>(a <span class="keyword">is</span> b)   <span class="comment"># False（在交互模式中）—— 但在同一个代码块中可能为 True（编译器优化）</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 注意：在同一个代码块（如脚本文件）中，编译器可能做常量折叠优化</span></span><br><span class="line"><span class="comment"># 以下在脚本文件中运行时可能为 True</span></span><br><span class="line">x = <span class="number">257</span>; y = <span class="number">257</span></span><br><span class="line"><span class="built_in">print</span>(x <span class="keyword">is</span> y)   <span class="comment"># 可能为 True（编译器优化为同一常量）</span></span><br></pre></td></tr></table></figure><p><strong>字符串驻留机制</strong>：</p><p>Python 会将符合特定规则的字符串进行”驻留”（intern），使相同内容的字符串共享同一个对象，节省内存。</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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 符合标识符规则的字符串会被驻留</span></span><br><span class="line">a = <span class="string">&quot;hello&quot;</span></span><br><span class="line">b = <span class="string">&quot;hello&quot;</span></span><br><span class="line"><span class="built_in">print</span>(a <span class="keyword">is</span> b)       <span class="comment"># True —— 被驻留</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 包含特殊字符的字符串不一定驻留</span></span><br><span class="line">a = <span class="string">&quot;hello!&quot;</span></span><br><span class="line">b = <span class="string">&quot;hello!&quot;</span></span><br><span class="line"><span class="built_in">print</span>(a <span class="keyword">is</span> b)       <span class="comment"># 可能为 False —— 不符合标识符规则</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 显式驻留</span></span><br><span class="line"><span class="keyword">import</span> sys</span><br><span class="line">a = sys.intern(<span class="string">&quot;hello!&quot;</span>)</span><br><span class="line">b = sys.intern(<span class="string">&quot;hello!&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(a <span class="keyword">is</span> b)       <span class="comment"># True —— 手动驻留后共享对象</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 字符串驻留的应用场景：大量重复字符串的字典键（如日志分析）</span></span><br><span class="line"><span class="comment"># 驻留后可大幅减少内存使用和比较时间（is 比 == 快）</span></span><br></pre></td></tr></table></figure><p><strong>命名规范（PEP 8）</strong>：</p><ul><li><code>snake_case</code>：变量、函数名（如 <code>user_name</code>、<code>calculate_total</code>）</li><li><code>PascalCase</code>：类名（如 <code>UserService</code>、<code>HttpResponse</code>）</li><li><code>UPPER_CASE</code>：常量（如 <code>MAX_RETRY_COUNT</code>、<code>DEFAULT_TIMEOUT</code>）</li><li><code>_single_leading_underscore</code>：”内部使用”约定，不公开 API</li><li><code>__double_leading_underscore</code>：触发名称改写（name mangling），用于避免子类属性冲突</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><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># ❌ 糟糕的命名</span></span><br><span class="line">l = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]          <span class="comment"># 小写 l 易与 1 混淆</span></span><br><span class="line">O = <span class="number">100</span>                 <span class="comment"># 大写 O 易与 0 混淆</span></span><br><span class="line">I = <span class="number">200</span>                 <span class="comment"># 大写 I 易与 l/1 混淆</span></span><br><span class="line">data = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]       <span class="comment"># 过于笼统</span></span><br><span class="line">flag = <span class="literal">True</span>             <span class="comment"># 含义不清</span></span><br><span class="line">tmp = compute()         <span class="comment"># 临时变量名无意义</span></span><br><span class="line">d1 = get_data()         <span class="comment"># 数字后缀无意义</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ✅ 好的命名</span></span><br><span class="line">user_list = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]</span><br><span class="line">max_retries = <span class="number">100</span></span><br><span class="line">payment_processed = <span class="literal">True</span></span><br><span class="line">computed_value = compute()</span><br><span class="line">primary_data = get_data()</span><br></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><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><span class="line"><span class="keyword">import</span> keyword</span><br><span class="line"><span class="built_in">print</span>(keyword.kwlist)</span><br><span class="line"><span class="comment"># [&#x27;False&#x27;, &#x27;None&#x27;, &#x27;True&#x27;, &#x27;and&#x27;, &#x27;as&#x27;, &#x27;assert&#x27;, &#x27;async&#x27;, &#x27;await&#x27;,</span></span><br><span class="line"><span class="comment">#  &#x27;break&#x27;, &#x27;class&#x27;, &#x27;continue&#x27;, &#x27;def&#x27;, &#x27;del&#x27;, &#x27;elif&#x27;, &#x27;else&#x27;, &#x27;except&#x27;,</span></span><br><span class="line"><span class="comment">#  &#x27;finally&#x27;, &#x27;for&#x27;, &#x27;from&#x27;, &#x27;global&#x27;, &#x27;if&#x27;, &#x27;import&#x27;, &#x27;in&#x27;, &#x27;is&#x27;,</span></span><br><span class="line"><span class="comment">#  &#x27;lambda&#x27;, &#x27;nonlocal&#x27;, &#x27;not&#x27;, &#x27;or&#x27;, &#x27;pass&#x27;, &#x27;raise&#x27;, &#x27;return&#x27;,</span></span><br><span class="line"><span class="comment">#  &#x27;try&#x27;, &#x27;while&#x27;, &#x27;with&#x27;, &#x27;yield&#x27;]</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Python 3.10+ 新增关键字</span></span><br><span class="line"><span class="built_in">print</span>(keyword.softkwlist)</span><br><span class="line"><span class="comment"># [&#x27;match&#x27;, &#x27;case&#x27;, &#x27;_&#x27;（通配符）]</span></span><br></pre></td></tr></table></figure><blockquote><p><strong>面试真题</strong>：Python 中 <code>is</code> 和 <code>==</code> 的区别是什么？为什么 <code>a = 257; b = 257; a is b</code> 在交互模式和脚本中结果可能不同？</p><p><strong>易错点</strong>：① 误用 <code>is</code> 比较值相等性（应使用 <code>==</code>）；② 误以为 <code>is</code> 比较的是值而非对象身份；③ 不知道小整数池的范围是 -5 到 256；④ 在 for 循环中用 <code>is</code> 判断是否等于某个整数，结果可能不符合预期。</p></blockquote><p><img src="https://trae-api-cn.mchost.guru/api/ide/v1/text_to_image?prompt=Python%20variable%20reference%20model%20showing%20variables%20as%20labels%20pointing%20to%20objects%20in%20memory,%20arrows%20from%20names%20to%20memory%20boxes,%20technical%20diagram,%20purple%20theme&image_size=landscape_16_9" alt="Python variable reference model showing variables as labels pointing to objects in memory, arrows from names to memory boxes, technical diagram, purple theme"></p><h3 id="2-2-缩进与代码块"><a href="#2-2-缩进与代码块" class="headerlink" title="2.2 缩进与代码块"></a>2.2 缩进与代码块</h3><p>Python 使用<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><span class="line"><span class="keyword">def</span> <span class="title function_">check_age</span>(<span class="params">age</span>):</span><br><span class="line">    <span class="keyword">if</span> age &gt;= <span class="number">18</span>:                  <span class="comment"># if 块开始</span></span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;成年人&quot;</span>)             <span class="comment"># 4 空格缩进（PEP 8 标准）</span></span><br><span class="line">        <span class="keyword">if</span> age &gt;= <span class="number">60</span>:              <span class="comment"># 嵌套块继续缩进</span></span><br><span class="line">            <span class="built_in">print</span>(<span class="string">&quot;老年人&quot;</span>)         <span class="comment"># 8 空格缩进</span></span><br><span class="line">    <span class="keyword">else</span>:                          <span class="comment"># 与 if 同级</span></span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;未成年人&quot;</span>)</span><br></pre></td></tr></table></figure><p><strong>缩进错误排查技巧</strong>：</p><ol><li><p><strong>TabError: inconsistent use of tabs and spaces in indentation</strong></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 快速检测文件中的 Tab 字符</span></span><br><span class="line">python -c <span class="string">&quot;with open(&#x27;file.py&#x27;) as f: [print(f&#x27;Line &#123;i+1&#125;: Tab found&#x27;) for i, line in enumerate(f) if &#x27;\t&#x27; in line]&quot;</span></span><br></pre></td></tr></table></figure></li><li><p><strong>IndentationError: unindent does not match any outer indentation level</strong></p><ul><li>最常见原因：混用空格和 Tab</li><li>复制粘贴代码后未统一缩进</li><li>不同编辑器 Tab 宽度设置不一致</li></ul></li><li><p><strong>自动修复工具</strong>：</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><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><span class="line"><span class="comment"># autopep8 —— 自动修复 PEP 8 违规（包括缩进）</span></span><br><span class="line">pip install autopep8</span><br><span class="line">autopep8 --in-place --aggressive file.py</span><br><span class="line"></span><br><span class="line"><span class="comment"># black —— 不可配置的格式化器（统一风格）</span></span><br><span class="line">pip install black</span><br><span class="line">black file.py</span><br><span class="line"></span><br><span class="line"><span class="comment"># Ruff —— 超快的 linter + formatter（推荐）</span></span><br><span class="line">pip install ruff</span><br><span class="line">ruff format file.py        <span class="comment"># 格式化</span></span><br><span class="line">ruff check --fix file.py   <span class="comment"># 检查并自动修复</span></span><br></pre></td></tr></table></figure></li></ol><p><strong>血泪教训</strong>：</p><ul><li>永远不要混用空格和 Tab，团队统一使用 <strong>4 个空格</strong>。</li><li>在 <code>.editorconfig</code> 中配置，从源头杜绝：<figure class="highlight ini"><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><span class="line"><span class="section">[*.py]</span></span><br><span class="line"><span class="attr">indent_style</span> = space</span><br><span class="line"><span class="attr">indent_size</span> = <span class="number">4</span></span><br></pre></td></tr></table></figure></li><li>复制粘贴代码时，若出现 <code>IndentationError</code>，优先检查是否混入了 Tab。</li><li>在 Git 中设置 <code>git config --global core.autocrlf false</code>，避免换行符差异导致缩进问题。</li></ul><blockquote><p><strong>面试真题</strong>：Python 为什么用缩进而非花括号？缩进混用空格和 Tab 会怎样？</p><p><strong>易错点</strong>：Tab 和空格在视觉上可能一样，但 Python 解释器会严格区分。PEP 8 规定使用 4 个空格，Tab 只在已有代码中与 Tab 保持一致时使用。</p></blockquote><h3 id="2-3-注释与文档字符串"><a href="#2-3-注释与文档字符串" class="headerlink" title="2.3 注释与文档字符串"></a>2.3 注释与文档字符串</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><span class="line"><span class="comment"># 单行注释：解释&quot;为什么&quot;，而非&quot;做什么&quot;</span></span><br><span class="line"><span class="comment"># 坏注释：x = x + 1  # x 加 1</span></span><br><span class="line"><span class="comment"># 好注释：补偿边界偏移量，使索引从 0 开始</span></span><br><span class="line">x = x + <span class="number">1</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># 多行注释（实际是多行字符串，未被引用时会被解释器忽略）</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">这是模块级别的说明，通常放在文件顶部。</span></span><br><span class="line"><span class="string">说明模块职责、主要类和函数的概要。</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">calculate_area</span>(<span class="params">radius</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;返回圆的面积。</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">    Args:</span></span><br><span class="line"><span class="string">        radius (float): 圆的半径，必须大于 0。</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">    Returns:</span></span><br><span class="line"><span class="string">        float: 圆的面积。</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">    Raises:</span></span><br><span class="line"><span class="string">        ValueError: 如果 radius &lt;= 0。</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">    Example:</span></span><br><span class="line"><span class="string">        &gt;&gt;&gt; calculate_area(5)</span></span><br><span class="line"><span class="string">        78.53981633974483</span></span><br><span class="line"><span class="string">    &quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">if</span> radius &lt;= <span class="number">0</span>:</span><br><span class="line">        <span class="keyword">raise</span> ValueError(<span class="string">&quot;半径必须大于 0&quot;</span>)</span><br><span class="line">    <span class="keyword">import</span> math</span><br><span class="line">    <span class="keyword">return</span> math.pi * radius ** <span class="number">2</span></span><br></pre></td></tr></table></figure><p><strong>Sphinx 文档生成流程</strong>：</p><figure class="highlight plaintext"><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><span class="line">docstring（Google/NumPy 风格）</span><br><span class="line">    ↓  sphinx-apidoc / autodoc 扩展</span><br><span class="line">.rst 文件（reStructuredText）</span><br><span class="line">    ↓  sphinx-build</span><br><span class="line">HTML 文档网站</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 安装 Sphinx</span></span><br><span class="line">pip install sphinx sphinx-rtd-theme</span><br><span class="line"></span><br><span class="line"><span class="comment"># 初始化文档目录</span></span><br><span class="line"><span class="built_in">cd</span> docs</span><br><span class="line">sphinx-quickstart</span><br><span class="line"></span><br><span class="line"><span class="comment"># 从代码自动生成 API 文档</span></span><br><span class="line">sphinx-apidoc -o <span class="built_in">source</span> ../src/my_package</span><br><span class="line"></span><br><span class="line"><span class="comment"># 构建文档</span></span><br><span class="line">sphinx-build -b html <span class="built_in">source</span> build</span><br></pre></td></tr></table></figure><p><strong>type hint 配合 docstring 的最佳实践</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><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><span class="line"><span class="keyword">from</span> typing <span class="keyword">import</span> <span class="type">Optional</span>, <span class="type">Union</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">search_user</span>(<span class="params"></span></span><br><span class="line"><span class="params">    user_id: <span class="built_in">int</span>,</span></span><br><span class="line"><span class="params">    *,</span></span><br><span class="line"><span class="params">    active_only: <span class="built_in">bool</span> = <span class="literal">True</span>,</span></span><br><span class="line"><span class="params">    fields: <span class="type">Optional</span>[<span class="built_in">list</span>[<span class="built_in">str</span>]] = <span class="literal">None</span>,</span></span><br><span class="line"><span class="params"></span>) -&gt; <span class="type">Optional</span>[<span class="built_in">dict</span>[<span class="built_in">str</span>, <span class="type">Union</span>[<span class="built_in">str</span>, <span class="built_in">int</span>]]]:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;根据 ID 搜索用户。</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">    Args:</span></span><br><span class="line"><span class="string">        user_id: 用户唯一标识符，必须为正整数。</span></span><br><span class="line"><span class="string">        active_only: 是否只搜索活跃用户，默认 True。</span></span><br><span class="line"><span class="string">        fields: 需要返回的字段列表，None 表示全部字段。</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">    Returns:</span></span><br><span class="line"><span class="string">        用户信息字典，未找到时返回 None。</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">    Raises:</span></span><br><span class="line"><span class="string">        ValueError: user_id 不是正整数时。</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">    Example:</span></span><br><span class="line"><span class="string">        &gt;&gt;&gt; search_user(42, active_only=False)</span></span><br><span class="line"><span class="string">        &#123;&#x27;id&#x27;: 42, &#x27;name&#x27;: &#x27;Alice&#x27;, &#x27;status&#x27;: &#x27;inactive&#x27;&#125;</span></span><br><span class="line"><span class="string">    &quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">if</span> user_id &lt;= <span class="number">0</span>:</span><br><span class="line">        <span class="keyword">raise</span> ValueError(<span class="string">&quot;user_id 必须为正整数&quot;</span>)</span><br><span class="line">    <span class="comment"># ... 实际逻辑</span></span><br></pre></td></tr></table></figure><p><strong>工程规范</strong>：</p><ul><li>公开 API（函数、类、模块）必须有文档字符串（docstring）。</li><li>推荐使用 <strong>Google 风格</strong> 或 <strong>NumPy 风格</strong>，配合 Sphinx 自动生成文档。</li><li>私有函数（<code>_</code> 开头）可简化注释，但复杂逻辑仍需说明。</li><li>type hint 和 docstring 互补：type hint 提供机器可读的类型信息，docstring 提供语义说明。</li></ul><blockquote><p><strong>面试真题</strong>：Python 的 docstring 有哪几种风格？如何在项目中自动生成 API 文档？</p><p><strong>易错点</strong>：混淆注释和文档字符串的用途。注释是给开发者看的，docstring 是 API 文档的一部分，会被 <code>help()</code> 和 Sphinx 提取。单行注释用 <code>#</code>，不要用三引号代替。</p></blockquote><hr><h2 id="3-数据类型与数据结构"><a href="#3-数据类型与数据结构" class="headerlink" title="3. 数据类型与数据结构"></a>3. 数据类型与数据结构</h2><p>Python 中一切皆为对象，每个对象都有<strong>类型</strong>、<strong>值</strong>和<strong>身份（id）</strong>。</p><h3 id="3-1-数字类型"><a href="#3-1-数字类型" class="headerlink" title="3.1 数字类型"></a>3.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><span class="line"><span class="comment"># int —— 任意精度整数，不存在溢出问题</span></span><br><span class="line">big = <span class="number">2</span> ** <span class="number">1000</span>          <span class="comment"># 完全可以</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">type</span>(big))         <span class="comment"># &lt;class &#x27;int&#x27;&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># float —— 双精度浮点数（IEEE 754），精度约 15-17 位有效数字</span></span><br><span class="line">a = <span class="number">0.1</span> + <span class="number">0.2</span></span><br><span class="line"><span class="built_in">print</span>(a)                 <span class="comment"># 0.30000000000000004 —— 浮点精度问题</span></span><br><span class="line"><span class="built_in">print</span>(a == <span class="number">0.3</span>)          <span class="comment"># False！</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 浮点数比较的正确姿势</span></span><br><span class="line"><span class="keyword">import</span> math</span><br><span class="line"><span class="built_in">print</span>(math.isclose(a, <span class="number">0.3</span>))   <span class="comment"># True</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 金融计算等精确场景用 decimal</span></span><br><span class="line"><span class="keyword">from</span> decimal <span class="keyword">import</span> Decimal, getcontext</span><br><span class="line">getcontext().prec = <span class="number">6</span></span><br><span class="line"><span class="built_in">print</span>(Decimal(<span class="string">&#x27;0.1&#x27;</span>) + Decimal(<span class="string">&#x27;0.2&#x27;</span>))   <span class="comment"># 0.3</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># complex —— 复数</span></span><br><span class="line">z = <span class="number">3</span> + <span class="number">4j</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">abs</span>(z))            <span class="comment"># 5.0（模长）</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># bool —— int 的子类，但语义上独立</span></span><br><span class="line"><span class="built_in">print</span>(<span class="literal">True</span> + <span class="literal">True</span>)       <span class="comment"># 2（可作数学运算，但不推荐这么写）</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">isinstance</span>(<span class="literal">True</span>, <span class="built_in">int</span>))  <span class="comment"># True</span></span><br></pre></td></tr></table></figure><p><strong>整数缓存机制详解</strong>：</p><p>CPython 不仅缓存 -5 到 256 的小整数，还会对某些特定值做优化：</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><span class="line"><span class="comment"># 小整数池：-5 到 256 被预创建并缓存</span></span><br><span class="line"><span class="keyword">import</span> sys</span><br><span class="line"><span class="built_in">print</span>(sys.getsizeof(<span class="number">0</span>))    <span class="comment"># 24 字节（64位系统）</span></span><br><span class="line"><span class="built_in">print</span>(sys.getsizeof(<span class="number">1</span>))    <span class="comment"># 28 字节</span></span><br><span class="line"><span class="built_in">print</span>(sys.getsizeof(<span class="number">2</span>**<span class="number">30</span>)) <span class="comment"># 32 字节，值越大占用内存越多</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 大整数每增加 2^30 约多占 4 字节</span></span><br><span class="line"><span class="built_in">print</span>(sys.getsizeof(<span class="number">2</span>**<span class="number">60</span>))   <span class="comment"># 36 字节</span></span><br><span class="line"><span class="built_in">print</span>(sys.getsizeof(<span class="number">2</span>**<span class="number">1000</span>)) <span class="comment"># 约 160 字节</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 布尔值本质上是整数的子类</span></span><br><span class="line"><span class="built_in">print</span>(<span class="literal">True</span> == <span class="number">1</span>)      <span class="comment"># True（值相等）</span></span><br><span class="line"><span class="built_in">print</span>(<span class="literal">False</span> == <span class="number">0</span>)     <span class="comment"># True</span></span><br><span class="line"><span class="built_in">print</span>(<span class="literal">True</span> <span class="keyword">is</span> <span class="number">1</span>)      <span class="comment"># False（不是同一个对象，但 bool 继承自 int）</span></span><br></pre></td></tr></table></figure><p><strong>浮点数 IEEE 754 详解</strong>：</p><figure class="highlight plaintext"><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><span class="line">IEEE 754 双精度浮点数（64 位）结构：</span><br><span class="line">┌─────┬──────────────┬─────────────────────────────────────┐</span><br><span class="line">│ 符号 │    指数(11位) │           尾数(52位)                 │</span><br><span class="line">│ 1位  │   范围: -1022 │   精度: 约 15-17 位有效数字          │</span><br><span class="line">│      │   ~ +1023     │                                     │</span><br><span class="line">└─────┴──────────────┴─────────────────────────────────────┘</span><br><span class="line"></span><br><span class="line">特殊值：</span><br><span class="line">- inf / -inf：无穷大（1.0 / 0.0）</span><br><span class="line">- nan：非数字（0.0 / 0.0 或 float(&#x27;nan&#x27;)）</span><br><span class="line">- -0.0：负零（-0.0 == 0.0 为 True，但 1/0.0 报错而 1/-0.0 为 -inf）</span><br></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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 浮点数精度问题的根本原因</span></span><br><span class="line"><span class="comment"># 0.1 在二进制中是无限循环小数：0.00011001100110011...</span></span><br><span class="line"><span class="comment"># 存储时被截断，导致 0.1 + 0.2 ≠ 0.3</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;<span class="subst">&#123;<span class="number">0.1</span>:<span class="number">.60</span>f&#125;</span>&quot;</span>)   <span class="comment"># 0.100000000000000005551115123125782702118158340454101562500000</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;<span class="subst">&#123;<span class="number">0.2</span>:<span class="number">.60</span>f&#125;</span>&quot;</span>)   <span class="comment"># 0.200000000000000011102230246251565404236316680908203125000000</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;<span class="subst">&#123;<span class="number">0.3</span>:<span class="number">.60</span>f&#125;</span>&quot;</span>)   <span class="comment"># 0.299999999999999988897769753748434595763683319091796875000000</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 特殊浮点值</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">float</span>(<span class="string">&#x27;inf&#x27;</span>))     <span class="comment"># inf</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">float</span>(<span class="string">&#x27;-inf&#x27;</span>))    <span class="comment"># -inf</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">float</span>(<span class="string">&#x27;nan&#x27;</span>))     <span class="comment"># nan</span></span><br><span class="line"><span class="built_in">print</span>(<span class="number">1.0</span> / <span class="number">0.0</span>)       <span class="comment"># ZeroDivisionError（不是 inf！Python 做了除零检查）</span></span><br><span class="line"><span class="keyword">import</span> math</span><br><span class="line"><span class="built_in">print</span>(math.inf)         <span class="comment"># inf（通过 math 获取）</span></span><br><span class="line"><span class="built_in">print</span>(math.nan)         <span class="comment"># nan</span></span><br><span class="line"><span class="built_in">print</span>(math.isnan(math.nan))  <span class="comment"># True（nan 不等于自身！）</span></span><br><span class="line"><span class="built_in">print</span>(math.nan == math.nan)  <span class="comment"># False！</span></span><br></pre></td></tr></table></figure><p><strong>Decimal 精度控制实战</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><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><span class="line"><span class="keyword">from</span> decimal <span class="keyword">import</span> Decimal, getcontext, ROUND_HALF_UP, ROUND_DOWN</span><br><span class="line"></span><br><span class="line"><span class="comment"># 设置全局精度</span></span><br><span class="line">getcontext().prec = <span class="number">28</span>    <span class="comment"># 28 位有效数字（默认值）</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 金融计算：精确到分</span></span><br><span class="line">price = Decimal(<span class="string">&#x27;19.99&#x27;</span>)</span><br><span class="line">tax_rate = Decimal(<span class="string">&#x27;0.08&#x27;</span>)</span><br><span class="line">tax = (price * tax_rate).quantize(Decimal(<span class="string">&#x27;0.01&#x27;</span>), rounding=ROUND_HALF_UP)</span><br><span class="line"><span class="built_in">print</span>(tax)     <span class="comment"># 1.60（四舍五入到分）</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 不同的舍入模式</span></span><br><span class="line">value = Decimal(<span class="string">&#x27;2.675&#x27;</span>)</span><br><span class="line"><span class="built_in">print</span>(value.quantize(Decimal(<span class="string">&#x27;0.01&#x27;</span>), rounding=ROUND_HALF_UP))   <span class="comment"># 2.68</span></span><br><span class="line"><span class="built_in">print</span>(value.quantize(Decimal(<span class="string">&#x27;0.01&#x27;</span>), rounding=ROUND_DOWN))       <span class="comment"># 2.67</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Decimal 必须用字符串初始化！</span></span><br><span class="line"><span class="built_in">print</span>(Decimal(<span class="number">0.1</span>))         <span class="comment"># 0.1000000000000000055511151231257827021181583404541015625</span></span><br><span class="line"><span class="built_in">print</span>(Decimal(<span class="string">&#x27;0.1&#x27;</span>))       <span class="comment"># 0.1 —— 精确值</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 上下文管理器临时修改精度</span></span><br><span class="line"><span class="keyword">with</span> getcontext() <span class="keyword">as</span> ctx:</span><br><span class="line">    ctx.prec = <span class="number">6</span></span><br><span class="line">    result = Decimal(<span class="string">&#x27;1&#x27;</span>) / Decimal(<span class="string">&#x27;3&#x27;</span>)</span><br><span class="line">    <span class="built_in">print</span>(result)    <span class="comment"># 0.333333</span></span><br></pre></td></tr></table></figure><p><strong>Fraction 分数类型</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> fractions <span class="keyword">import</span> Fraction</span><br><span class="line"></span><br><span class="line"><span class="comment"># 精确的分数运算</span></span><br><span class="line">a = Fraction(<span class="number">1</span>, <span class="number">3</span>)    <span class="comment"># 1/3</span></span><br><span class="line">b = Fraction(<span class="number">2</span>, <span class="number">3</span>)    <span class="comment"># 2/3</span></span><br><span class="line"><span class="built_in">print</span>(a + b)           <span class="comment"># 1（精确结果，不是 0.999...）</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 从浮点数创建（可能不是你期望的）</span></span><br><span class="line"><span class="built_in">print</span>(Fraction(<span class="number">0.1</span>))          <span class="comment"># 3602879701896397/36028797018963968</span></span><br><span class="line"><span class="built_in">print</span>(Fraction(<span class="string">&#x27;0.1&#x27;</span>))        <span class="comment"># 1/10 —— 用字符串更精确</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 自动约分</span></span><br><span class="line"><span class="built_in">print</span>(Fraction(<span class="number">6</span>, <span class="number">4</span>))         <span class="comment"># 3/2</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 分数与浮点数互转</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">float</span>(Fraction(<span class="number">1</span>, <span class="number">3</span>)))  <span class="comment"># 0.3333333333333333</span></span><br></pre></td></tr></table></figure><blockquote><p><strong>面试真题</strong>：为什么 <code>0.1 + 0.2 != 0.3</code>？如何在 Python 中进行精确的小数运算？</p><p><strong>易错点</strong>：① <code>Decimal(0.1)</code> 仍然会有精度问题，必须用 <code>Decimal(&#39;0.1&#39;)</code>；② <code>math.nan == math.nan</code> 返回 <code>False</code>，判断 nan 必须用 <code>math.isnan()</code>；③ 布尔值参与算术运算虽然合法，但代码可读性极差。</p></blockquote><p><img src="https://trae-api-cn.mchost.guru/api/ide/v1/text_to_image?prompt=Python%20numeric%20types%20hierarchy%20diagram%20showing%20int%20float%20complex%20bool%20Decimal%20relationships,%20clean%20technical%20illustration,%20orange%20theme&image_size=landscape_16_9" alt="Python numeric types hierarchy diagram showing int float complex bool Decimal relationships, clean technical illustration, orange theme"></p><h3 id="3-2-序列类型"><a href="#3-2-序列类型" class="headerlink" title="3.2 序列类型"></a>3.2 序列类型</h3><h4 id="字符串-str（不可变）"><a href="#字符串-str（不可变）" class="headerlink" title="字符串 str（不可变）"></a>字符串 <code>str</code>（不可变）</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></pre></td><td class="code"><pre><span class="line">s = <span class="string">&quot;Hello, Python!&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 索引与切片</span></span><br><span class="line"><span class="built_in">print</span>(s[<span class="number">0</span>])       <span class="comment"># &#x27;H&#x27;</span></span><br><span class="line"><span class="built_in">print</span>(s[-<span class="number">1</span>])      <span class="comment"># &#x27;!&#x27;</span></span><br><span class="line"><span class="built_in">print</span>(s[<span class="number">7</span>:<span class="number">13</span>])    <span class="comment"># &#x27;Python&#x27;（左闭右开）</span></span><br><span class="line"><span class="built_in">print</span>(s[::<span class="number">2</span>])     <span class="comment"># &#x27;Hlo yhn&#x27;（步长为 2）</span></span><br><span class="line"><span class="built_in">print</span>(s[::-<span class="number">1</span>])    <span class="comment"># &#x27;!nohtyP ,olleH&#x27;（反转字符串）</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># f-string（3.6+，最推荐的格式化方式）</span></span><br><span class="line">name = <span class="string">&quot;Alice&quot;</span></span><br><span class="line">age = <span class="number">30</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;姓名: <span class="subst">&#123;name&#125;</span>, 年龄: <span class="subst">&#123;age&#125;</span>, 明年: <span class="subst">&#123;age + <span class="number">1</span>&#125;</span>&quot;</span>)</span><br><span class="line"><span class="comment"># 表达式内联、自动调用 __format__，性能也优于 % 和 format()</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 多行字符串与原始字符串</span></span><br><span class="line">query = <span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">SELECT id, name</span></span><br><span class="line"><span class="string">FROM users</span></span><br><span class="line"><span class="string">WHERE age &gt; %s</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line">path = <span class="string">r&quot;C:\Users\name\file.txt&quot;</span>   <span class="comment"># r 前缀：原始字符串，不转义</span></span><br></pre></td></tr></table></figure><p><strong>字符串切片可视化</strong>：</p><figure class="highlight plaintext"><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><span class="line">字符串: H  e  l  l  o  ,     P  y  t  h  o  n  !</span><br><span class="line">正索引: 0  1  2  3  4  5  6  7  8  9 10 11 12 13</span><br><span class="line">负索引:-14-13-12-11-10 -9 -8 -7 -6 -5 -4 -3 -2 -1</span><br><span class="line"></span><br><span class="line">s[7:13]  →  ┌───────────────┐</span><br><span class="line">             │ P  y  t  h  o  n │</span><br><span class="line">             └───────────────┘</span><br><span class="line"></span><br><span class="line">s[::2]   →  H . l . o .   . P . t . o . !</span><br><span class="line">             ↑     ↑     ↑     ↑     ↑     ↑     ↑</span><br><span class="line"></span><br><span class="line">s[::-1]  →  ! n o h t y P   , o l l e H   (反转)</span><br></pre></td></tr></table></figure><p><strong>切片通用公式</strong>：<code>s[start:stop:step]</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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 切片永远返回新对象，不会修改原字符串</span></span><br><span class="line">s = <span class="string">&quot;Hello, Python!&quot;</span></span><br><span class="line"><span class="built_in">print</span>(s[<span class="number">3</span>:<span class="number">8</span>])       <span class="comment"># &#x27;lo, P&#x27;</span></span><br><span class="line"><span class="built_in">print</span>(s[-<span class="number">6</span>:])       <span class="comment"># &#x27;ython!&#x27;</span></span><br><span class="line"><span class="built_in">print</span>(s[:<span class="number">5</span>])        <span class="comment"># &#x27;Hello&#x27;</span></span><br><span class="line"><span class="built_in">print</span>(s[<span class="number">5</span>:])        <span class="comment"># &#x27;, Python!&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 步长为负数时，默认从右到左</span></span><br><span class="line"><span class="built_in">print</span>(s[::-<span class="number">1</span>])      <span class="comment"># &#x27;!nohtyP ,olleH&#x27;（完整反转）</span></span><br><span class="line"><span class="built_in">print</span>(s[::-<span class="number">2</span>])      <span class="comment"># &#x27;!otP olH&#x27;（隔一个取一个，从右向左）</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 切片越界不报错（与索引不同！）</span></span><br><span class="line"><span class="built_in">print</span>(s[<span class="number">100</span>:])      <span class="comment"># &#x27;&#x27;（空字符串，不报 IndexError）</span></span><br><span class="line"><span class="built_in">print</span>(s[:<span class="number">100</span>])      <span class="comment"># &#x27;Hello, Python!&#x27;（不报错）</span></span><br><span class="line"><span class="comment"># print(s[100])     # IndexError！（直接索引访问越界会报错）</span></span><br></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><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><span class="line"><span class="comment"># encode: str → bytes（编码）</span></span><br><span class="line">text = <span class="string">&quot;你好，世界&quot;</span></span><br><span class="line">b = text.encode(<span class="string">&quot;utf-8&quot;</span>)     <span class="comment"># b&#x27;\xe4\xbd\xa0\xe5\xa5\xbd\xef\xbc\x8c\xe4\xb8\x96\xe7\x95\x8c&#x27;</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">len</span>(b))                 <span class="comment"># 15（UTF-8 中每个中文约 3 字节）</span></span><br><span class="line"></span><br><span class="line">b_gbk = text.encode(<span class="string">&quot;gbk&quot;</span>)   <span class="comment"># GBK 编码，每个中文 2 字节</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">len</span>(b_gbk))             <span class="comment"># 8</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># decode: bytes → str（解码）</span></span><br><span class="line">decoded = b.decode(<span class="string">&quot;utf-8&quot;</span>)   <span class="comment"># &#x27;你好，世界&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 编码错误处理</span></span><br><span class="line">bad_bytes = <span class="string">b&#x27;\xff\xfe&#x27;</span>       <span class="comment"># 无效的 UTF-8 字节</span></span><br><span class="line"><span class="comment"># bad_bytes.decode(&quot;utf-8&quot;)   # UnicodeDecodeError</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(bad_bytes.decode(<span class="string">&quot;utf-8&quot;</span>, errors=<span class="string">&quot;ignore&quot;</span>))   <span class="comment"># &#x27;&#x27;（忽略错误字节）</span></span><br><span class="line"><span class="built_in">print</span>(bad_bytes.decode(<span class="string">&quot;utf-8&quot;</span>, errors=<span class="string">&quot;replace&quot;</span>))   <span class="comment"># &#x27;��&#x27;（替换为占位符）</span></span><br><span class="line"><span class="built_in">print</span>(bad_bytes.decode(<span class="string">&quot;utf-8&quot;</span>, errors=<span class="string">&quot;backslashreplace&quot;</span>))  <span class="comment"># &#x27;\\xff\\xfe&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 常用编码格式</span></span><br><span class="line"><span class="comment"># UTF-8: 可变长度（1-4字节），互联网标准，推荐使用</span></span><br><span class="line"><span class="comment"># GBK: 定长2字节（中文），中文 Windows 常用</span></span><br><span class="line"><span class="comment"># ASCII: 1字节，仅支持英文</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># chardet 自动检测编码（需安装：pip install chardet）</span></span><br><span class="line"><span class="comment"># import chardet</span></span><br><span class="line"><span class="comment"># result = chardet.detect(b&#x27;\xc4\xe3\xba\xc3&#x27;)</span></span><br><span class="line"><span class="comment"># print(result)  # &#123;&#x27;encoding&#x27;: &#x27;GB2312&#x27;, &#x27;confidence&#x27;: 0.99, ...&#125;</span></span><br></pre></td></tr></table></figure><blockquote><p><strong>面试真题</strong>：Python 中字符串的切片和索引有什么区别？切片越界会怎样？字符串编码和解码的过程是什么？</p><p><strong>易错点</strong>：① 切片越界不报错但索引越界报 <code>IndexError</code>；② <code>s[::-1]</code> 不是原地修改，而是返回新字符串；③ 编码和解码方向搞反——<code>str.encode()</code> 得到 bytes，<code>bytes.decode()</code> 得到 str。</p></blockquote><p><img src="https://trae-api-cn.mchost.guru/api/ide/v1/text_to_image?prompt=Python%20string%20slicing%20visualization%20with%20index%20positions%20shown%20above%20and%20below%20a%20string,%20highlighting%20slice%20ranges%20with%20colored%20boxes,%20red%20theme&image_size=landscape_16_9" alt="Python string slicing visualization with index positions shown above and below a string, highlighting slice ranges with colored boxes, red theme"></p><h4 id="bytes-与-bytearray（二进制序列）"><a href="#bytes-与-bytearray（二进制序列）" class="headerlink" title="bytes 与 bytearray（二进制序列）"></a><code>bytes</code> 与 <code>bytearray</code>（二进制序列）</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></pre></td><td class="code"><pre><span class="line"><span class="comment"># bytes —— 不可变的字节序列</span></span><br><span class="line">b = <span class="string">b&quot;hello&quot;</span>                    <span class="comment"># 字面量</span></span><br><span class="line">b = <span class="built_in">bytes</span>([<span class="number">104</span>, <span class="number">101</span>, <span class="number">108</span>, <span class="number">108</span>, <span class="number">111</span>])  <span class="comment"># 从列表构造</span></span><br><span class="line">b = <span class="string">&quot;hello&quot;</span>.encode(<span class="string">&quot;utf-8&quot;</span>)     <span class="comment"># 从字符串编码</span></span><br><span class="line"><span class="built_in">print</span>(b[<span class="number">0</span>])                     <span class="comment"># 104（整数！不是字节对象）</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># bytearray —— 可变的字节序列</span></span><br><span class="line">ba = <span class="built_in">bytearray</span>(<span class="string">b&quot;hello&quot;</span>)</span><br><span class="line">ba[<span class="number">0</span>] = <span class="number">104</span>                     <span class="comment"># 支持原地修改</span></span><br><span class="line">ba.append(<span class="number">33</span>)                   <span class="comment"># 追加</span></span><br><span class="line"><span class="built_in">print</span>(ba)                       <span class="comment"># bytearray(b&#x27;hello!&#x27;)</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 工程场景：处理二进制协议、文件读写、网络传输</span></span><br><span class="line"><span class="comment"># 差异对照：</span></span><br><span class="line"><span class="comment"># | 特性 | bytes | bytearray |</span></span><br><span class="line"><span class="comment"># |------|-------|-----------|</span></span><br><span class="line"><span class="comment"># | 可变性 | 不可变 | 可变 |</span></span><br><span class="line"><span class="comment"># | 哈希able | 是 | 否 |</span></span><br><span class="line"><span class="comment"># | 适用场景 | 常量数据、dict 键 | 需要修改的缓冲区 |</span></span><br></pre></td></tr></table></figure><h4 id="列表-list（可变，有序）"><a href="#列表-list（可变，有序）" class="headerlink" title="列表 list（可变，有序）"></a>列表 <code>list</code>（可变，有序）</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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 列表推导式（List Comprehension）—— Pythonic 的核心标志</span></span><br><span class="line">squares = [x**<span class="number">2</span> <span class="keyword">for</span> x <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">10</span>)]</span><br><span class="line">evens = [x <span class="keyword">for</span> x <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">10</span>) <span class="keyword">if</span> x % <span class="number">2</span> == <span class="number">0</span>]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 带条件的推导式</span></span><br><span class="line">labels = [<span class="string">&quot;even&quot;</span> <span class="keyword">if</span> x % <span class="number">2</span> == <span class="number">0</span> <span class="keyword">else</span> <span class="string">&quot;odd&quot;</span> <span class="keyword">for</span> x <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">5</span>)]</span><br><span class="line"><span class="comment"># [&#x27;even&#x27;, &#x27;odd&#x27;, &#x27;even&#x27;, &#x27;odd&#x27;, &#x27;even&#x27;]</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 嵌套推导式（扁平化矩阵）</span></span><br><span class="line">matrix = [[<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>], [<span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>], [<span class="number">7</span>, <span class="number">8</span>, <span class="number">9</span>]]</span><br><span class="line">flat = [x <span class="keyword">for</span> row <span class="keyword">in</span> matrix <span class="keyword">for</span> x <span class="keyword">in</span> row]</span><br><span class="line"><span class="comment"># [1, 2, 3, 4, 5, 6, 7, 8, 9]</span></span><br></pre></td></tr></table></figure><p><strong>列表 28 种常用方法速查</strong>：</p><table><thead><tr><th>类别</th><th>方法</th><th>说明</th><th>时间复杂度</th></tr></thead><tbody><tr><td><strong>添加</strong></td><td><code>append(x)</code></td><td>尾部追加</td><td>O(1)均摊</td></tr><tr><td></td><td><code>extend(iter)</code></td><td>追加可迭代对象</td><td>O(k)</td></tr><tr><td></td><td><code>insert(i, x)</code></td><td>指定位置插入</td><td>O(n)</td></tr><tr><td><strong>删除</strong></td><td><code>remove(x)</code></td><td>删除第一个值为 x 的元素</td><td>O(n)</td></tr><tr><td></td><td><code>pop()</code></td><td>弹出最后一个元素</td><td>O(1)</td></tr><tr><td></td><td><code>pop(i)</code></td><td>弹出指定位置元素</td><td>O(n)</td></tr><tr><td></td><td><code>clear()</code></td><td>清空列表</td><td>O(1)</td></tr><tr><td><strong>查找</strong></td><td><code>index(x)</code></td><td>返回 x 的索引</td><td>O(n)</td></tr><tr><td></td><td><code>count(x)</code></td><td>统计 x 出现次数</td><td>O(n)</td></tr><tr><td></td><td><code>x in lst</code></td><td>成员检测</td><td>O(n)</td></tr><tr><td><strong>排序</strong></td><td><code>sort()</code></td><td>原地排序</td><td>O(n log n)</td></tr><tr><td></td><td><code>reverse()</code></td><td>原地反转</td><td>O(n)</td></tr><tr><td><strong>复制</strong></td><td><code>copy()</code></td><td>浅拷贝</td><td>O(n)</td></tr><tr><td><strong>其他</strong></td><td><code>len(lst)</code></td><td>长度</td><td>O(1)</td></tr><tr><td></td><td><code>lst[i]</code></td><td>索引访问</td><td>O(1)</td></tr><tr><td></td><td><code>lst[i] = x</code></td><td>索引赋值</td><td>O(1)</td></tr><tr><td></td><td><code>lst + lst2</code></td><td>拼接（返回新列表）</td><td>O(n+m)</td></tr><tr><td></td><td><code>lst * n</code></td><td>重复（注意嵌套陷阱！）</td><td>O(n*k)</td></tr></tbody></table><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><span class="line"><span class="comment"># 列表重复的陷阱</span></span><br><span class="line">a = [[<span class="number">0</span>]] * <span class="number">3</span>     <span class="comment"># [[0], [0], [0]]</span></span><br><span class="line">a[<span class="number">0</span>][<span class="number">0</span>] = <span class="number">1</span>       <span class="comment"># [[1], [1], [1]] —— 三个子列表是同一个对象！</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 正确的二维列表创建方式</span></span><br><span class="line">a = [[<span class="number">0</span>] <span class="keyword">for</span> _ <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">3</span>)]   <span class="comment"># [[0], [0], [0]]</span></span><br><span class="line">a[<span class="number">0</span>][<span class="number">0</span>] = <span class="number">1</span>                    <span class="comment"># [[1], [0], [0]] —— 互不影响</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># sort() 的高级用法</span></span><br><span class="line">data = [(<span class="string">&quot;Alice&quot;</span>, <span class="number">30</span>), (<span class="string">&quot;Bob&quot;</span>, <span class="number">25</span>), (<span class="string">&quot;Charlie&quot;</span>, <span class="number">35</span>)]</span><br><span class="line">data.sort(key=<span class="keyword">lambda</span> x: x[<span class="number">1</span>])              <span class="comment"># 按年龄排序</span></span><br><span class="line">data.sort(key=<span class="keyword">lambda</span> x: x[<span class="number">1</span>], reverse=<span class="literal">True</span>) <span class="comment"># 降序</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 稳定排序：多次排序</span></span><br><span class="line">employees = [</span><br><span class="line">    &#123;<span class="string">&quot;name&quot;</span>: <span class="string">&quot;Alice&quot;</span>, <span class="string">&quot;dept&quot;</span>: <span class="string">&quot;Engineering&quot;</span>, <span class="string">&quot;salary&quot;</span>: <span class="number">120000</span>&#125;,</span><br><span class="line">    &#123;<span class="string">&quot;name&quot;</span>: <span class="string">&quot;Bob&quot;</span>, <span class="string">&quot;dept&quot;</span>: <span class="string">&quot;Engineering&quot;</span>, <span class="string">&quot;salary&quot;</span>: <span class="number">110000</span>&#125;,</span><br><span class="line">    &#123;<span class="string">&quot;name&quot;</span>: <span class="string">&quot;Charlie&quot;</span>, <span class="string">&quot;dept&quot;</span>: <span class="string">&quot;Sales&quot;</span>, <span class="string">&quot;salary&quot;</span>: <span class="number">90000</span>&#125;,</span><br><span class="line">]</span><br><span class="line"><span class="comment"># 先按薪水排序，再按部门排序 → 最终以部门为主要排序</span></span><br><span class="line">employees.sort(key=<span class="keyword">lambda</span> x: x[<span class="string">&quot;salary&quot;</span>])      <span class="comment"># 第一排序键</span></span><br><span class="line">employees.sort(key=<span class="keyword">lambda</span> x: x[<span class="string">&quot;dept&quot;</span>])         <span class="comment"># 第二排序键（稳定排序保证前面的相对顺序）</span></span><br></pre></td></tr></table></figure><p><strong>常见操作时间复杂度（面试常考）</strong>：</p><ul><li>索引访问 <code>lst[i]</code>：O(1)</li><li>尾部追加 <code>append()</code>：均摊 O(1)</li><li>任意位置插入 <code>insert(i, x)</code>：O(n)</li><li>查找 <code>x in lst</code>：O(n)</li><li>排序 <code>sort()</code>：O(n log n)</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> copy</span><br><span class="line">original = [[<span class="number">1</span>, <span class="number">2</span>], [<span class="number">3</span>, <span class="number">4</span>]]</span><br><span class="line">shallow = original.copy()          <span class="comment"># 或 list(original) / original[:]</span></span><br><span class="line">deep = copy.deepcopy(original)</span><br><span class="line"></span><br><span class="line">original[<span class="number">0</span>][<span class="number">0</span>] = <span class="number">99</span></span><br><span class="line"><span class="built_in">print</span>(shallow)   <span class="comment"># [[99, 2], [3, 4]] —— 内层对象共享引用</span></span><br><span class="line"><span class="built_in">print</span>(deep)      <span class="comment"># [[1, 2], [3, 4]] —— 完全独立副本</span></span><br></pre></td></tr></table></figure><p><strong>深浅拷贝内存模型对比</strong>：</p><figure class="highlight plaintext"><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><span class="line">浅拷贝 (shallow copy):</span><br><span class="line">  original ──→ [ ┌──┐ , ┌──┐ ]</span><br><span class="line">               │  │   │  │</span><br><span class="line">  shallow  ──→ [ └│┘   └│┘ ]    ← 外层列表是新的，但内层对象共享！</span><br><span class="line">                │      │</span><br><span class="line">                ↓      ↓</span><br><span class="line">              [99,2]  [3,4]      ← 修改 original[0][0] 会影响 shallow</span><br><span class="line"></span><br><span class="line">深拷贝 (deep copy):</span><br><span class="line">  original ──→ [ ┌──┐ , ┌──┐ ]</span><br><span class="line">                ↓      ↓</span><br><span class="line">              [99,2]  [3,4]      ← 原始对象</span><br><span class="line"></span><br><span class="line">  deep     ──→ [ ┌──┐ , ┌──┐ ]  ← 完全独立的新列表</span><br><span class="line">                ↓      ↓</span><br><span class="line">              [1,2]   [3,4]      ← 内层对象也被递归复制，互不影响</span><br></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><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><span class="line">lst = [<span class="number">1</span>, [<span class="number">2</span>, <span class="number">3</span>], <span class="number">4</span>]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 方式1：list.copy()</span></span><br><span class="line">c1 = lst.copy()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 方式2：list()</span></span><br><span class="line">c2 = <span class="built_in">list</span>(lst)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 方式3：切片</span></span><br><span class="line">c3 = lst[:]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 三种方式效果完全相同：创建新的外层列表，内层对象共享引用</span></span><br><span class="line"><span class="comment"># 注意：lst * 1 也是浅拷贝</span></span><br></pre></td></tr></table></figure><blockquote><p><strong>面试真题</strong>：Python 中深拷贝和浅拷贝有什么区别？<code>list.copy()</code> 是深拷贝还是浅拷贝？如何实现一个真正的深拷贝？</p><p><strong>易错点</strong>：① <code>list.copy()</code> &#x2F; <code>list()</code> &#x2F; <code>lst[:]</code> 都是浅拷贝；② <code>[[0]] * 3</code> 三个子列表是同一个对象；③ <code>copy.deepcopy()</code> 会递归复制所有嵌套对象，但无法拷贝自定义对象的内部状态（如文件句柄、数据库连接）。</p></blockquote><p><img src="https://trae-api-cn.mchost.guru/api/ide/v1/text_to_image?prompt=Python%20deep%20copy%20vs%20shallow%20copy%20memory%20diagram%20showing%20reference%20sharing,%20two%20comparison%20diagrams%20side%20by%20side,%20teal%20color%20scheme&image_size=landscape_16_9" alt="Python deep copy vs shallow copy memory diagram showing reference sharing, two comparison diagrams side by side, teal color scheme"></p><h4 id="元组-tuple（不可变，有序）"><a href="#元组-tuple（不可变，有序）" class="headerlink" title="元组 tuple（不可变，有序）"></a>元组 <code>tuple</code>（不可变，有序）</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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 元组的&quot;不可变&quot;指的是引用不可变，但引用的对象可变</span></span><br><span class="line">t = (<span class="number">1</span>, [<span class="number">2</span>, <span class="number">3</span>], <span class="number">4</span>)</span><br><span class="line"><span class="comment"># t[0] = 10     # TypeError</span></span><br><span class="line"><span class="comment"># t[1] = [5, 6] # TypeError</span></span><br><span class="line">t[<span class="number">1</span>].append(<span class="number">4</span>)   <span class="comment"># 合法！t 变为 (1, [2, 3, 4], 4)</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 元组解包 —— 优雅地处理多返回值</span></span><br><span class="line">coord = (<span class="number">120.5</span>, <span class="number">30.2</span>)</span><br><span class="line">lng, lat = coord</span><br><span class="line"></span><br><span class="line"><span class="comment"># 扩展解包（3.0+）</span></span><br><span class="line">first, *middle, last = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>]</span><br><span class="line"><span class="built_in">print</span>(middle)    <span class="comment"># [2, 3, 4]</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 单元素元组（注意逗号！）</span></span><br><span class="line">single = (<span class="number">42</span>,)   <span class="comment"># (42,) 是元组</span></span><br><span class="line">not_tuple = (<span class="number">42</span>) <span class="comment"># 42 是整数，括号被当作数学优先级</span></span><br></pre></td></tr></table></figure><p><strong>元组作为 dict 键的优势</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><span class="line"><span class="comment"># 元组是不可变的（前提是内部元素也 immutable），因此可以作为字典的键</span></span><br><span class="line">location_data = &#123;</span><br><span class="line">    (<span class="number">35.6762</span>, <span class="number">139.6503</span>): <span class="string">&quot;东京&quot;</span>,</span><br><span class="line">    (<span class="number">39.9042</span>, <span class="number">116.4074</span>): <span class="string">&quot;北京&quot;</span>,</span><br><span class="line">    (<span class="number">40.7128</span>, -<span class="number">74.0060</span>): <span class="string">&quot;纽约&quot;</span>,</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 列表不能作为字典的键（因为列表可变，不可哈希）</span></span><br><span class="line"><span class="comment"># key = [1, 2]   # TypeError: unhashable type: &#x27;list&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 注意：元组内含可变对象时也不能作为键</span></span><br><span class="line"><span class="comment"># bad_key = (1, [2, 3])  # TypeError: unhashable type: &#x27;list&#x27;</span></span><br></pre></td></tr></table></figure><p><strong>namedtuple 用法</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><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><span class="line"><span class="keyword">from</span> collections <span class="keyword">import</span> namedtuple</span><br><span class="line"></span><br><span class="line"><span class="comment"># 定义命名元组</span></span><br><span class="line">Point = namedtuple(<span class="string">&#x27;Point&#x27;</span>, [<span class="string">&#x27;x&#x27;</span>, <span class="string">&#x27;y&#x27;</span>])</span><br><span class="line">p = Point(x=<span class="number">3</span>, y=<span class="number">4</span>)</span><br><span class="line"><span class="built_in">print</span>(p.x, p.y)          <span class="comment"># 3 4</span></span><br><span class="line"><span class="built_in">print</span>(p[<span class="number">0</span>], p[<span class="number">1</span>])        <span class="comment"># 3 4（仍支持索引访问）</span></span><br><span class="line">x, y = p                  <span class="comment"># 支持解包</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 命名元组比普通元组可读性更好</span></span><br><span class="line"><span class="comment"># 普通元组：point = (3, 4)，需要记住位置含义</span></span><br><span class="line"><span class="comment"># 命名元组：point.x, point.y，自文档化</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 命名元组是不可变的</span></span><br><span class="line"><span class="comment"># p.x = 10  # AttributeError</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># _make 从可迭代对象创建</span></span><br><span class="line">coords = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>]</span><br><span class="line">Point = namedtuple(<span class="string">&#x27;Point&#x27;</span>, [<span class="string">&#x27;x&#x27;</span>, <span class="string">&#x27;y&#x27;</span>, <span class="string">&#x27;z&#x27;</span>, <span class="string">&#x27;w&#x27;</span>])</span><br><span class="line">p = Point._make(coords)</span><br><span class="line"></span><br><span class="line"><span class="comment"># _asdict 转为字典</span></span><br><span class="line"><span class="built_in">print</span>(p._asdict())        <span class="comment"># OrderedDict([(&#x27;x&#x27;, 1), (&#x27;y&#x27;, 2), (&#x27;z&#x27;, 3), (&#x27;w&#x27;, 4)])</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># _replace 创建新实例（因为不可变，只能&quot;替换&quot;生成新对象）</span></span><br><span class="line">p2 = p._replace(x=<span class="number">10</span>)</span><br><span class="line"><span class="built_in">print</span>(p2)                 <span class="comment"># Point(x=10, y=2, z=3, w=4)</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Python 3.7+ 还可以用 typing.NamedTuple（支持类型提示）</span></span><br><span class="line"><span class="keyword">from</span> typing <span class="keyword">import</span> NamedTuple</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Employee</span>(<span class="title class_ inherited__">NamedTuple</span>):</span><br><span class="line">    name: <span class="built_in">str</span></span><br><span class="line">    age: <span class="built_in">int</span></span><br><span class="line">    department: <span class="built_in">str</span> = <span class="string">&quot;Engineering&quot;</span>  <span class="comment"># 支持默认值</span></span><br><span class="line"></span><br><span class="line">emp = Employee(<span class="string">&quot;Alice&quot;</span>, <span class="number">30</span>)</span><br><span class="line"><span class="built_in">print</span>(emp.name, emp.department)   <span class="comment"># Alice Engineering</span></span><br></pre></td></tr></table></figure><h4 id="range-深入理解"><a href="#range-深入理解" class="headerlink" title="range 深入理解"></a><code>range</code> 深入理解</h4><p><code>range</code> 是 Python 中常被低估的序列类型，它不只是 for 循环的辅助工具。</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><span class="line"><span class="comment"># range 是惰性序列，不存储所有元素</span></span><br><span class="line">r = <span class="built_in">range</span>(<span class="number">0</span>, <span class="number">10</span>, <span class="number">2</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">type</span>(r))          <span class="comment"># &lt;class &#x27;range&#x27;&gt;</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">list</span>(r))          <span class="comment"># [0, 2, 4, 6, 8]</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">len</span>(r))           <span class="comment"># 5</span></span><br><span class="line"><span class="built_in">print</span>(r[<span class="number">3</span>])             <span class="comment"># 6（支持索引访问！）</span></span><br><span class="line"><span class="built_in">print</span>(r.index(<span class="number">4</span>))       <span class="comment"># 2（支持查找）</span></span><br><span class="line"><span class="built_in">print</span>(r.count(<span class="number">2</span>))       <span class="comment"># 1</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 内存优势：range(1000000) 只占约 48 字节，无论范围多大</span></span><br><span class="line"><span class="keyword">import</span> sys</span><br><span class="line"><span class="built_in">print</span>(sys.getsizeof(<span class="built_in">range</span>(<span class="number">1000000</span>)))       <span class="comment"># 48</span></span><br><span class="line"><span class="built_in">print</span>(sys.getsizeof(<span class="built_in">list</span>(<span class="built_in">range</span>(<span class="number">1000000</span>)))) <span class="comment"># 约 8MB</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 底层原理：range 存储 (start, stop, step) 三元组，通过公式计算值</span></span><br><span class="line"><span class="comment"># 值 = start + index * step</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 工程场景：大范围遍历、检查某个数是否在范围内</span></span><br><span class="line"><span class="comment"># 检查性能：in 操作是 O(1)</span></span><br><span class="line"><span class="built_in">print</span>(<span class="number">500000</span> <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">1000000</span>))   <span class="comment"># True，瞬间返回</span></span><br></pre></td></tr></table></figure><h3 id="3-3-哈希类数据结构"><a href="#3-3-哈希类数据结构" class="headerlink" title="3.3 哈希类数据结构"></a>3.3 哈希类数据结构</h3><h4 id="字典-dict（3-7-有序，键必须可哈希）"><a href="#字典-dict（3-7-有序，键必须可哈希）" class="headerlink" title="字典 dict（3.7+ 有序，键必须可哈希）"></a>字典 <code>dict</code>（3.7+ 有序，键必须可哈希）</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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 字典推导式</span></span><br><span class="line">word_lengths = &#123;word: <span class="built_in">len</span>(word) <span class="keyword">for</span> word <span class="keyword">in</span> [<span class="string">&quot;apple&quot;</span>, <span class="string">&quot;banana&quot;</span>, <span class="string">&quot;cherry&quot;</span>]&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># get() 避免 KeyError</span></span><br><span class="line">counts = &#123;<span class="string">&quot;a&quot;</span>: <span class="number">1</span>&#125;</span><br><span class="line"><span class="built_in">print</span>(counts.get(<span class="string">&quot;b&quot;</span>, <span class="number">0</span>))   <span class="comment"># 0，不报错</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># setdefault() 与 defaultdict 的对比</span></span><br><span class="line"><span class="comment"># 笨方法：</span></span><br><span class="line"><span class="keyword">if</span> <span class="string">&quot;b&quot;</span> <span class="keyword">not</span> <span class="keyword">in</span> counts:</span><br><span class="line">    counts[<span class="string">&quot;b&quot;</span>] = []</span><br><span class="line">counts[<span class="string">&quot;b&quot;</span>].append(<span class="number">2</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 优雅方法（但 setdefault 可读性一般）</span></span><br><span class="line">counts.setdefault(<span class="string">&quot;c&quot;</span>, []).append(<span class="number">3</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 最优雅：collections.defaultdict</span></span><br><span class="line"><span class="keyword">from</span> collections <span class="keyword">import</span> defaultdict</span><br><span class="line">d = defaultdict(<span class="built_in">list</span>)</span><br><span class="line">d[<span class="string">&quot;d&quot;</span>].append(<span class="number">4</span>)   <span class="comment"># 自动创建空列表</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 合并字典（3.9+）</span></span><br><span class="line">d1 = &#123;<span class="string">&quot;a&quot;</span>: <span class="number">1</span>, <span class="string">&quot;b&quot;</span>: <span class="number">2</span>&#125;</span><br><span class="line">d2 = &#123;<span class="string">&quot;b&quot;</span>: <span class="number">3</span>, <span class="string">&quot;c&quot;</span>: <span class="number">4</span>&#125;</span><br><span class="line">merged = d1 | d2    <span class="comment"># &#123;&#x27;a&#x27;: 1, &#x27;b&#x27;: 3, &#x27;c&#x27;: 4&#125; —— d2 覆盖 d1 的同名键</span></span><br></pre></td></tr></table></figure><p><strong>dict 底层哈希表实现原理</strong>：</p><figure class="highlight plaintext"><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><span class="line">哈希表（Hash Table）内部结构：</span><br><span class="line"></span><br><span class="line">┌─────────────────────────────────────────────┐</span><br><span class="line">│ 哈希函数 hash(key) → 索引位置                │</span><br><span class="line">│                                              │</span><br><span class="line">│ Bucket Array（桶数组）:                       │</span><br><span class="line">│  ┌───┬──────────────────┬──────────────┐    │</span><br><span class="line">│  │ 0 │   (空)            │              │    │</span><br><span class="line">│  ├───┼──────────────────┼──────────────┤    │</span><br><span class="line">│  │ 1 │ hash=...  key=&#x27;name&#x27; val=&#x27;Alice&#x27;│    │</span><br><span class="line">│  ├───┼──────────────────┼──────────────┤    │</span><br><span class="line">│  │ 2 │   (空)            │              │    │</span><br><span class="line">│  ├───┼──────────────────┼──────────────┤    │</span><br><span class="line">│  │ 3 │ hash=...  key=&#x27;age&#x27;  val=30     │    │</span><br><span class="line">│  ├───┼──────────────────┼──────────────┤    │</span><br><span class="line">│  │ 4 │ (碰撞→指向下一位置)              │    │</span><br><span class="line">│  └───┴──────────────────┴──────────────┘    │</span><br><span class="line">│                                              │</span><br><span class="line">│ 查找流程:                                    │</span><br><span class="line">│ 1. hash(key) → 计算哈希值                    │</span><br><span class="line">│ 2. hash % len(table) → 定位桶索引            │</span><br><span class="line">│ 3. 比较键是否相等 → 找到或继续探查            │</span><br><span class="line">│ 4. 负载因子 &gt; 2/3 时扩容（重新哈希）          │</span><br><span class="line">└─────────────────────────────────────────────┘</span><br></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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 哈希表的键要求</span></span><br><span class="line"><span class="comment"># 1. 可哈希（hashable）：实现了 __hash__ 和 __eq__</span></span><br><span class="line"><span class="comment"># 2. 不可变类型：int, float, str, tuple（内部元素也需可哈希）, frozenset, bool, None</span></span><br><span class="line"><span class="comment"># 3. 不可哈希：list, dict, set, bytearray</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看对象的哈希值</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">hash</span>(<span class="string">&quot;name&quot;</span>))     <span class="comment"># 整数</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">hash</span>(<span class="number">42</span>))         <span class="comment"># 42</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">hash</span>((<span class="number">1</span>, <span class="number">2</span>)))     <span class="comment"># 整数</span></span><br><span class="line"><span class="comment"># hash([1, 2])          # TypeError: unhashable type: &#x27;list&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 自定义对象默认可哈希（基于 id），但定义了 __eq__ 后需同时定义 __hash__</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Person</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, name</span>):</span><br><span class="line">        <span class="variable language_">self</span>.name = name</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__eq__</span>(<span class="params">self, other</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>.name == other.name</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__hash__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">hash</span>(<span class="variable language_">self</span>.name)  <span class="comment"># 必须！否则变为不可哈希</span></span><br></pre></td></tr></table></figure><p><strong>dict vs list 性能对比数据</strong>：</p><table><thead><tr><th>操作</th><th>dict 时间复杂度</th><th>list 时间复杂度</th><th>倍率</th></tr></thead><tbody><tr><td>查找 <code>x in obj</code></td><td>O(1)</td><td>O(n)</td><td>dict 快约 100-10000 倍</td></tr><tr><td>插入 <code>obj[k] = v</code></td><td>O(1)均摊</td><td><code>append</code> O(1), <code>insert(i,v)</code> O(n)</td><td>dict 更稳定</td></tr><tr><td>删除 <code>del obj[k]</code></td><td>O(1)</td><td>O(n)</td><td>dict 快得多</td></tr><tr><td>遍历</td><td>O(n)</td><td>O(n)</td><td>相同</td></tr><tr><td>内存占用</td><td>较大（哈希表开销）</td><td>较小</td><td>list 更省内存</td></tr></tbody></table><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><span class="line"><span class="comment"># 实际性能测试</span></span><br><span class="line"><span class="keyword">import</span> timeit</span><br><span class="line"></span><br><span class="line"><span class="comment"># 在 100000 个元素中查找</span></span><br><span class="line">setup_dict = <span class="string">&quot;d = &#123;i: i*2 for i in range(100000)&#125;&quot;</span></span><br><span class="line">setup_list = <span class="string">&quot;l = list(range(100000))&quot;</span></span><br><span class="line"></span><br><span class="line">dict_time = timeit.timeit(<span class="string">&quot;50000 in d&quot;</span>, setup=setup_dict, number=<span class="number">10000</span>)</span><br><span class="line">list_time = timeit.timeit(<span class="string">&quot;50000 in l&quot;</span>, setup=setup_list, number=<span class="number">10000</span>)</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;dict 查找: <span class="subst">&#123;dict_time:<span class="number">.4</span>f&#125;</span>s&quot;</span>)   <span class="comment"># 约 0.001s</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;list 查找: <span class="subst">&#123;list_time:<span class="number">.4</span>f&#125;</span>s&quot;</span>)   <span class="comment"># 约 3-5s</span></span><br><span class="line"><span class="comment"># dict 快约 3000-5000 倍！</span></span><br></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><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><span class="line"><span class="comment"># OrderedDict —— 有序字典（3.7 之前 dict 无序时的选择，现仍有特殊用途）</span></span><br><span class="line"><span class="keyword">from</span> collections <span class="keyword">import</span> OrderedDict</span><br><span class="line">od = OrderedDict()</span><br><span class="line">od[<span class="string">&#x27;z&#x27;</span>] = <span class="number">1</span></span><br><span class="line">od[<span class="string">&#x27;a&#x27;</span>] = <span class="number">2</span></span><br><span class="line">od[<span class="string">&#x27;m&#x27;</span>] = <span class="number">3</span></span><br><span class="line"><span class="built_in">print</span>(od)                    <span class="comment"># OrderedDict([(&#x27;z&#x27;, 1), (&#x27;a&#x27;, 2), (&#x27;m&#x27;, 3)])</span></span><br><span class="line"><span class="built_in">print</span>(od.popitem(last=<span class="literal">True</span>)) <span class="comment"># (&#x27;m&#x27;, 3) —— 弹出最后插入的</span></span><br><span class="line"><span class="built_in">print</span>(od.popitem(last=<span class="literal">False</span>))<span class="comment"># (&#x27;z&#x27;, 1) —— 弹出最先插入的（FIFO）</span></span><br><span class="line"><span class="comment"># 按插入顺序移动到末尾</span></span><br><span class="line">od[<span class="string">&#x27;a&#x27;</span>] = <span class="number">2</span>                  <span class="comment"># 不移动位置</span></span><br><span class="line">od.move_to_end(<span class="string">&#x27;a&#x27;</span>)          <span class="comment"># 移到末尾</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ChainMap —— 多字典链式查找</span></span><br><span class="line"><span class="keyword">from</span> collections <span class="keyword">import</span> ChainMap</span><br><span class="line">defaults = &#123;<span class="string">&quot;theme&quot;</span>: <span class="string">&quot;dark&quot;</span>, <span class="string">&quot;lang&quot;</span>: <span class="string">&quot;en&quot;</span>, <span class="string">&quot;debug&quot;</span>: <span class="literal">False</span>&#125;</span><br><span class="line">user_config = &#123;<span class="string">&quot;theme&quot;</span>: <span class="string">&quot;light&quot;</span>, <span class="string">&quot;lang&quot;</span>: <span class="string">&quot;zh&quot;</span>&#125;</span><br><span class="line">env_config = &#123;<span class="string">&quot;debug&quot;</span>: <span class="literal">True</span>&#125;</span><br><span class="line"></span><br><span class="line">config = ChainMap(env_config, user_config, defaults)</span><br><span class="line"><span class="built_in">print</span>(config[<span class="string">&quot;theme&quot;</span>])    <span class="comment"># &quot;light&quot;（从 user_config 中找到）</span></span><br><span class="line"><span class="built_in">print</span>(config[<span class="string">&quot;debug&quot;</span>])    <span class="comment"># True（从 env_config 中找到）</span></span><br><span class="line"><span class="built_in">print</span>(config[<span class="string">&quot;lang&quot;</span>])     <span class="comment"># &quot;zh&quot;（从 user_config 中找到）</span></span><br><span class="line"><span class="comment"># 查找顺序：从左到右，第一个找到的为准</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Counter —— 计数器</span></span><br><span class="line"><span class="keyword">from</span> collections <span class="keyword">import</span> Counter</span><br><span class="line">words = [<span class="string">&quot;apple&quot;</span>, <span class="string">&quot;banana&quot;</span>, <span class="string">&quot;apple&quot;</span>, <span class="string">&quot;cherry&quot;</span>, <span class="string">&quot;banana&quot;</span>, <span class="string">&quot;apple&quot;</span>]</span><br><span class="line">word_counts = Counter(words)</span><br><span class="line"><span class="built_in">print</span>(word_counts)                    <span class="comment"># Counter(&#123;&#x27;apple&#x27;: 3, &#x27;banana&#x27;: 2, &#x27;cherry&#x27;: 1&#125;)</span></span><br><span class="line"><span class="built_in">print</span>(word_counts.most_common(<span class="number">2</span>))     <span class="comment"># [(&#x27;apple&#x27;, 3), (&#x27;banana&#x27;, 2)]</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Counter 运算</span></span><br><span class="line">c1 = Counter(<span class="string">&quot;aabbc&quot;</span>)</span><br><span class="line">c2 = Counter(<span class="string">&quot;abccc&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(c1 + c2)     <span class="comment"># Counter(&#123;&#x27;c&#x27;: 4, &#x27;a&#x27;: 3, &#x27;b&#x27;: 3&#125;)  —— 加法</span></span><br><span class="line"><span class="built_in">print</span>(c1 - c2)     <span class="comment"># Counter(&#123;&#x27;a&#x27;: 1&#125;)                    —— 减法（只保留正数）</span></span><br><span class="line"><span class="built_in">print</span>(c1 &amp; c2)     <span class="comment"># Counter(&#123;&#x27;a&#x27;: 1, &#x27;b&#x27;: 1, &#x27;c&#x27;: 1&#125;)  —— 交集（取最小）</span></span><br><span class="line"><span class="built_in">print</span>(c1 | c2)     <span class="comment"># Counter(&#123;&#x27;a&#x27;: 2, &#x27;b&#x27;: 2, &#x27;c&#x27;: 3&#125;)  —— 并集（取最大）</span></span><br></pre></td></tr></table></figure><blockquote><p><strong>面试真题</strong>：Python 字典的底层实现原理是什么？为什么字典的键必须是可哈希的？dict 和 list 在查找性能上差多少？</p><p><strong>易错点</strong>：① Python 3.7+ 的 dict 是有序的（按插入顺序），但不要将 dict 当作有序数据结构使用，<code>OrderedDict</code> 才有 <code>move_to_end</code>、<code>popitem(last=False)</code> 等有序操作；② 自定义类定义了 <code>__eq__</code> 后不定义 <code>__hash__</code> 会使实例变为不可哈希。</p></blockquote><p><img src="https://trae-api-cn.mchost.guru/api/ide/v1/text_to_image?prompt=Python%20dictionary%20hash%20table%20internal%20structure%20diagram%20showing%20key-value%20pairs%20with%20hash%20function%20and%20bucket%20array,%20technical%20illustration,%20blue%20theme&image_size=landscape_16_9" alt="Python dictionary hash table internal structure diagram showing key-value pairs with hash function and bucket array, technical illustration, blue theme"></p><h4 id="集合-set（可变）与-frozenset（不可变）"><a href="#集合-set（可变）与-frozenset（不可变）" class="headerlink" title="集合 set（可变）与 frozenset（不可变）"></a>集合 <code>set</code>（可变）与 <code>frozenset</code>（不可变）</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></pre></td><td class="code"><pre><span class="line">a = &#123;<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">3</span>, <span class="number">3</span>&#125;   <span class="comment"># &#123;1, 2, 3&#125;，自动去重</span></span><br><span class="line">b = &#123;<span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 集合运算（代码比数学公式还简洁）</span></span><br><span class="line"><span class="built_in">print</span>(a | b)     <span class="comment"># 并集：&#123;1, 2, 3, 4, 5&#125;</span></span><br><span class="line"><span class="built_in">print</span>(a &amp; b)     <span class="comment"># 交集：&#123;3&#125;</span></span><br><span class="line"><span class="built_in">print</span>(a - b)     <span class="comment"># 差集：&#123;1, 2&#125;</span></span><br><span class="line"><span class="built_in">print</span>(a ^ b)     <span class="comment"># 对称差集：&#123;1, 2, 4, 5&#125;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 典型应用场景：去重与成员检测</span></span><br><span class="line">users_seen = <span class="built_in">set</span>()</span><br><span class="line"><span class="keyword">for</span> user_id <span class="keyword">in</span> stream:</span><br><span class="line">    <span class="keyword">if</span> user_id <span class="keyword">in</span> users_seen:      <span class="comment"># O(1)，远快于列表的 O(n)</span></span><br><span class="line">        <span class="keyword">continue</span></span><br><span class="line">    users_seen.add(user_id)</span><br><span class="line">    process(user_id)</span><br><span class="line"></span><br><span class="line"><span class="comment"># frozenset —— 不可变集合，可作为 dict 的键</span></span><br><span class="line">fs = <span class="built_in">frozenset</span>([<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>])</span><br><span class="line">d = &#123;fs: <span class="string">&quot;hello&quot;</span>&#125;    <span class="comment"># 合法！</span></span><br></pre></td></tr></table></figure><h3 id="3-4-特殊类型"><a href="#3-4-特殊类型" class="headerlink" title="3.4 特殊类型"></a>3.4 特殊类型</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><span class="line"><span class="comment"># None —— 表示&quot;无&quot;的单例对象</span></span><br><span class="line">result = <span class="literal">None</span></span><br><span class="line"><span class="built_in">print</span>(result <span class="keyword">is</span> <span class="literal">None</span>)    <span class="comment"># True（用 is 判断，不要用 ==）</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 函数默认参数的陷阱（经典面试题）</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">append_item</span>(<span class="params">item, lst=[]</span>):</span><br><span class="line">    lst.append(item)</span><br><span class="line">    <span class="keyword">return</span> lst</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(append_item(<span class="number">1</span>))   <span class="comment"># [1]</span></span><br><span class="line"><span class="built_in">print</span>(append_item(<span class="number">2</span>))   <span class="comment"># [1, 2] —— 惊喜！默认参数在定义时求值，lst 被共享了</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 正确写法</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">append_item_safe</span>(<span class="params">item, lst=<span class="literal">None</span></span>):</span><br><span class="line">    <span class="keyword">if</span> lst <span class="keyword">is</span> <span class="literal">None</span>:</span><br><span class="line">        lst = []</span><br><span class="line">    lst.append(item)</span><br><span class="line">    <span class="keyword">return</span> lst</span><br><span class="line"></span><br><span class="line"><span class="comment"># Ellipsis —— 省略号对象，NumPy 中用于切片，类型提示中表示可变长度元组</span></span><br><span class="line"><span class="keyword">from</span> typing <span class="keyword">import</span> <span class="type">Tuple</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">process</span>(<span class="params">items: <span class="type">Tuple</span>[<span class="built_in">int</span>, ...]</span>) -&gt; <span class="literal">None</span>:</span><br><span class="line">    ...                    <span class="comment"># pass 的占位符，常用于 stub 文件</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># memoryview —— 内存视图（零拷贝访问内存）</span></span><br><span class="line"><span class="comment"># 在不复制数据的情况下，以不同方式查看底层内存</span></span><br><span class="line">data = <span class="built_in">bytearray</span>(<span class="string">b&quot;hello world&quot;</span>)</span><br><span class="line">mv = <span class="built_in">memoryview</span>(data)</span><br><span class="line"><span class="built_in">print</span>(mv[<span class="number">0</span>])              <span class="comment"># 104（访问第一个字节）</span></span><br><span class="line">mv[<span class="number">0</span>] = <span class="number">72</span>                <span class="comment"># 修改内存视图会反映到原数据</span></span><br><span class="line"><span class="built_in">print</span>(data)               <span class="comment"># bytearray(b&#x27;Hello world&#x27;)</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 切片不复制数据，返回新视图</span></span><br><span class="line">slice_view = mv[<span class="number">6</span>:<span class="number">11</span>]</span><br><span class="line"><span class="built_in">print</span>(slice_view.tobytes())   <span class="comment"># b&#x27;world&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 工程场景：大文件处理、网络包解析、NumPy 数组操作</span></span><br><span class="line"><span class="comment"># 性能优势：避免大量数据复制</span></span><br></pre></td></tr></table></figure><p><strong>Enum 枚举类型</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><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><span class="line"><span class="keyword">from</span> enum <span class="keyword">import</span> Enum, auto, IntEnum, Flag, IntFlag</span><br><span class="line"></span><br><span class="line"><span class="comment"># 基本枚举</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Color</span>(<span class="title class_ inherited__">Enum</span>):</span><br><span class="line">    RED = <span class="number">1</span></span><br><span class="line">    GREEN = <span class="number">2</span></span><br><span class="line">    BLUE = <span class="number">3</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(Color.RED)              <span class="comment"># Color.RED</span></span><br><span class="line"><span class="built_in">print</span>(Color.RED.value)        <span class="comment"># 1</span></span><br><span class="line"><span class="built_in">print</span>(Color(<span class="number">1</span>))               <span class="comment"># Color.RED（通过值查找）</span></span><br><span class="line"><span class="built_in">print</span>(Color[<span class="string">&#x27;RED&#x27;</span>])           <span class="comment"># Color.RED（通过名称查找）</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># auto() 自动分配值</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Status</span>(<span class="title class_ inherited__">Enum</span>):</span><br><span class="line">    PENDING = auto()</span><br><span class="line">    PROCESSING = auto()</span><br><span class="line">    DONE = auto()</span><br><span class="line"></span><br><span class="line"><span class="comment"># IntEnum —— 可与整数比较</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Priority</span>(<span class="title class_ inherited__">IntEnum</span>):</span><br><span class="line">    LOW = <span class="number">1</span></span><br><span class="line">    MEDIUM = <span class="number">2</span></span><br><span class="line">    HIGH = <span class="number">3</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(Priority.HIGH &gt; Priority.LOW)   <span class="comment"># True</span></span><br><span class="line"><span class="built_in">print</span>(Priority.HIGH == <span class="number">3</span>)             <span class="comment"># True（IntEnum 支持与 int 比较）</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Flag / IntFlag —— 支持位运算的组合枚举</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Permission</span>(<span class="title class_ inherited__">Flag</span>):</span><br><span class="line">    READ = auto()       <span class="comment"># 1</span></span><br><span class="line">    WRITE = auto()      <span class="comment"># 2</span></span><br><span class="line">    EXECUTE = auto()    <span class="comment"># 4</span></span><br><span class="line"></span><br><span class="line">perm = Permission.READ | Permission.WRITE</span><br><span class="line"><span class="built_in">print</span>(perm)                         <span class="comment"># Permission.READ|WRITE</span></span><br><span class="line"><span class="built_in">print</span>(Permission.READ <span class="keyword">in</span> perm)      <span class="comment"># True</span></span><br></pre></td></tr></table></figure><p><strong>dataclass 数据类</strong>（Python 3.7+）：</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> dataclasses <span class="keyword">import</span> dataclass, field, asdict, astuple</span><br><span class="line"></span><br><span class="line"><span class="meta">@dataclass</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">User</span>:</span><br><span class="line">    name: <span class="built_in">str</span></span><br><span class="line">    age: <span class="built_in">int</span></span><br><span class="line">    email: <span class="built_in">str</span> = <span class="string">&quot;&quot;</span>                      <span class="comment"># 默认值</span></span><br><span class="line">    tags: <span class="built_in">list</span>[<span class="built_in">str</span>] = field(default_factory=<span class="built_in">list</span>)  <span class="comment"># 可变默认值必须用 field</span></span><br><span class="line">    <span class="built_in">id</span>: <span class="built_in">int</span> = field(init=<span class="literal">False</span>, <span class="built_in">repr</span>=<span class="literal">False</span>)  <span class="comment"># 不参与 __init__，不显示在 repr 中</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__post_init__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;初始化后自动调用&quot;&quot;&quot;</span></span><br><span class="line">        <span class="variable language_">self</span>.<span class="built_in">id</span> = <span class="built_in">hash</span>(<span class="variable language_">self</span>.name)</span><br><span class="line"></span><br><span class="line">user = User(<span class="string">&quot;Alice&quot;</span>, <span class="number">30</span>, tags=[<span class="string">&quot;admin&quot;</span>])</span><br><span class="line"><span class="built_in">print</span>(user)                   <span class="comment"># User(name=&#x27;Alice&#x27;, age=30, email=&#x27;&#x27;, tags=[&#x27;admin&#x27;])</span></span><br><span class="line"><span class="built_in">print</span>(asdict(user))           <span class="comment"># &#123;&#x27;name&#x27;: &#x27;Alice&#x27;, &#x27;age&#x27;: 30, &#x27;email&#x27;: &#x27;&#x27;, &#x27;tags&#x27;: [&#x27;admin&#x27;], &#x27;id&#x27;: ...&#125;</span></span><br><span class="line"><span class="built_in">print</span>(astuple(user))          <span class="comment"># (&#x27;Alice&#x27;, 30, &#x27;&#x27;, [&#x27;admin&#x27;], ...)</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># frozen=True 使实例不可变（可哈希，可作为 dict 键）</span></span><br><span class="line"><span class="meta">@dataclass(<span class="params">frozen=<span class="literal">True</span></span>)</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Point</span>:</span><br><span class="line">    x: <span class="built_in">float</span></span><br><span class="line">    y: <span class="built_in">float</span></span><br><span class="line"></span><br><span class="line">p = Point(<span class="number">1.0</span>, <span class="number">2.0</span>)</span><br><span class="line"><span class="comment"># p.x = 3.0  # FrozenInstanceError</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">hash</span>(p))   <span class="comment"># 可哈希！</span></span><br></pre></td></tr></table></figure><p><strong>typing 常用类型提示</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><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><span class="line"><span class="keyword">from</span> typing <span class="keyword">import</span> (</span><br><span class="line">    <span class="type">Optional</span>, <span class="type">Union</span>, <span class="type">Literal</span>, TypeAlias,</span><br><span class="line">    <span class="type">Callable</span>, Iterable, <span class="type">Sequence</span>, Mapping,</span><br><span class="line">    TypeVar, <span class="type">Generic</span>, Protocol, <span class="type">Any</span>, Never,</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Optional —— 可选类型（等价于 Union[T, None]）</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">find_user</span>(<span class="params">user_id: <span class="built_in">int</span></span>) -&gt; <span class="type">Optional</span>[<span class="built_in">dict</span>]:</span><br><span class="line">    ...  <span class="comment"># 返回 dict 或 None</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Union —— 联合类型</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">process</span>(<span class="params">value: <span class="type">Union</span>[<span class="built_in">str</span>, <span class="built_in">int</span>, <span class="built_in">float</span>]</span>) -&gt; <span class="built_in">str</span>:</span><br><span class="line">    ...  <span class="comment"># 接受 str/int/float</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Python 3.10+ 可用 | 语法</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">process</span>(<span class="params">value: <span class="built_in">str</span> | <span class="built_in">int</span> | <span class="built_in">float</span></span>) -&gt; <span class="built_in">str</span>:</span><br><span class="line">    ...</span><br><span class="line"></span><br><span class="line"><span class="comment"># Literal —— 字面量类型</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">set_mode</span>(<span class="params">mode: <span class="type">Literal</span>[<span class="string">&quot;train&quot;</span>, <span class="string">&quot;eval&quot;</span>, <span class="string">&quot;test&quot;</span>]</span>) -&gt; <span class="literal">None</span>:</span><br><span class="line">    ...  <span class="comment"># 只接受这三个字符串</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># TypeAlias —— 类型别名</span></span><br><span class="line">Vector: TypeAlias = <span class="built_in">list</span>[<span class="built_in">float</span>]  <span class="comment"># Python 3.12+ 可用 type Vector = list[float]</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Callable —— 可调用对象</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">apply</span>(<span class="params">func: <span class="type">Callable</span>[[<span class="built_in">int</span>, <span class="built_in">int</span>], <span class="built_in">int</span>], a: <span class="built_in">int</span>, b: <span class="built_in">int</span></span>) -&gt; <span class="built_in">int</span>:</span><br><span class="line">    <span class="keyword">return</span> func(a, b)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 泛型</span></span><br><span class="line">T = TypeVar(<span class="string">&#x27;T&#x27;</span>)</span><br><span class="line"><span class="keyword">def</span> <span class="title function_">first</span>(<span class="params">items: <span class="built_in">list</span>[T]</span>) -&gt; T:</span><br><span class="line">    <span class="keyword">return</span> items[<span class="number">0</span>]</span><br><span class="line"></span><br><span class="line"><span class="comment"># Protocol —— 结构化子类型（鸭子类型的类型提示版）</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Drawable</span>(<span class="title class_ inherited__">Protocol</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">draw</span>(<span class="params">self</span>) -&gt; <span class="literal">None</span>: ...</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">render</span>(<span class="params">obj: Drawable</span>) -&gt; <span class="literal">None</span>:</span><br><span class="line">    obj.draw()  <span class="comment"># 任何有 draw() 方法的对象都满足</span></span><br></pre></td></tr></table></figure><blockquote><p><strong>面试真题</strong>：Python 的 <code>@dataclass</code> 和普通 <code>class</code> 有什么区别？<code>Enum</code> 和普通常量相比有什么优势？</p><p><strong>易错点</strong>：① dataclass 中可变默认值必须使用 <code>field(default_factory=list)</code>，不能直接写 <code>tags: list = []</code>；② <code>Enum</code> 成员是单例，比较用 <code>is</code> 而非 <code>==</code>（虽然 <code>==</code> 也能用）；③ <code>Optional[T]</code> 不等于 <code>T | None</code>（Python 3.10 以下），但语义相同。</p></blockquote><hr><h2 id="4-运算符与表达式"><a href="#4-运算符与表达式" class="headerlink" title="4. 运算符与表达式"></a>4. 运算符与表达式</h2><h3 id="4-1-运算符分类"><a href="#4-1-运算符分类" class="headerlink" title="4.1 运算符分类"></a>4.1 运算符分类</h3><table><thead><tr><th>类别</th><th>运算符</th><th>要点</th></tr></thead><tbody><tr><td>算术</td><td><code>+ - * / // % **</code></td><td><code>//</code> 是向下取整除法，<code>-3 // 2 == -2</code></td></tr><tr><td>比较</td><td><code>== != &gt; &lt; &gt;= &lt;=</code></td><td>可链式比较：<code>1 &lt; x &lt; 10</code></td></tr><tr><td>赋值</td><td><code>= += -= *= /=</code></td><td>没有 <code>++</code> &#x2F; <code>--</code></td></tr><tr><td>逻辑</td><td><code>and or not</code></td><td>短路求值，返回最后一个求值的操作数</td></tr><tr><td>位运算</td><td><code>&amp; | ^ ~ &lt;&lt; &gt;&gt;</code></td><td>直接操作二进制位，效率优化场景使用</td></tr><tr><td>成员</td><td><code>in not in</code></td><td>字典判断键：<code>&quot;key&quot; in dict_obj</code></td></tr><tr><td>身份</td><td><code>is is not</code></td><td>判断同一对象，不要用于值比较</td></tr></tbody></table><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><span class="line"><span class="comment"># Python 的 // 是向下取整（floor division），而非截断取整</span></span><br><span class="line"><span class="built_in">print</span>(<span class="number">7</span> // <span class="number">2</span>)     <span class="comment"># 3（正数：向下取整 = 截断取整）</span></span><br><span class="line"><span class="built_in">print</span>(-<span class="number">7</span> // <span class="number">2</span>)    <span class="comment"># -4（负数：向下取整 ≠ 截断取整！）</span></span><br><span class="line"><span class="comment"># 数学：-7 / 2 = -3.5，向下取整为 -4</span></span><br><span class="line"><span class="comment"># C/Java 截断取整：-7 / 2 = -3</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># divmod 同时获取商和余数</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">divmod</span>(<span class="number">7</span>, <span class="number">2</span>))     <span class="comment"># (3, 1)</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">divmod</span>(-<span class="number">7</span>, <span class="number">2</span>))    <span class="comment"># (-4, 1)  ← 余数始终与除数同号</span></span><br><span class="line"><span class="comment"># 验证：商 * 除数 + 余数 = 被除数 → -4 * 2 + 1 = -7 ✓</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># % 取模运算也是向下取整配套的</span></span><br><span class="line"><span class="built_in">print</span>(-<span class="number">7</span> % <span class="number">2</span>)    <span class="comment"># 1（不是 -1！）</span></span><br><span class="line"><span class="built_in">print</span>(<span class="number">7</span> % -<span class="number">2</span>)    <span class="comment"># -1</span></span><br><span class="line"><span class="comment"># 规则：a % b 的符号与 b 相同</span></span><br></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><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><span class="line"><span class="comment"># 1. 权限控制（Linux 文件权限模型）</span></span><br><span class="line">READ = <span class="number">0b100</span>     <span class="comment"># 4</span></span><br><span class="line">WRITE = <span class="number">0b010</span>    <span class="comment"># 2</span></span><br><span class="line">EXECUTE = <span class="number">0b001</span>  <span class="comment"># 1</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 组合权限</span></span><br><span class="line">user_perm = READ | WRITE          <span class="comment"># 0b110 = 6</span></span><br><span class="line">admin_perm = READ | WRITE | EXECUTE  <span class="comment"># 0b111 = 7</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 检查权限</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">bool</span>(user_perm &amp; READ))     <span class="comment"># True（有读权限）</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">bool</span>(user_perm &amp; EXECUTE))  <span class="comment"># False（无执行权限）</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 添加/移除权限</span></span><br><span class="line">user_perm |= EXECUTE              <span class="comment"># 添加执行权限</span></span><br><span class="line">user_perm &amp;= ~WRITE               <span class="comment"># 移除写权限</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 2. 颜色计算（RGB 值）</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">rgb_to_hex</span>(<span class="params">r, g, b</span>):</span><br><span class="line">    <span class="keyword">return</span> (r &lt;&lt; <span class="number">16</span>) | (g &lt;&lt; <span class="number">8</span>) | b</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">hex_to_rgb</span>(<span class="params">hex_color</span>):</span><br><span class="line">    r = (hex_color &gt;&gt; <span class="number">16</span>) &amp; <span class="number">0xFF</span></span><br><span class="line">    g = (hex_color &gt;&gt; <span class="number">8</span>) &amp; <span class="number">0xFF</span></span><br><span class="line">    b = hex_color &amp; <span class="number">0xFF</span></span><br><span class="line">    <span class="keyword">return</span> r, g, b</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">hex</span>(rgb_to_hex(<span class="number">255</span>, <span class="number">128</span>, <span class="number">0</span>)))    <span class="comment"># &#x27;0xff8000&#x27;</span></span><br><span class="line"><span class="built_in">print</span>(hex_to_rgb(<span class="number">0xff8000</span>))             <span class="comment"># (255, 128, 0)</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 3. 快速乘除 2 的幂</span></span><br><span class="line"><span class="built_in">print</span>(<span class="number">5</span> &lt;&lt; <span class="number">3</span>)    <span class="comment"># 40（5 * 2^3 = 40）</span></span><br><span class="line"><span class="built_in">print</span>(<span class="number">40</span> &gt;&gt; <span class="number">3</span>)   <span class="comment"># 5（40 / 2^3 = 5）</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 4. 交换两个数（不用临时变量）</span></span><br><span class="line">a, b = <span class="number">10</span>, <span class="number">20</span></span><br><span class="line">a ^= b; b ^= a; a ^= b</span><br><span class="line"><span class="built_in">print</span>(a, b)  <span class="comment"># 20 10</span></span><br><span class="line"><span class="comment"># 实际开发中用 a, b = b, a 更 Pythonic</span></span><br></pre></td></tr></table></figure><blockquote><p><strong>面试真题</strong>：Python 中 <code>-7 // 2</code> 的结果是什么？和 C 语言的结果一样吗？位运算如何实现权限控制？</p><p><strong>易错点</strong>：① Python 的 <code>//</code> 是向下取整，负数结果与 C&#x2F;Java 不同；② <code>%</code> 取模的结果符号与除数相同，不是与被除数相同；③ <code>~x = -(x+1)</code>，而不是简单的按位取反加 1（那是补码视角）。</p></blockquote><h3 id="4-2-逻辑运算符的短路特性"><a href="#4-2-逻辑运算符的短路特性" class="headerlink" title="4.2 逻辑运算符的短路特性"></a>4.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></pre></td><td class="code"><pre><span class="line"><span class="comment"># and：若左操作数为 False，直接返回左操作数；否则返回右操作数</span></span><br><span class="line">result = <span class="number">0</span> <span class="keyword">and</span> <span class="number">999</span>      <span class="comment"># 0</span></span><br><span class="line">result = <span class="number">5</span> <span class="keyword">and</span> <span class="number">999</span>      <span class="comment"># 999</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># or：若左操作数为 True，直接返回左操作数；否则返回右操作数</span></span><br><span class="line">result = <span class="number">5</span> <span class="keyword">or</span> <span class="number">999</span>       <span class="comment"># 5</span></span><br><span class="line">result = <span class="number">0</span> <span class="keyword">or</span> <span class="number">999</span>       <span class="comment"># 999</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 实战：默认值设置</span></span><br><span class="line">greeting = user_input <span class="keyword">or</span> <span class="string">&quot;Hello&quot;</span>   <span class="comment"># 若 user_input 为假值（空串/None），使用默认值</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 三目运算符（条件表达式）</span></span><br><span class="line">status = <span class="string">&quot;adult&quot;</span> <span class="keyword">if</span> age &gt;= <span class="number">18</span> <span class="keyword">else</span> <span class="string">&quot;minor&quot;</span></span><br></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><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><span class="line"><span class="comment"># 1. 安全访问属性（避免 AttributeError）</span></span><br><span class="line"><span class="comment"># 假设 obj 可能为 None</span></span><br><span class="line">result = obj <span class="keyword">and</span> obj.method()   <span class="comment"># 如果 obj 为 None，短路返回 None</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 2. 条件执行函数</span></span><br><span class="line">debug <span class="keyword">and</span> <span class="built_in">print</span>(<span class="string">&quot;调试信息&quot;</span>)     <span class="comment"># debug 为 False 时不执行 print</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 3. 链式默认值</span></span><br><span class="line">config = env_value <span class="keyword">or</span> default_value <span class="keyword">or</span> <span class="string">&quot;fallback&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 4. 安全的列表访问</span></span><br><span class="line"><span class="comment"># 避免 IndexError</span></span><br><span class="line">item = <span class="built_in">len</span>(lst) &gt; <span class="number">0</span> <span class="keyword">and</span> lst[<span class="number">0</span>] <span class="keyword">or</span> <span class="literal">None</span></span><br><span class="line"><span class="comment"># 更 Pythonic 的写法：</span></span><br><span class="line">item = lst[<span class="number">0</span>] <span class="keyword">if</span> lst <span class="keyword">else</span> <span class="literal">None</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 5. 数据库查询中的常见模式</span></span><br><span class="line">user = db.get_user(<span class="built_in">id</span>) <span class="keyword">or</span> db.create_guest_user()</span><br></pre></td></tr></table></figure><p><strong>and&#x2F;or 实现三元运算</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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 传统 if-else</span></span><br><span class="line">result = value_if_true <span class="keyword">if</span> condition <span class="keyword">else</span> value_if_false</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用 and/or 模拟（不推荐，了解即可）</span></span><br><span class="line">result = condition <span class="keyword">and</span> value_if_true <span class="keyword">or</span> value_if_false</span><br><span class="line"><span class="comment"># ⚠️ 陷阱：当 value_if_true 为假值（0, &#x27;&#x27;, None）时，结果错误！</span></span><br><span class="line"><span class="built_in">print</span>(<span class="literal">True</span> <span class="keyword">and</span> <span class="number">0</span> <span class="keyword">or</span> <span class="string">&quot;fallback&quot;</span>)   <span class="comment"># &quot;fallback&quot; —— 不是 0！</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 正确的三元运算始终用 if-else 语法</span></span><br><span class="line">result = <span class="number">0</span> <span class="keyword">if</span> <span class="literal">True</span> <span class="keyword">else</span> <span class="string">&quot;fallback&quot;</span>  <span class="comment"># 0 —— 正确</span></span><br></pre></td></tr></table></figure><blockquote><p><strong>面试真题</strong>：Python 中 <code>and</code> 和 <code>or</code> 的返回值是什么？如何利用短路求值实现默认值？</p><p><strong>易错点</strong>：① <code>and</code> 和 <code>or</code> 返回的不是 <code>True/False</code>，而是最后一个求值的操作数；② <code>condition and value or fallback</code> 在 <code>value</code> 为假值时会出错，不要用这种方式替代三元运算符；③ <code>not</code> 返回的是 <code>True/False</code>，与 <code>and/or</code> 行为不同。</p></blockquote><h3 id="4-3-海象运算符-（3-8-）"><a href="#4-3-海象运算符-（3-8-）" class="headerlink" title="4.3 海象运算符 :=（3.8+）"></a>4.3 海象运算符 <code>:=</code>（3.8+）</h3><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 传统写法</span></span><br><span class="line"><span class="keyword">match</span> = pattern.search(data)</span><br><span class="line"><span class="keyword">if</span> <span class="keyword">match</span>:</span><br><span class="line">    <span class="built_in">print</span>(<span class="keyword">match</span>.group(<span class="number">1</span>))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 海象运算符</span></span><br><span class="line"><span class="keyword">if</span> <span class="keyword">match</span> := pattern.search(data):</span><br><span class="line">    <span class="built_in">print</span>(<span class="keyword">match</span>.group(<span class="number">1</span>))</span><br><span class="line"></span><br><span class="line"><span class="comment"># while 循环中读取直到空</span></span><br><span class="line"><span class="keyword">while</span> (line := file.readline().strip()):</span><br><span class="line">    process(line)</span><br></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><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><span class="line"><span class="comment"># 1. 列表推导式中避免重复计算</span></span><br><span class="line"><span class="comment"># 不使用海象运算符：计算两次</span></span><br><span class="line">results = [expensive_computation(x) <span class="keyword">for</span> x <span class="keyword">in</span> data <span class="keyword">if</span> expensive_computation(x) &gt; threshold]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用海象运算符：只计算一次</span></span><br><span class="line">results = [y <span class="keyword">for</span> x <span class="keyword">in</span> data <span class="keyword">if</span> (y := expensive_computation(x)) &gt; threshold]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 2. while 循环中的缓冲区读取</span></span><br><span class="line"><span class="keyword">while</span> (chunk := response.read(<span class="number">8192</span>)):</span><br><span class="line">    process(chunk)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 3. 条件语句中的复杂计算</span></span><br><span class="line"><span class="keyword">if</span> (n := <span class="built_in">len</span>(data)) &gt; <span class="number">10</span>:</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;数据量过大: <span class="subst">&#123;n&#125;</span> 条记录&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 4. 三元表达式中复用计算结果</span></span><br><span class="line">discount = <span class="number">0.1</span> <span class="keyword">if</span> (total := <span class="built_in">sum</span>(prices)) &gt; <span class="number">1000</span> <span class="keyword">else</span> <span class="number">0</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;总价: <span class="subst">&#123;total&#125;</span>, 折扣: <span class="subst">&#123;discount&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 5. any/all 中的值捕获</span></span><br><span class="line"><span class="keyword">if</span> <span class="built_in">any</span>((invalid := <span class="keyword">not</span> is_valid(item)) <span class="keyword">for</span> item <span class="keyword">in</span> items):</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;发现无效项: <span class="subst">&#123;invalid&#125;</span>&quot;</span>)  <span class="comment"># 注意：这里捕获的是最后一个值</span></span><br></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><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><span class="line"><span class="comment"># ❌ 不能用于属性赋值</span></span><br><span class="line"><span class="comment"># obj.attr := value  # SyntaxError</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ❌ 不能用于简单赋值（没有意义）</span></span><br><span class="line"><span class="comment"># x := 10  # SyntaxError: cannot use assignment expressions with simple name</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ✅ 必须在表达式中使用</span></span><br><span class="line">(x := <span class="number">10</span>)  <span class="comment"># 加括号可以，但不推荐这样写</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ❌ 不能用于 f-string 中的简单表达式（3.8-3.11）</span></span><br><span class="line"><span class="comment"># Python 3.12+ 可以在 f-string 中使用</span></span><br><span class="line"><span class="comment"># print(f&quot;&#123;x := 10&#125;&quot;)  # Python 3.12+ 才支持</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ⚠️ 可读性警告：不要过度使用海象运算符</span></span><br><span class="line"><span class="comment"># 简单场景用传统写法更清晰</span></span><br></pre></td></tr></table></figure><blockquote><p><strong>面试真题</strong>：海象运算符 <code>:=</code> 有什么用？在列表推导式中如何避免重复计算？</p><p><strong>易错点</strong>：① 海象运算符不能用于属性赋值（<code>obj.attr := val</code> 是语法错误）；② 过度使用会降低代码可读性；③ 在 <code>any()</code>&#x2F;<code>all()</code> 中使用时，捕获的值是生成器中最后一个被计算的值，可能不是你期望的。</p></blockquote><h3 id="4-4-运算符优先级与运算符重载"><a href="#4-4-运算符优先级与运算符重载" class="headerlink" title="4.4 运算符优先级与运算符重载"></a>4.4 运算符优先级与运算符重载</h3><p><strong>运算符优先级（常用优先级，从高到低）</strong>：</p><table><thead><tr><th>优先级</th><th>运算符</th></tr></thead><tbody><tr><td>最高</td><td><code>()</code>（括号）</td></tr><tr><td>高</td><td><code>**</code></td></tr><tr><td>中</td><td><code>* / // % @</code></td></tr><tr><td>中</td><td><code>+ -</code></td></tr><tr><td>中</td><td><code>== != &gt; &lt; &gt;= &lt;=</code></td></tr><tr><td>低</td><td><code>not</code></td></tr><tr><td>低</td><td><code>and</code></td></tr><tr><td>最低</td><td><code>or</code></td></tr></tbody></table><p><strong>工程建议</strong>：不要依赖记忆优先级，复杂表达式用括号明确意图。</p><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><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><span class="line"><span class="keyword">class</span> <span class="title class_">Vector</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;二维向量，演示运算符重载&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, x, y</span>):</span><br><span class="line">        <span class="variable language_">self</span>.x = x</span><br><span class="line">        <span class="variable language_">self</span>.y = y</span><br><span class="line"></span><br><span class="line">    <span class="comment"># + 运算符</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__add__</span>(<span class="params">self, other</span>):</span><br><span class="line">        <span class="keyword">return</span> Vector(<span class="variable language_">self</span>.x + other.x, <span class="variable language_">self</span>.y + other.y)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># - 运算符</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__sub__</span>(<span class="params">self, other</span>):</span><br><span class="line">        <span class="keyword">return</span> Vector(<span class="variable language_">self</span>.x - other.x, <span class="variable language_">self</span>.y - other.y)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># * 运算符（标量乘法）</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__mul__</span>(<span class="params">self, scalar</span>):</span><br><span class="line">        <span class="keyword">if</span> <span class="built_in">isinstance</span>(scalar, (<span class="built_in">int</span>, <span class="built_in">float</span>)):</span><br><span class="line">            <span class="keyword">return</span> Vector(<span class="variable language_">self</span>.x * scalar, <span class="variable language_">self</span>.y * scalar)</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">NotImplemented</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># == 运算符</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__eq__</span>(<span class="params">self, other</span>):</span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> <span class="built_in">isinstance</span>(other, Vector):</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">NotImplemented</span></span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>.x == other.x <span class="keyword">and</span> <span class="variable language_">self</span>.y == other.y</span><br><span class="line"></span><br><span class="line">    <span class="comment"># &lt; 运算符（按模长比较）</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__lt__</span>(<span class="params">self, other</span>):</span><br><span class="line">        <span class="keyword">return</span> (<span class="variable language_">self</span>.x**<span class="number">2</span> + <span class="variable language_">self</span>.y**<span class="number">2</span>) &lt; (other.x**<span class="number">2</span> + other.y**<span class="number">2</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># str() 和 repr()</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__repr__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="string">f&quot;Vector(<span class="subst">&#123;self.x&#125;</span>, <span class="subst">&#123;self.y&#125;</span>)&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># len() 支持</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__abs__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> (<span class="variable language_">self</span>.x**<span class="number">2</span> + <span class="variable language_">self</span>.y**<span class="number">2</span>) ** <span class="number">0.5</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># bool() 支持（模长为0则为False）</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__bool__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>.x != <span class="number">0</span> <span class="keyword">or</span> <span class="variable language_">self</span>.y != <span class="number">0</span></span><br><span class="line"></span><br><span class="line">v1 = Vector(<span class="number">3</span>, <span class="number">4</span>)</span><br><span class="line">v2 = Vector(<span class="number">1</span>, <span class="number">2</span>)</span><br><span class="line"><span class="built_in">print</span>(v1 + v2)       <span class="comment"># Vector(4, 6)</span></span><br><span class="line"><span class="built_in">print</span>(v1 * <span class="number">3</span>)        <span class="comment"># Vector(9, 12)</span></span><br><span class="line"><span class="built_in">print</span>(v1 == v2)      <span class="comment"># False</span></span><br><span class="line"><span class="built_in">print</span>(v1 &gt; v2)       <span class="comment"># True（模长 5 &gt; 2.24）</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">abs</span>(v1))        <span class="comment"># 5.0</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">bool</span>(v1))       <span class="comment"># True</span></span><br></pre></td></tr></table></figure><p><strong>常用运算符与对应魔术方法速查</strong>：</p><table><thead><tr><th>运算符</th><th>魔术方法</th><th>说明</th></tr></thead><tbody><tr><td><code>+</code></td><td><code>__add__</code>, <code>__radd__</code></td><td>加法 &#x2F; 右侧加法</td></tr><tr><td><code>-</code></td><td><code>__sub__</code>, <code>__rsub__</code></td><td>减法</td></tr><tr><td><code>*</code></td><td><code>__mul__</code>, <code>__rmul__</code></td><td>乘法</td></tr><tr><td><code>//</code></td><td><code>__floordiv__</code></td><td>整除</td></tr><tr><td><code>**</code></td><td><code>__pow__</code></td><td>幂运算</td></tr><tr><td><code>==</code></td><td><code>__eq__</code></td><td>等于</td></tr><tr><td><code>&lt;</code></td><td><code>__lt__</code></td><td>小于</td></tr><tr><td><code>&lt;=</code></td><td><code>__le__</code></td><td>小于等于</td></tr><tr><td><code>in</code></td><td><code>__contains__</code></td><td>成员检测</td></tr><tr><td><code>len()</code></td><td><code>__len__</code></td><td>长度</td></tr><tr><td><code>str()</code></td><td><code>__str__</code></td><td>字符串表示</td></tr><tr><td><code>repr()</code></td><td><code>__repr__</code></td><td>开发者表示</td></tr><tr><td><code>bool()</code></td><td><code>__bool__</code></td><td>布尔转换</td></tr><tr><td><code>[]</code></td><td><code>__getitem__</code>, <code>__setitem__</code></td><td>索引访问&#x2F;赋值</td></tr><tr><td><code>()</code></td><td><code>__call__</code></td><td>可调用对象</td></tr></tbody></table><blockquote><p><strong>面试真题</strong>：Python 如何实现运算符重载？<code>__eq__</code> 和 <code>__hash__</code> 的关系是什么？</p><p><strong>易错点</strong>：① 运算符重载方法返回 <code>NotImplemented</code>（不是 <code>NotImplementedError</code>！）表示不支持该操作，Python 会尝试反向方法；② 定义了 <code>__eq__</code> 后，<code>__hash__</code> 会被设为 <code>None</code>，实例变为不可哈希，需要同时定义。</p></blockquote><hr><h2 id="5-流程控制"><a href="#5-流程控制" class="headerlink" title="5. 流程控制"></a>5. 流程控制</h2><h3 id="5-1-条件语句"><a href="#5-1-条件语句" class="headerlink" title="5.1 条件语句"></a>5.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></pre></td><td class="code"><pre><span class="line">score = <span class="number">85</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> score &gt;= <span class="number">90</span>:</span><br><span class="line">    grade = <span class="string">&quot;A&quot;</span></span><br><span class="line"><span class="keyword">elif</span> score &gt;= <span class="number">80</span>:</span><br><span class="line">    grade = <span class="string">&quot;B&quot;</span></span><br><span class="line"><span class="keyword">elif</span> score &gt;= <span class="number">60</span>:</span><br><span class="line">    grade = <span class="string">&quot;C&quot;</span></span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line">    grade = <span class="string">&quot;D&quot;</span></span><br></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><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><span class="line"><span class="comment"># 三元表达式（条件表达式）</span></span><br><span class="line">grade = <span class="string">&quot;A&quot;</span> <span class="keyword">if</span> score &gt;= <span class="number">90</span> <span class="keyword">else</span> <span class="string">&quot;B&quot;</span> <span class="keyword">if</span> score &gt;= <span class="number">80</span> <span class="keyword">else</span> <span class="string">&quot;C&quot;</span> <span class="keyword">if</span> score &gt;= <span class="number">60</span> <span class="keyword">else</span> <span class="string">&quot;D&quot;</span></span><br><span class="line"><span class="comment"># ⚠️ 虽然语法正确，但嵌套三元表达式极难阅读！</span></span><br><span class="line"><span class="comment"># 推荐拆分为 if-elif-else 或使用字典映射</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 更优雅的写法：字典映射替代 if-elif</span></span><br><span class="line">grade_map = &#123;</span><br><span class="line">    <span class="string">&quot;A&quot;</span>: <span class="keyword">lambda</span> s: s &gt;= <span class="number">90</span>,</span><br><span class="line">    <span class="string">&quot;B&quot;</span>: <span class="keyword">lambda</span> s: s &gt;= <span class="number">80</span>,</span><br><span class="line">    <span class="string">&quot;C&quot;</span>: <span class="keyword">lambda</span> s: s &gt;= <span class="number">60</span>,</span><br><span class="line">&#125;</span><br><span class="line">grade = <span class="built_in">next</span>((g <span class="keyword">for</span> g, cond <span class="keyword">in</span> grade_map.items() <span class="keyword">if</span> cond(score)), <span class="string">&quot;D&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 简化版：利用 bisect（适合连续区间）</span></span><br><span class="line"><span class="keyword">import</span> bisect</span><br><span class="line">breakpoints = [<span class="number">60</span>, <span class="number">80</span>, <span class="number">90</span>]</span><br><span class="line">grades = [<span class="string">&quot;D&quot;</span>, <span class="string">&quot;C&quot;</span>, <span class="string">&quot;B&quot;</span>, <span class="string">&quot;A&quot;</span>]</span><br><span class="line">grade = grades[bisect.bisect_right(breakpoints, score)]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 函数分派模式（替代冗长的 if-elif）</span></span><br><span class="line">handlers = &#123;</span><br><span class="line">    <span class="string">&quot;create&quot;</span>: handle_create,</span><br><span class="line">    <span class="string">&quot;update&quot;</span>: handle_update,</span><br><span class="line">    <span class="string">&quot;delete&quot;</span>: handle_delete,</span><br><span class="line">&#125;</span><br><span class="line">handler = handlers.get(action, handle_default)</span><br><span class="line">handler(data)</span><br></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><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><span class="line"><span class="comment"># Python 支持链式比较（其他语言不支持！）</span></span><br><span class="line">x = <span class="number">5</span></span><br><span class="line"><span class="built_in">print</span>(<span class="number">1</span> &lt; x &lt; <span class="number">10</span>)      <span class="comment"># True</span></span><br><span class="line"><span class="built_in">print</span>(<span class="number">1</span> &lt; x &gt; <span class="number">0</span>)       <span class="comment"># True</span></span><br><span class="line"><span class="built_in">print</span>(<span class="number">0</span> &lt; x &lt; <span class="number">10</span> &lt; <span class="number">100</span>) <span class="comment"># True</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 等价展开</span></span><br><span class="line"><span class="comment"># 1 &lt; x &lt; 10 等价于 (1 &lt; x) and (x &lt; 10)</span></span><br><span class="line"><span class="comment"># 但链式写法中 x 只计算一次！</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 实用场景</span></span><br><span class="line">age = <span class="number">25</span></span><br><span class="line"><span class="keyword">if</span> <span class="number">18</span> &lt;= age &lt;= <span class="number">65</span>:</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;工作年龄&quot;</span>)</span><br></pre></td></tr></table></figure><p><strong>Python 没有 switch 语句</strong>，但 3.10+ 提供了更强大的 <code>match / case</code>。</p><blockquote><p><strong>面试真题</strong>：Python 中如何替代 switch&#x2F;case？三元表达式嵌套有什么问题？</p><p><strong>易错点</strong>：① 嵌套三元表达式可读性极差，不要在生产代码中使用；② 字典映射模式要求所有分支返回相同类型；③ 链式比较 <code>a &lt; b &gt; c</code> 不是 <code>a &lt; b and b &gt; c and a &gt; c</code>，而是 <code>a &lt; b and b &gt; c</code>。</p></blockquote><h3 id="5-2-循环语句"><a href="#5-2-循环语句" class="headerlink" title="5.2 循环语句"></a>5.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><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><span class="line"><span class="comment"># for 循环 —— 遍历可迭代对象</span></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">5</span>):           <span class="comment"># 0, 1, 2, 3, 4</span></span><br><span class="line">    <span class="built_in">print</span>(i)</span><br><span class="line"></span><br><span class="line"><span class="comment"># enumerate —— 同时获取索引和值</span></span><br><span class="line"><span class="keyword">for</span> idx, value <span class="keyword">in</span> <span class="built_in">enumerate</span>([<span class="string">&quot;a&quot;</span>, <span class="string">&quot;b&quot;</span>, <span class="string">&quot;c&quot;</span>], start=<span class="number">1</span>):</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;<span class="subst">&#123;idx&#125;</span>: <span class="subst">&#123;value&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># zip —— 并行遍历多个序列</span></span><br><span class="line">names = [<span class="string">&quot;Alice&quot;</span>, <span class="string">&quot;Bob&quot;</span>]</span><br><span class="line">ages = [<span class="number">25</span>, <span class="number">30</span>]</span><br><span class="line"><span class="keyword">for</span> name, age <span class="keyword">in</span> <span class="built_in">zip</span>(names, ages):</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;<span class="subst">&#123;name&#125;</span> is <span class="subst">&#123;age&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># while 循环</span></span><br><span class="line">n = <span class="number">5</span></span><br><span class="line"><span class="keyword">while</span> n &gt; <span class="number">0</span>:</span><br><span class="line">    <span class="built_in">print</span>(n)</span><br><span class="line">    n -= <span class="number">1</span></span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;循环正常结束（未被 break）&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># break / continue / else 子句</span></span><br><span class="line"><span class="keyword">for</span> num <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">10</span>):</span><br><span class="line">    <span class="keyword">if</span> num == <span class="number">3</span>:</span><br><span class="line">        <span class="keyword">continue</span>      <span class="comment"># 跳过当前迭代</span></span><br><span class="line">    <span class="keyword">if</span> num == <span class="number">7</span>:</span><br><span class="line">        <span class="keyword">break</span>         <span class="comment"># 终止循环</span></span><br><span class="line">    <span class="built_in">print</span>(num)</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;循环完整执行&quot;</span>)   <span class="comment"># 若被 break，else 不会执行</span></span><br></pre></td></tr></table></figure><blockquote><p><strong>for-else 的实用场景</strong>：在搜索场景下避免引入标志变量。</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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 搜索列表中是否存在素数（不用 flag 变量）</span></span><br><span class="line"><span class="keyword">for</span> n <span class="keyword">in</span> [<span class="number">4</span>, <span class="number">6</span>, <span class="number">8</span>, <span class="number">9</span>, <span class="number">11</span>, <span class="number">12</span>]:</span><br><span class="line">    <span class="keyword">if</span> is_prime(n):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;找到素数: <span class="subst">&#123;n&#125;</span>&quot;</span>)</span><br><span class="line">        <span class="keyword">break</span></span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;没有找到素数&quot;</span>)   <span class="comment"># 循环未被 break 时才执行</span></span><br></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><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><span class="line"><span class="comment"># 可迭代对象（Iterable）：实现了 __iter__() 方法</span></span><br><span class="line"><span class="comment"># 迭代器（Iterator）：同时实现了 __iter__() 和 __next__() 方法</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 可迭代对象 ≠ 迭代器</span></span><br><span class="line">lst = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]          <span class="comment"># 可迭代对象</span></span><br><span class="line">it = <span class="built_in">iter</span>(lst)            <span class="comment"># 调用 __iter__() 返回迭代器</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">type</span>(it))           <span class="comment"># &lt;class &#x27;list_iterator&#x27;&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">next</span>(it))           <span class="comment"># 1 —— 调用 __next__()</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">next</span>(it))           <span class="comment"># 2</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">next</span>(it))           <span class="comment"># 3</span></span><br><span class="line"><span class="comment"># print(next(it))         # StopIteration —— 迭代耗尽</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 迭代器是一次性的</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">list</span>(it))           <span class="comment"># [] —— 迭代器已耗尽！</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 字符串、列表、元组、字典、集合都是可迭代对象，但不是迭代器</span></span><br><span class="line"><span class="comment"># 文件对象、map、filter、zip 返回的是迭代器</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 判断是否可迭代 / 是否为迭代器</span></span><br><span class="line"><span class="keyword">from</span> collections.abc <span class="keyword">import</span> Iterable, Iterator</span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">isinstance</span>(lst, Iterable))   <span class="comment"># True</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">isinstance</span>(lst, Iterator))   <span class="comment"># False</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">isinstance</span>(it, Iterator))    <span class="comment"># True</span></span><br></pre></td></tr></table></figure><p><strong>itertools 常用函数</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><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><span class="line"><span class="keyword">import</span> itertools</span><br><span class="line"></span><br><span class="line"><span class="comment"># count —— 无限计数器</span></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> itertools.count(start=<span class="number">1</span>, step=<span class="number">2</span>):</span><br><span class="line">    <span class="keyword">if</span> i &gt; <span class="number">10</span>:</span><br><span class="line">        <span class="keyword">break</span></span><br><span class="line">    <span class="built_in">print</span>(i)  <span class="comment"># 1, 3, 5, 7, 9</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># cycle —— 无限循环</span></span><br><span class="line"><span class="comment"># for item in itertools.cycle([1, 2, 3]):</span></span><br><span class="line"><span class="comment">#     ...  # 1, 2, 3, 1, 2, 3, ...（无限循环）</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># repeat —— 重复</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">list</span>(itertools.repeat(<span class="string">&quot;hello&quot;</span>, <span class="number">3</span>)))  <span class="comment"># [&#x27;hello&#x27;, &#x27;hello&#x27;, &#x27;hello&#x27;]</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># chain —— 链接多个可迭代对象</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">list</span>(itertools.chain([<span class="number">1</span>, <span class="number">2</span>], [<span class="number">3</span>, <span class="number">4</span>], [<span class="number">5</span>])))  <span class="comment"># [1, 2, 3, 4, 5]</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># islice —— 切片迭代器</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">list</span>(itertools.islice(<span class="built_in">range</span>(<span class="number">100</span>), <span class="number">5</span>, <span class="number">10</span>)))  <span class="comment"># [5, 6, 7, 8, 9]</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># product —— 笛卡尔积</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">list</span>(itertools.product([<span class="number">1</span>, <span class="number">2</span>], [<span class="string">&#x27;a&#x27;</span>, <span class="string">&#x27;b&#x27;</span>])))</span><br><span class="line"><span class="comment"># [(1, &#x27;a&#x27;), (1, &#x27;b&#x27;), (2, &#x27;a&#x27;), (2, &#x27;b&#x27;)]</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># permutations —— 排列</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">list</span>(itertools.permutations([<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>], <span class="number">2</span>)))</span><br><span class="line"><span class="comment"># [(1, 2), (1, 3), (2, 1), (2, 3), (3, 1), (3, 2)]</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># combinations —— 组合</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">list</span>(itertools.combinations([<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>], <span class="number">2</span>)))</span><br><span class="line"><span class="comment"># [(1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)]</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># groupby —— 分组（需要先排序！）</span></span><br><span class="line">data = [(<span class="string">&quot;A&quot;</span>, <span class="number">1</span>), (<span class="string">&quot;A&quot;</span>, <span class="number">2</span>), (<span class="string">&quot;B&quot;</span>, <span class="number">3</span>), (<span class="string">&quot;B&quot;</span>, <span class="number">4</span>)]</span><br><span class="line"><span class="keyword">for</span> key, group <span class="keyword">in</span> itertools.groupby(data, key=<span class="keyword">lambda</span> x: x[<span class="number">0</span>]):</span><br><span class="line">    <span class="built_in">print</span>(key, <span class="built_in">list</span>(group))</span><br><span class="line"><span class="comment"># A [(&#x27;A&#x27;, 1), (&#x27;A&#x27;, 2)]</span></span><br><span class="line"><span class="comment"># B [(&#x27;B&#x27;, 3), (&#x27;B&#x27;, 4)]</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># accumulate —— 累积</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">list</span>(itertools.accumulate([<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>])))  <span class="comment"># [1, 3, 6, 10]</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">list</span>(itertools.accumulate([<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>], initial=<span class="number">0</span>)))  <span class="comment"># [0, 1, 3, 6, 10]</span></span><br></pre></td></tr></table></figure><p><strong>生成器表达式 vs 列表推导式性能对比</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><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><span class="line"><span class="keyword">import</span> sys</span><br><span class="line"></span><br><span class="line"><span class="comment"># 列表推导式：一次性创建完整列表</span></span><br><span class="line">list_comp = [x**<span class="number">2</span> <span class="keyword">for</span> x <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">1000000</span>)]</span><br><span class="line"><span class="built_in">print</span>(sys.getsizeof(list_comp))  <span class="comment"># 约 8.4MB</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 生成器表达式：惰性求值，只存储当前元素</span></span><br><span class="line">gen_expr = (x**<span class="number">2</span> <span class="keyword">for</span> x <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">1000000</span>))</span><br><span class="line"><span class="built_in">print</span>(sys.getsizeof(gen_expr))   <span class="comment"># 约 200 字节！</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 性能对比</span></span><br><span class="line"><span class="keyword">import</span> timeit</span><br><span class="line"></span><br><span class="line"><span class="comment"># 列表推导式：需要先创建完整列表，再求和</span></span><br><span class="line">list_time = timeit.timeit(</span><br><span class="line">    <span class="string">&quot;sum([x**2 for x in range(10000)])&quot;</span>,</span><br><span class="line">    number=<span class="number">1000</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 生成器表达式：惰性求值，逐个产生</span></span><br><span class="line">gen_time = timeit.timeit(</span><br><span class="line">    <span class="string">&quot;sum(x**2 for x in range(10000))&quot;</span>,</span><br><span class="line">    number=<span class="number">1000</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 列表推导式通常稍快（避免生成器开销），但占用更多内存</span></span><br><span class="line"><span class="comment"># 内存敏感场景（大数据处理）优先用生成器表达式</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 选择原则：</span></span><br><span class="line"><span class="comment"># - 需要多次遍历 / 索引访问 → 列表推导式</span></span><br><span class="line"><span class="comment"># - 只遍历一次 / 数据量大 → 生成器表达式</span></span><br><span class="line"><span class="comment"># - 需要作为函数参数立即消费 → 生成器表达式</span></span><br></pre></td></tr></table></figure><blockquote><p><strong>面试真题</strong>：可迭代对象和迭代器有什么区别？生成器表达式和列表推导式在性能上有什么差异？</p><p><strong>易错点</strong>：① 迭代器是一次性的，遍历完后不能重用；② <code>zip</code> 在 Python 3 中返回迭代器，遍历一次后为空；③ 生成器表达式更省内存但不一定更快（生成器有函数调用开销）；④ <code>itertools.groupby</code> 要求输入已按分组键排序，否则会创建多个分组。</p></blockquote><p><img src="https://trae-api-cn.mchost.guru/api/ide/v1/text_to_image?prompt=Python%20control%20flow%20diagram%20showing%20if-elif-else%20for-else%20while-break-continue%20flowchart,%20clean%20technical%20illustration,%20dark%20blue%20theme&image_size=landscape_16_9" alt="Python control flow diagram showing if-elif-else for-else while-break-continue flowchart, clean technical illustration, dark blue theme"></p><h3 id="5-3-模式匹配-match-case（3-10-）"><a href="#5-3-模式匹配-match-case（3-10-）" class="headerlink" title="5.3 模式匹配 match / case（3.10+）"></a>5.3 模式匹配 <code>match / case</code>（3.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></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">handle_command</span>(<span class="params">command</span>):</span><br><span class="line">    <span class="keyword">match</span> command:</span><br><span class="line">        <span class="keyword">case</span> [<span class="string">&quot;load&quot;</span>, filename]:</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">f&quot;加载文件: <span class="subst">&#123;filename&#125;</span>&quot;</span>)</span><br><span class="line">        <span class="keyword">case</span> [<span class="string">&quot;save&quot;</span>, filename, *options]:</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">f&quot;保存文件: <span class="subst">&#123;filename&#125;</span>, 选项: <span class="subst">&#123;options&#125;</span>&quot;</span>)</span><br><span class="line">        <span class="keyword">case</span> &#123;<span class="string">&quot;type&quot;</span>: <span class="string">&quot;error&quot;</span>, <span class="string">&quot;msg&quot;</span>: message&#125;:</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">f&quot;错误: <span class="subst">&#123;message&#125;</span>&quot;</span>)</span><br><span class="line">        <span class="keyword">case</span> _:</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">&quot;未知命令&quot;</span>)</span><br><span class="line"></span><br><span class="line">handle_command([<span class="string">&quot;load&quot;</span>, <span class="string">&quot;data.txt&quot;</span>])</span><br><span class="line">handle_command(&#123;<span class="string">&quot;type&quot;</span>: <span class="string">&quot;error&quot;</span>, <span class="string">&quot;msg&quot;</span>: <span class="string">&quot;连接超时&quot;</span>&#125;)</span><br></pre></td></tr></table></figure><p><code>match</code> 支持：</p><ul><li>序列解构（列表、元组）</li><li>映射解构（字典）</li><li>捕获变量、通配符 <code>_</code></li><li>守卫子句（guard）：<code>case [x, y] if x == y:</code></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><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><span class="line"><span class="comment"># 1. 类型匹配</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">describe</span>(<span class="params">value</span>):</span><br><span class="line">    <span class="keyword">match</span> value:</span><br><span class="line">        <span class="keyword">case</span> <span class="built_in">int</span>(n) <span class="keyword">if</span> n &gt; <span class="number">0</span>:</span><br><span class="line">            <span class="keyword">return</span> <span class="string">f&quot;正整数: <span class="subst">&#123;n&#125;</span>&quot;</span></span><br><span class="line">        <span class="keyword">case</span> <span class="built_in">int</span>(n):</span><br><span class="line">            <span class="keyword">return</span> <span class="string">f&quot;非正整数: <span class="subst">&#123;n&#125;</span>&quot;</span></span><br><span class="line">        <span class="keyword">case</span> <span class="built_in">float</span>(f):</span><br><span class="line">            <span class="keyword">return</span> <span class="string">f&quot;浮点数: <span class="subst">&#123;f&#125;</span>&quot;</span></span><br><span class="line">        <span class="keyword">case</span> <span class="built_in">str</span>(s):</span><br><span class="line">            <span class="keyword">return</span> <span class="string">f&quot;字符串: <span class="subst">&#123;s&#125;</span>&quot;</span></span><br><span class="line">        <span class="keyword">case</span> [x, y]:</span><br><span class="line">            <span class="keyword">return</span> <span class="string">f&quot;二元组: (<span class="subst">&#123;x&#125;</span>, <span class="subst">&#123;y&#125;</span>)&quot;</span></span><br><span class="line">        <span class="keyword">case</span> _:</span><br><span class="line">            <span class="keyword">return</span> <span class="string">f&quot;其他类型: <span class="subst">&#123;<span class="built_in">type</span>(value)&#125;</span>&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 2. OR 模式（多模式匹配）</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">is_vowel</span>(<span class="params">char</span>):</span><br><span class="line">    <span class="keyword">match</span> char.lower():</span><br><span class="line">        <span class="keyword">case</span> <span class="string">&quot;a&quot;</span> | <span class="string">&quot;e&quot;</span> | <span class="string">&quot;i&quot;</span> | <span class="string">&quot;o&quot;</span> | <span class="string">&quot;u&quot;</span>:</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">True</span></span><br><span class="line">        <span class="keyword">case</span> _:</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">False</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 3. 类匹配（需要 __match_args__ 或使用关键字参数）</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Point</span>:</span><br><span class="line">    __match_args__ = (<span class="string">&quot;x&quot;</span>, <span class="string">&quot;y&quot;</span>)</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, x, y</span>):</span><br><span class="line">        <span class="variable language_">self</span>.x = x</span><br><span class="line">        <span class="variable language_">self</span>.y = y</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">where_is</span>(<span class="params">point</span>):</span><br><span class="line">    <span class="keyword">match</span> point:</span><br><span class="line">        <span class="keyword">case</span> Point(x=<span class="number">0</span>, y=<span class="number">0</span>):</span><br><span class="line">            <span class="keyword">return</span> <span class="string">&quot;原点&quot;</span></span><br><span class="line">        <span class="keyword">case</span> Point(x=<span class="number">0</span>):</span><br><span class="line">            <span class="keyword">return</span> <span class="string">&quot;Y 轴上&quot;</span></span><br><span class="line">        <span class="keyword">case</span> Point(y=<span class="number">0</span>):</span><br><span class="line">            <span class="keyword">return</span> <span class="string">&quot;X 轴上&quot;</span></span><br><span class="line">        <span class="keyword">case</span> Point():</span><br><span class="line">            <span class="keyword">return</span> <span class="string">&quot;其他位置&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 4. 嵌套解构</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">process_config</span>(<span class="params">config</span>):</span><br><span class="line">    <span class="keyword">match</span> config:</span><br><span class="line">        <span class="keyword">case</span> &#123;<span class="string">&quot;database&quot;</span>: &#123;<span class="string">&quot;host&quot;</span>: host, <span class="string">&quot;port&quot;</span>: port&#125;, <span class="string">&quot;debug&quot;</span>: <span class="literal">True</span>&#125;:</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">f&quot;调试模式连接: <span class="subst">&#123;host&#125;</span>:<span class="subst">&#123;port&#125;</span>&quot;</span>)</span><br><span class="line">        <span class="keyword">case</span> &#123;<span class="string">&quot;database&quot;</span>: &#123;<span class="string">&quot;host&quot;</span>: host, <span class="string">&quot;port&quot;</span>: port&#125;&#125;:</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">f&quot;连接: <span class="subst">&#123;host&#125;</span>:<span class="subst">&#123;port&#125;</span>&quot;</span>)</span><br><span class="line">        <span class="keyword">case</span> _:</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">&quot;配置格式不正确&quot;</span>)</span><br></pre></td></tr></table></figure><p><strong>match&#x2F;case 与 if-elif 的性能对比</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><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><span class="line"><span class="comment"># match/case 的优势在于模式解构，而非性能</span></span><br><span class="line"><span class="comment"># 简单的条件判断，if-elif 性能相当甚至略优</span></span><br><span class="line"><span class="comment"># 复杂的模式解构，match/case 代码更清晰</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 性能测试</span></span><br><span class="line"><span class="keyword">import</span> timeit</span><br><span class="line"></span><br><span class="line"><span class="comment"># if-elif 版本</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">classify_if</span>(<span class="params">x</span>):</span><br><span class="line">    <span class="keyword">if</span> x == <span class="number">0</span>: <span class="keyword">return</span> <span class="string">&quot;zero&quot;</span></span><br><span class="line">    <span class="keyword">elif</span> x == <span class="number">1</span>: <span class="keyword">return</span> <span class="string">&quot;one&quot;</span></span><br><span class="line">    <span class="keyword">elif</span> x == <span class="number">2</span>: <span class="keyword">return</span> <span class="string">&quot;two&quot;</span></span><br><span class="line">    <span class="keyword">else</span>: <span class="keyword">return</span> <span class="string">&quot;other&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># match 版本</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">classify_match</span>(<span class="params">x</span>):</span><br><span class="line">    <span class="keyword">match</span> x:</span><br><span class="line">        <span class="keyword">case</span> <span class="number">0</span>: <span class="keyword">return</span> <span class="string">&quot;zero&quot;</span></span><br><span class="line">        <span class="keyword">case</span> <span class="number">1</span>: <span class="keyword">return</span> <span class="string">&quot;one&quot;</span></span><br><span class="line">        <span class="keyword">case</span> <span class="number">2</span>: <span class="keyword">return</span> <span class="string">&quot;two&quot;</span></span><br><span class="line">        <span class="keyword">case</span> _: <span class="keyword">return</span> <span class="string">&quot;other&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 性能差异通常在 5% 以内，选择依据是可读性和表达力</span></span><br><span class="line"><span class="comment"># match/case 适合：复杂数据结构解构、类型匹配、OR 模式</span></span><br><span class="line"><span class="comment"># if-elif 适合：简单条件、范围判断、复杂布尔表达式</span></span><br></pre></td></tr></table></figure><p><strong>工程建议</strong>：<code>match</code> 适合处理复杂的数据结构模式，简单的条件判断仍用 <code>if/elif</code>。</p><blockquote><p><strong>面试真题</strong>：Python 3.10 的 <code>match/case</code> 和 <code>if/elif</code> 有什么区别？match 支持哪些模式？</p><p><strong>易错点</strong>：① <code>case _:</code> 中的 <code>_</code> 是通配符，不是变量名，不会绑定值；② <code>case [x, y]</code> 匹配的是长度为 2 的序列，<code>case [x, y, *rest]</code> 匹配长度 ≥ 2 的序列；③ 类匹配需要 <code>__match_args__</code> 或使用关键字参数。</p></blockquote><hr><h2 id="6-输入输出基础"><a href="#6-输入输出基础" class="headerlink" title="6. 输入输出基础"></a>6. 输入输出基础</h2><h3 id="6-1-print-深度用法"><a href="#6-1-print-深度用法" class="headerlink" title="6.1 print() 深度用法"></a>6.1 <code>print()</code> 深度用法</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><span class="line"><span class="comment"># 自定义分隔符与结尾</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;2024&quot;</span>, <span class="string">&quot;07&quot;</span>, <span class="string">&quot;20&quot;</span>, sep=<span class="string">&quot;-&quot;</span>)        <span class="comment"># 2024-07-20</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;Loading&quot;</span>, end=<span class="string">&quot;&quot;</span>)                    <span class="comment"># 不换行</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;.&quot;</span>, end=<span class="string">&quot;&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 输出到文件</span></span><br><span class="line"><span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">&quot;log.txt&quot;</span>, <span class="string">&quot;w&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;日志内容&quot;</span>, file=f)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 格式化输出（对齐与精度）</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;<span class="subst">&#123;<span class="string">&#x27;Product&#x27;</span>:&lt;<span class="number">15</span>&#125;</span> <span class="subst">&#123;<span class="string">&#x27;Price&#x27;</span>:&gt;<span class="number">10</span>&#125;</span>&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;<span class="subst">&#123;<span class="string">&#x27;Apple&#x27;</span>:&lt;<span class="number">15</span>&#125;</span> <span class="subst">&#123;<span class="number">5.5</span>:&gt;<span class="number">10.2</span>f&#125;</span>&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;<span class="subst">&#123;<span class="string">&#x27;Banana&#x27;</span>:&lt;<span class="number">15</span>&#125;</span> <span class="subst">&#123;<span class="number">3.0</span>:&gt;<span class="number">10.2</span>f&#125;</span>&quot;</span>)</span><br><span class="line"><span class="comment"># Product               Price</span></span><br><span class="line"><span class="comment"># Apple                  5.50</span></span><br><span class="line"><span class="comment"># Banana                 3.00</span></span><br></pre></td></tr></table></figure><p><strong>flush 参数详解</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><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># flush=False（默认）：输出到缓冲区，由系统决定何时刷新</span></span><br><span class="line"><span class="comment"># flush=True：立即刷新输出缓冲区</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 场景1：进度条（必须 flush）</span></span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">10</span>):</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;\r进度: <span class="subst">&#123;<span class="string">&#x27;█&#x27;</span> * (i+<span class="number">1</span>)&#125;</span><span class="subst">&#123;<span class="string">&#x27;░&#x27;</span> * (<span class="number">9</span>-i)&#125;</span> <span class="subst">&#123;(i+<span class="number">1</span>)*<span class="number">10</span>&#125;</span>%&quot;</span>, end=<span class="string">&quot;&quot;</span>, flush=<span class="literal">True</span>)</span><br><span class="line">    time.sleep(<span class="number">0.5</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 场景2：实时日志</span></span><br><span class="line"><span class="keyword">import</span> sys</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;开始处理...&quot;</span>, flush=<span class="literal">True</span>)</span><br><span class="line"><span class="comment"># 长时间计算...</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;完成！&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 场景3：管道输出时</span></span><br><span class="line"><span class="comment"># python script.py | grep &quot;error&quot;</span></span><br><span class="line"><span class="comment"># 需要 flush=True 确保输出及时被管道读取</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 缓冲模式</span></span><br><span class="line"><span class="comment"># - 交互终端：行缓冲（遇到换行自动刷新）</span></span><br><span class="line"><span class="comment"># - 管道/重定向：全缓冲（缓冲区满才刷新）</span></span><br><span class="line"><span class="comment"># - flush=True：无缓冲（立即刷新）</span></span><br></pre></td></tr></table></figure><p><strong>logging 模块入门</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><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><span class="line"><span class="keyword">import</span> logging</span><br><span class="line"></span><br><span class="line"><span class="comment"># 基本配置</span></span><br><span class="line">logging.basicConfig(</span><br><span class="line">    level=logging.DEBUG,</span><br><span class="line">    <span class="built_in">format</span>=<span class="string">&quot;%(asctime)s [%(levelname)s] %(name)s: %(message)s&quot;</span>,</span><br><span class="line">    datefmt=<span class="string">&quot;%Y-%m-%d %H:%M:%S&quot;</span>,</span><br><span class="line">    filename=<span class="string">&quot;app.log&quot;</span>,</span><br><span class="line">    filemode=<span class="string">&quot;a&quot;</span>,    <span class="comment"># 追加模式</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 五个日志级别（从低到高）</span></span><br><span class="line">logging.debug(<span class="string">&quot;调试信息，仅开发时可见&quot;</span>)     <span class="comment"># DEBUG</span></span><br><span class="line">logging.info(<span class="string">&quot;常规信息，如启动/关闭&quot;</span>)        <span class="comment"># INFO</span></span><br><span class="line">logging.warning(<span class="string">&quot;警告，可继续运行但需注意&quot;</span>)  <span class="comment"># WARNING</span></span><br><span class="line">logging.error(<span class="string">&quot;错误，功能受影响&quot;</span>)            <span class="comment"># ERROR</span></span><br><span class="line">logging.critical(<span class="string">&quot;严重错误，系统可能崩溃&quot;</span>)   <span class="comment"># CRITICAL</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 生产环境推荐配置</span></span><br><span class="line">logging.basicConfig(</span><br><span class="line">    level=logging.INFO,</span><br><span class="line">    <span class="built_in">format</span>=<span class="string">&quot;%(asctime)s [%(levelname)-8s] %(filename)s:%(lineno)d - %(message)s&quot;</span>,</span><br><span class="line">    handlers=[</span><br><span class="line">        logging.FileHandler(<span class="string">&quot;app.log&quot;</span>),      <span class="comment"># 输出到文件</span></span><br><span class="line">        logging.StreamHandler(),              <span class="comment"># 输出到控制台</span></span><br><span class="line">    ],</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment"># __name__ 作为 logger 名称，便于追踪</span></span><br><span class="line">logger = logging.getLogger(__name__)</span><br><span class="line">logger.info(<span class="string">&quot;模块初始化完成&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 异常日志（自动包含堆栈跟踪）</span></span><br><span class="line"><span class="keyword">try</span>:</span><br><span class="line">    risky_operation()</span><br><span class="line"><span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">    logger.exception(<span class="string">&quot;操作失败&quot;</span>)  <span class="comment"># 自动打印完整堆栈</span></span><br></pre></td></tr></table></figure><blockquote><p><strong>面试真题</strong>：Python 的 <code>print()</code> 的 <code>flush</code> 参数有什么用？生产环境中应该用 <code>print</code> 还是 <code>logging</code>？</p><p><strong>易错点</strong>：① 在管道&#x2F;重定向场景下，<code>print</code> 默认全缓冲，输出可能延迟；② 生产代码中不要用 <code>print</code> 做日志，应使用 <code>logging</code> 模块；③ <code>logging.basicConfig()</code> 只在第一次调用时生效，后续调用不会更新配置。</p></blockquote><h3 id="6-2-input-与类型转换"><a href="#6-2-input-与类型转换" class="headerlink" title="6.2 input() 与类型转换"></a>6.2 <code>input()</code> 与类型转换</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><span class="line"><span class="comment"># input 始终返回字符串</span></span><br><span class="line">age_str = <span class="built_in">input</span>(<span class="string">&quot;请输入年龄: &quot;</span>)</span><br><span class="line">age = <span class="built_in">int</span>(age_str)       <span class="comment"># 需手动转换，若输入非数字会抛出 ValueError</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 安全的输入处理</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">get_int</span>(<span class="params">prompt</span>):</span><br><span class="line">    <span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">        <span class="keyword">try</span>:</span><br><span class="line">            <span class="keyword">return</span> <span class="built_in">int</span>(<span class="built_in">input</span>(prompt))</span><br><span class="line">        <span class="keyword">except</span> ValueError:</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">&quot;请输入有效的整数！&quot;</span>)</span><br><span class="line"></span><br><span class="line">age = get_int(<span class="string">&quot;请输入年龄: &quot;</span>)</span><br></pre></td></tr></table></figure><p><strong>argparse 命令行参数解析</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><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><span class="line"><span class="keyword">import</span> argparse</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">main</span>():</span><br><span class="line">    parser = argparse.ArgumentParser(description=<span class="string">&quot;数据处理工具&quot;</span>)</span><br><span class="line">    <span class="comment"># 位置参数（必需）</span></span><br><span class="line">    parser.add_argument(<span class="string">&quot;input_file&quot;</span>, <span class="built_in">help</span>=<span class="string">&quot;输入文件路径&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 可选参数</span></span><br><span class="line">    parser.add_argument(<span class="string">&quot;-o&quot;</span>, <span class="string">&quot;--output&quot;</span>, default=<span class="string">&quot;output.txt&quot;</span>, <span class="built_in">help</span>=<span class="string">&quot;输出文件路径&quot;</span>)</span><br><span class="line">    parser.add_argument(<span class="string">&quot;-v&quot;</span>, <span class="string">&quot;--verbose&quot;</span>, action=<span class="string">&quot;store_true&quot;</span>, <span class="built_in">help</span>=<span class="string">&quot;详细输出模式&quot;</span>)</span><br><span class="line">    parser.add_argument(<span class="string">&quot;-n&quot;</span>, <span class="string">&quot;--count&quot;</span>, <span class="built_in">type</span>=<span class="built_in">int</span>, default=<span class="number">10</span>, <span class="built_in">help</span>=<span class="string">&quot;处理数量&quot;</span>)</span><br><span class="line">    parser.add_argument(<span class="string">&quot;--mode&quot;</span>, choices=[<span class="string">&quot;fast&quot;</span>, <span class="string">&quot;safe&quot;</span>], default=<span class="string">&quot;safe&quot;</span>, <span class="built_in">help</span>=<span class="string">&quot;处理模式&quot;</span>)</span><br><span class="line"></span><br><span class="line">    args = parser.parse_args()</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> args.verbose:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;输入: <span class="subst">&#123;args.input_file&#125;</span>&quot;</span>)</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;输出: <span class="subst">&#123;args.output&#125;</span>&quot;</span>)</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;模式: <span class="subst">&#123;args.mode&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用示例：</span></span><br><span class="line"><span class="comment"># python script.py data.txt -o result.txt -v --mode fast</span></span><br><span class="line"><span class="comment"># python script.py --help</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    main()</span><br></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><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><span class="line"><span class="comment"># 读取文件</span></span><br><span class="line"><span class="comment"># 方式1：一次性读取全部内容</span></span><br><span class="line"><span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">&quot;data.txt&quot;</span>, <span class="string">&quot;r&quot;</span>, encoding=<span class="string">&quot;utf-8&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">    content = f.read()          <span class="comment"># 读取全部内容为字符串</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 方式2：逐行读取（推荐，内存友好）</span></span><br><span class="line"><span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">&quot;data.txt&quot;</span>, <span class="string">&quot;r&quot;</span>, encoding=<span class="string">&quot;utf-8&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">    <span class="keyword">for</span> line <span class="keyword">in</span> f:              <span class="comment"># 文件对象本身是可迭代的</span></span><br><span class="line">        process(line.strip())   <span class="comment"># strip() 移除换行符</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 方式3：读取所有行为列表</span></span><br><span class="line"><span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">&quot;data.txt&quot;</span>, <span class="string">&quot;r&quot;</span>, encoding=<span class="string">&quot;utf-8&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">    lines = f.readlines()       <span class="comment"># [&quot;line1\n&quot;, &quot;line2\n&quot;, ...]</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 写入文件</span></span><br><span class="line"><span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">&quot;output.txt&quot;</span>, <span class="string">&quot;w&quot;</span>, encoding=<span class="string">&quot;utf-8&quot;</span>) <span class="keyword">as</span> f:   <span class="comment"># &quot;w&quot; 覆盖写入</span></span><br><span class="line">    f.write(<span class="string">&quot;Hello, World!\n&quot;</span>)</span><br><span class="line">    f.writelines([<span class="string">&quot;line1\n&quot;</span>, <span class="string">&quot;line2\n&quot;</span>])                <span class="comment"># 写入多行</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">&quot;output.txt&quot;</span>, <span class="string">&quot;a&quot;</span>, encoding=<span class="string">&quot;utf-8&quot;</span>) <span class="keyword">as</span> f:   <span class="comment"># &quot;a&quot; 追加写入</span></span><br><span class="line">    f.write(<span class="string">&quot;追加的内容\n&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 文件模式</span></span><br><span class="line"><span class="comment"># &quot;r&quot;  只读（默认）</span></span><br><span class="line"><span class="comment"># &quot;w&quot;  写入（覆盖）</span></span><br><span class="line"><span class="comment"># &quot;a&quot;  追加</span></span><br><span class="line"><span class="comment"># &quot;x&quot;  独占创建（文件已存在则报错）</span></span><br><span class="line"><span class="comment"># &quot;b&quot;  二进制模式（如 &quot;rb&quot;, &quot;wb&quot;）</span></span><br><span class="line"><span class="comment"># &quot;+&quot;  读写模式（如 &quot;r+&quot;, &quot;w+&quot;）</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 始终使用 with 语句！确保文件自动关闭</span></span><br><span class="line"><span class="comment"># 始终指定 encoding！避免跨平台编码问题</span></span><br></pre></td></tr></table></figure><blockquote><p><strong>面试真题</strong>：Python 读取大文件时应该用什么方式？<code>with</code> 语句的作用是什么？</p><p><strong>易错点</strong>：① <code>f.read()</code> 读取大文件会撑爆内存，应使用 <code>for line in f</code> 逐行读取；② 不指定 <code>encoding</code> 在 Windows 上默认用 GBK，Linux 上用 UTF-8，导致跨平台问题；③ <code>f.readlines()</code> 也会一次性读取全部内容，大文件慎用。</p></blockquote><h3 id="6-3-字符串格式化方式对比"><a href="#6-3-字符串格式化方式对比" class="headerlink" title="6.3 字符串格式化方式对比"></a>6.3 字符串格式化方式对比</h3><table><thead><tr><th>方式</th><th>示例</th><th>推荐度</th><th>适用场景</th></tr></thead><tbody><tr><td>f-string</td><td><code>f&quot;Hello, {name}&quot;</code></td><td>⭐⭐⭐</td><td>所有新代码</td></tr><tr><td><code>str.format()</code></td><td><code>&quot;Hello, {}&quot;.format(name)</code></td><td>⭐⭐</td><td>动态模板、国际化</td></tr><tr><td><code>%</code> 格式化</td><td><code>&quot;Hello, %s&quot; % name</code></td><td>⭐</td><td>仅维护旧代码</td></tr></tbody></table><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><span class="line">name = <span class="string">&quot;Alice&quot;</span></span><br><span class="line">age = <span class="number">30</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># f-string（3.6+，性能最好、可读性最高）</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;<span class="subst">&#123;name&#125;</span> 今年 <span class="subst">&#123;age&#125;</span> 岁&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;明年 <span class="subst">&#123;name&#125;</span> 就是 <span class="subst">&#123;age + <span class="number">1</span>&#125;</span> 岁了&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;圆周率: <span class="subst">&#123;<span class="number">3.14159</span>:<span class="number">.2</span>f&#125;</span>&quot;</span>)     <span class="comment"># 3.14</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;二进制: <span class="subst">&#123;<span class="number">42</span>:b&#125;</span>&quot;</span>)            <span class="comment"># 101010</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;千分位: <span class="subst">&#123;<span class="number">1234567</span>:,&#125;</span>&quot;</span>)       <span class="comment"># 1,234,567</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 对齐与填充</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;<span class="subst">&#123;name:*&gt;<span class="number">10</span>&#125;</span>&quot;</span>)    <span class="comment"># *****Alice（右对齐，* 填充）</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;<span class="subst">&#123;name:*&lt;<span class="number">10</span>&#125;</span>&quot;</span>)    <span class="comment"># Alice*****（左对齐）</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;<span class="subst">&#123;name:*^<span class="number">10</span>&#125;</span>&quot;</span>)    <span class="comment"># **Alice***（居中）</span></span><br></pre></td></tr></table></figure><p><strong>f-string 高级用法</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><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><span class="line"><span class="comment"># 1. 调试输出 = 语法（Python 3.8+）</span></span><br><span class="line">x = <span class="number">42</span></span><br><span class="line">y = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;<span class="subst">&#123;x = &#125;</span>&quot;</span>)          <span class="comment"># x = 42</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;<span class="subst">&#123;x = :b&#125;</span>&quot;</span>)         <span class="comment"># x = 101010（调试 + 格式化）</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;<span class="subst">&#123;y = &#125;</span>&quot;</span>)           <span class="comment"># y = [1, 2, 3]</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;<span class="subst">&#123;<span class="built_in">len</span>(y) = &#125;</span>&quot;</span>)      <span class="comment"># len(y) = 3</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 2. 嵌套格式化（Python 3.12+ 放宽了限制）</span></span><br><span class="line">width = <span class="number">10</span></span><br><span class="line">precision = <span class="number">3</span></span><br><span class="line">value = <span class="number">3.14159</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;<span class="subst">&#123;value:&#123;width&#125;</span>.<span class="subst">&#123;precision&#125;</span>f&#125;&quot;</span>)   <span class="comment"># &#x27;     3.142&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 动态对齐宽度</span></span><br><span class="line">align = <span class="string">&quot;&gt;&quot;</span></span><br><span class="line">fill = <span class="string">&quot;*&quot;</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;<span class="subst">&#123;name:&#123;fill&#125;</span><span class="subst">&#123;align&#125;</span>10&#125;&quot;</span>)   <span class="comment"># &#x27;*****Alice&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 3. 日期格式化</span></span><br><span class="line"><span class="keyword">from</span> datetime <span class="keyword">import</span> datetime</span><br><span class="line">now = datetime.now()</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;<span class="subst">&#123;now:%Y-%m-%d %H:%M:%S&#125;</span>&quot;</span>)       <span class="comment"># 2026-07-22 10:30:00</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;<span class="subst">&#123;now:%A, %B %d, %Y&#125;</span>&quot;</span>)            <span class="comment"># Wednesday, July 22, 2026</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;<span class="subst">&#123;now:%I:%M %p&#125;</span>&quot;</span>)                 <span class="comment"># 10:30 AM</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 4. 数字格式化</span></span><br><span class="line">num = <span class="number">1234567.8912</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;<span class="subst">&#123;num:<span class="number">.2</span>f&#125;</span>&quot;</span>)         <span class="comment"># 1234567.89（2位小数）</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;<span class="subst">&#123;num:,<span class="number">.2</span>f&#125;</span>&quot;</span>)        <span class="comment"># 1,234,567.89（千分位+2位小数）</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;<span class="subst">&#123;num:<span class="number">.2</span>e&#125;</span>&quot;</span>)         <span class="comment"># 1.23e+06（科学计数法）</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;<span class="subst">&#123;num:<span class="number">.3</span>%&#125;</span>&quot;</span>)         <span class="comment"># 123456789.120%（百分比）</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;<span class="subst">&#123;<span class="number">42</span>:#b&#125;</span>&quot;</span>)           <span class="comment"># 0b101010（带前缀的二进制）</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;<span class="subst">&#123;<span class="number">42</span>:#x&#125;</span>&quot;</span>)           <span class="comment"># 0x2a（带前缀的十六进制）</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;<span class="subst">&#123;<span class="number">42</span>:#o&#125;</span>&quot;</span>)           <span class="comment"># 0o52（带前缀的八进制）</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 5. 对象格式化</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Point</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__format__</span>(<span class="params">self, format_spec</span>):</span><br><span class="line">        <span class="keyword">if</span> format_spec == <span class="string">&quot;r&quot;</span>:</span><br><span class="line">            <span class="keyword">return</span> <span class="string">f&quot;(<span class="subst">&#123;self.x&#125;</span>, <span class="subst">&#123;self.y&#125;</span>)&quot;</span></span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">repr</span>(<span class="variable language_">self</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, x, y</span>):</span><br><span class="line">        <span class="variable language_">self</span>.x = x</span><br><span class="line">        <span class="variable language_">self</span>.y = y</span><br><span class="line"></span><br><span class="line">p = Point(<span class="number">3</span>, <span class="number">4</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;<span class="subst">&#123;p:r&#125;</span>&quot;</span>)   <span class="comment"># (3, 4) —— 使用自定义格式</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 6. 多行 f-string</span></span><br><span class="line">query = <span class="string">f&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">    SELECT *</span></span><br><span class="line"><span class="string">    FROM users</span></span><br><span class="line"><span class="string">    WHERE name = &#x27;<span class="subst">&#123;name&#125;</span>&#x27;</span></span><br><span class="line"><span class="string">      AND age &gt; <span class="subst">&#123;age&#125;</span></span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 7. 字典键的 f-string（注意引号！）</span></span><br><span class="line">data = &#123;<span class="string">&quot;name&quot;</span>: <span class="string">&quot;Alice&quot;</span>, <span class="string">&quot;age&quot;</span>: <span class="number">30</span>&#125;</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;<span class="subst">&#123;data[<span class="string">&#x27;name&#x27;</span>]&#125;</span>&quot;</span>)       <span class="comment"># Alice（外层双引号，内层单引号）</span></span><br><span class="line"><span class="comment"># print(f&quot;&#123;data[&quot;name&quot;]&#125;&quot;)     # SyntaxError（Python 3.11 及以下）</span></span><br><span class="line"><span class="comment"># Python 3.12+ 放宽了限制，可以在 f-string 中使用同类型引号</span></span><br></pre></td></tr></table></figure><blockquote><p><strong>面试真题</strong>：f-string 的 <code>=</code> 语法有什么用？f-string 中如何使用字典的键？f-string 和 <code>str.format()</code> 的性能差异？</p><p><strong>易错点</strong>：① f-string 中的表达式在运行时求值，不是编译期常量；② Python 3.11 及以下 f-string 中字典键不能用同类型引号；③ f-string 不能跨行使用反斜杠（<code>\n</code>），需要用变量代替。</p></blockquote><hr><h2 id="新手常见错误-Top-10"><a href="#新手常见错误-Top-10" class="headerlink" title="新手常见错误 Top 10"></a>新手常见错误 Top 10</h2><p>以下是 Python 初学者最容易犯的错误，务必牢记：</p><h3 id="1-缩进错误"><a href="#1-缩进错误" class="headerlink" title="1. 缩进错误"></a>1. 缩进错误</h3><p>Python 使用缩进表示代码块，混用空格和 Tab 会导致错误。</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><span class="line"><span class="comment"># 错误：混用空格和 Tab</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">greet</span>():</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;Hello&quot;</span>)      <span class="comment"># Tab 缩进</span></span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;World&quot;</span>)     <span class="comment"># 空格缩进（IndentationError）</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 正确：统一使用 4 个空格</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">greet</span>():</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;Hello&quot;</span>)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;World&quot;</span>)</span><br></pre></td></tr></table></figure><p><strong>建议</strong>：在编辑器中设置”显示不可见字符”，并配置”将 Tab 转换为空格”。</p><h3 id="2-变量未定义就使用"><a href="#2-变量未定义就使用" class="headerlink" title="2. 变量未定义就使用"></a>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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 错误：变量未定义</span></span><br><span class="line"><span class="built_in">print</span>(count)  <span class="comment"># NameError: name &#x27;count&#x27; is not defined</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 正确：先定义后使用</span></span><br><span class="line">count = <span class="number">0</span></span><br><span class="line"><span class="built_in">print</span>(count)</span><br></pre></td></tr></table></figure><h3 id="3-列表可变陷阱"><a href="#3-列表可变陷阱" class="headerlink" title="3. 列表可变陷阱"></a>3. 列表可变陷阱</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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 错误：列表作为默认参数（同一对象被复用）</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">add_item</span>(<span class="params">item, items=[]</span>):</span><br><span class="line">    items.append(item)</span><br><span class="line">    <span class="keyword">return</span> items</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(add_item(<span class="number">1</span>))  <span class="comment"># [1]</span></span><br><span class="line"><span class="built_in">print</span>(add_item(<span class="number">2</span>))  <span class="comment"># [1, 2] ← 不是 [2]！</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 正确：使用 None 作为默认值</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">add_item</span>(<span class="params">item, items=<span class="literal">None</span></span>):</span><br><span class="line">    <span class="keyword">if</span> items <span class="keyword">is</span> <span class="literal">None</span>:</span><br><span class="line">        items = []</span><br><span class="line">    items.append(item)</span><br><span class="line">    <span class="keyword">return</span> items</span><br></pre></td></tr></table></figure><h3 id="4-循环中修改列表"><a href="#4-循环中修改列表" class="headerlink" title="4. 循环中修改列表"></a>4. 循环中修改列表</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><span class="line"><span class="comment"># 错误：在遍历时删除元素</span></span><br><span class="line">numbers = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>]</span><br><span class="line"><span class="keyword">for</span> n <span class="keyword">in</span> numbers:</span><br><span class="line">    <span class="keyword">if</span> n % <span class="number">2</span> == <span class="number">0</span>:</span><br><span class="line">        numbers.remove(n)</span><br><span class="line"><span class="built_in">print</span>(numbers)  <span class="comment"># [1, 3, 5] ← 可能漏删！</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 正确：遍历副本或使用列表推导式</span></span><br><span class="line">numbers = [n <span class="keyword">for</span> n <span class="keyword">in</span> numbers <span class="keyword">if</span> n % <span class="number">2</span> != <span class="number">0</span>]</span><br></pre></td></tr></table></figure><h3 id="5-整数除法丢失精度"><a href="#5-整数除法丢失精度" class="headerlink" title="5. 整数除法丢失精度"></a>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></pre></td><td class="code"><pre><span class="line"><span class="comment"># Python 3 中 / 是浮点除法，// 是整数除法</span></span><br><span class="line"><span class="built_in">print</span>(<span class="number">5</span> / <span class="number">2</span>)   <span class="comment"># 2.5（浮点数）</span></span><br><span class="line"><span class="built_in">print</span>(<span class="number">5</span> // <span class="number">2</span>)  <span class="comment"># 2（整数）</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 错误场景：期望整数但得到浮点数</span></span><br><span class="line">index = <span class="number">5</span> / <span class="number">2</span></span><br><span class="line">my_list[index]  <span class="comment"># TypeError: list indices must be integers</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 正确：使用整数除法</span></span><br><span class="line">index = <span class="number">5</span> // <span class="number">2</span></span><br><span class="line">my_list[index]  <span class="comment"># OK</span></span><br></pre></td></tr></table></figure><h3 id="6-误用-is-比较值"><a href="#6-误用-is-比较值" class="headerlink" title="6. 误用 is 比较值"></a>6. 误用 is 比较值</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><span class="line"><span class="comment"># 错误：用 is 比较值相等性</span></span><br><span class="line">a = <span class="number">257</span></span><br><span class="line">b = <span class="number">257</span></span><br><span class="line"><span class="built_in">print</span>(a <span class="keyword">is</span> b)  <span class="comment"># 可能是 False（超出小整数池范围）</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 正确：用 == 比较值</span></span><br><span class="line"><span class="built_in">print</span>(a == b)  <span class="comment"># True</span></span><br></pre></td></tr></table></figure><h3 id="7-字典键不存在错误"><a href="#7-字典键不存在错误" class="headerlink" title="7. 字典键不存在错误"></a>7. 字典键不存在错误</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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 错误：直接访问不存在的键</span></span><br><span class="line">d = &#123;<span class="string">&quot;name&quot;</span>: <span class="string">&quot;Alice&quot;</span>&#125;</span><br><span class="line"><span class="built_in">print</span>(d[<span class="string">&quot;age&quot;</span>])  <span class="comment"># KeyError: &#x27;age&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 正确：使用 get() 方法并提供默认值</span></span><br><span class="line"><span class="built_in">print</span>(d.get(<span class="string">&quot;age&quot;</span>, <span class="number">0</span>))  <span class="comment"># 0</span></span><br></pre></td></tr></table></figure><h3 id="8-字符串不可变陷阱"><a href="#8-字符串不可变陷阱" class="headerlink" title="8. 字符串不可变陷阱"></a>8. 字符串不可变陷阱</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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 错误：尝试修改字符串</span></span><br><span class="line">s = <span class="string">&quot;hello&quot;</span></span><br><span class="line">s[<span class="number">0</span>] = <span class="string">&quot;H&quot;</span>  <span class="comment"># TypeError: &#x27;str&#x27; object does not support item assignment</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 正确：创建新字符串</span></span><br><span class="line">s = <span class="string">&quot;H&quot;</span> + s[<span class="number">1</span>:]  <span class="comment"># &quot;Hello&quot;</span></span><br><span class="line"><span class="comment"># 或使用 replace</span></span><br><span class="line">s = s.replace(<span class="string">&quot;h&quot;</span>, <span class="string">&quot;H&quot;</span>)</span><br></pre></td></tr></table></figure><h3 id="9-浮点数精度问题"><a href="#9-浮点数精度问题" class="headerlink" title="9. 浮点数精度问题"></a>9. 浮点数精度问题</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><span class="line"><span class="comment"># 错误：浮点数精确比较</span></span><br><span class="line"><span class="built_in">print</span>(<span class="number">0.1</span> + <span class="number">0.2</span> == <span class="number">0.3</span>)  <span class="comment"># False（因为 0.1 + 0.2 = 0.30000000000000004）</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 正确：使用 decimal 模块或设置容差</span></span><br><span class="line"><span class="keyword">from</span> decimal <span class="keyword">import</span> Decimal</span><br><span class="line"><span class="built_in">print</span>(Decimal(<span class="string">&#x27;0.1&#x27;</span>) + Decimal(<span class="string">&#x27;0.2&#x27;</span>) == Decimal(<span class="string">&#x27;0.3&#x27;</span>))  <span class="comment"># True</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 或使用容差比较</span></span><br><span class="line"><span class="built_in">abs</span>((<span class="number">0.1</span> + <span class="number">0.2</span>) - <span class="number">0.3</span>) &lt; <span class="number">1e-9</span>  <span class="comment"># True</span></span><br></pre></td></tr></table></figure><h3 id="10-文件未关闭"><a href="#10-文件未关闭" class="headerlink" title="10. 文件未关闭"></a>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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 错误：手动打开但忘记关闭</span></span><br><span class="line">f = <span class="built_in">open</span>(<span class="string">&quot;data.txt&quot;</span>)</span><br><span class="line">data = f.read()</span><br><span class="line"><span class="comment"># 忘记 f.close()，可能导致数据丢失或资源泄漏</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 正确：使用 with 语句（自动关闭）</span></span><br><span class="line"><span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">&quot;data.txt&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">    data = f.read()</span><br><span class="line"><span class="comment"># 文件自动关闭，即使发生异常</span></span><br></pre></td></tr></table></figure><hr><h2 id="常见面试题汇总"><a href="#常见面试题汇总" class="headerlink" title="常见面试题汇总"></a>常见面试题汇总</h2><h3 id="Q1：Python-是解释型语言还是编译型语言？"><a href="#Q1：Python-是解释型语言还是编译型语言？" class="headerlink" title="Q1：Python 是解释型语言还是编译型语言？"></a>Q1：Python 是解释型语言还是编译型语言？</h3><p><strong>答案</strong>：Python 是<strong>解释型语言</strong>，但准确地说，Python 采用”编译+解释”的混合模式。</p><p><strong>执行流程</strong>：</p><ol><li>源代码 (.py) 被编译成字节码 (.pyc)</li><li>Python 虚拟机（PVM）逐行解释执行字节码</li></ol><p><strong>与纯解释型语言的区别</strong>：Python 会缓存字节码，下次执行时如果源码未修改，直接加载 .pyc 文件，提高了启动速度。</p><h3 id="Q2：is-和-的区别是什么？"><a href="#Q2：is-和-的区别是什么？" class="headerlink" title="Q2：is 和 == 的区别是什么？"></a>Q2：<code>is</code> 和 <code>==</code> 的区别是什么？</h3><p><strong>答案</strong>：</p><ul><li><code>is</code> 比较对象的身份（内存地址），即两个引用是否指向同一个对象</li><li><code>==</code> 比较对象的值相等性，调用对象的 <code>__eq__</code> 方法</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></pre></td><td class="code"><pre><span class="line">a = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]</span><br><span class="line">b = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]</span><br><span class="line"><span class="built_in">print</span>(a <span class="keyword">is</span> b)  <span class="comment"># False（不同对象）</span></span><br><span class="line"><span class="built_in">print</span>(a == b)  <span class="comment"># True（值相等）</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 小整数池</span></span><br><span class="line">a = <span class="number">256</span></span><br><span class="line">b = <span class="number">256</span></span><br><span class="line"><span class="built_in">print</span>(a <span class="keyword">is</span> b)  <span class="comment"># True（小整数池范围内）</span></span><br><span class="line"></span><br><span class="line">a = <span class="number">257</span></span><br><span class="line">b = <span class="number">257</span></span><br><span class="line"><span class="built_in">print</span>(a <span class="keyword">is</span> b)  <span class="comment"># 可能 False（超出范围）</span></span><br></pre></td></tr></table></figure><h3 id="Q3：什么是-Python-的小整数池？"><a href="#Q3：什么是-Python-的小整数池？" class="headerlink" title="Q3：什么是 Python 的小整数池？"></a>Q3：什么是 Python 的小整数池？</h3><p><strong>答案</strong>：CPython 将 -5 到 256 的整数预先创建并缓存，这些整数是单例的，可以安全地用 <code>is</code> 比较。</p><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><span class="line">a = <span class="number">256</span></span><br><span class="line">b = <span class="number">256</span></span><br><span class="line"><span class="built_in">print</span>(a <span class="keyword">is</span> b)  <span class="comment"># True</span></span><br><span class="line"></span><br><span class="line">a = <span class="number">257</span></span><br><span class="line">b = <span class="number">257</span></span><br><span class="line"><span class="built_in">print</span>(a <span class="keyword">is</span> b)  <span class="comment"># False（在交互式中）</span></span><br></pre></td></tr></table></figure><h3 id="Q4：深拷贝和浅拷贝的区别？"><a href="#Q4：深拷贝和浅拷贝的区别？" class="headerlink" title="Q4：深拷贝和浅拷贝的区别？"></a>Q4：深拷贝和浅拷贝的区别？</h3><p><strong>答案</strong>：</p><ul><li><strong>浅拷贝</strong>：创建新对象，但对内部子对象仍是引用（<code>copy.copy()</code> 或 <code>list[:]</code>）</li><li><strong>深拷贝</strong>：递归复制所有子对象，完全独立（<code>copy.deepcopy()</code>）</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> copy</span><br><span class="line"></span><br><span class="line">original = [[<span class="number">1</span>, <span class="number">2</span>], [<span class="number">3</span>, <span class="number">4</span>]]</span><br><span class="line"></span><br><span class="line">shallow = copy.copy(original)</span><br><span class="line">shallow[<span class="number">0</span>][<span class="number">0</span>] = <span class="number">99</span></span><br><span class="line"><span class="built_in">print</span>(original)  <span class="comment"># [[99, 2], [3, 4]] ← 被影响</span></span><br><span class="line"></span><br><span class="line">deep = copy.deepcopy(original)</span><br><span class="line">deep[<span class="number">0</span>][<span class="number">0</span>] = <span class="number">100</span></span><br><span class="line"><span class="built_in">print</span>(original)  <span class="comment"># [[99, 2], [3, 4]] ← 不受影响</span></span><br></pre></td></tr></table></figure><h3 id="Q5：Python-中的变量是什么？"><a href="#Q5：Python-中的变量是什么？" class="headerlink" title="Q5：Python 中的变量是什么？"></a>Q5：Python 中的变量是什么？</h3><p><strong>答案</strong>：Python 中的变量是<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></pre></td><td class="code"><pre><span class="line">a = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]</span><br><span class="line">b = a</span><br><span class="line">b.append(<span class="number">4</span>)</span><br><span class="line"><span class="built_in">print</span>(a)  <span class="comment"># [1, 2, 3, 4] ← a 也变了</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 赋值是创建新引用，不是复制对象</span></span><br></pre></td></tr></table></figure><h3 id="Q6：-args-和-kwargs-的作用？"><a href="#Q6：-args-和-kwargs-的作用？" class="headerlink" title="Q6：*args 和 **kwargs 的作用？"></a>Q6：<code>*args</code> 和 <code>**kwargs</code> 的作用？</h3><p><strong>答案</strong>：</p><ul><li><code>*args</code>：收集位置参数为元组</li><li><code>**kwargs</code>：收集关键字参数为字典</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">func</span>(<span class="params">*args, **kwargs</span>):</span><br><span class="line">    <span class="built_in">print</span>(args)      <span class="comment"># (1, 2, 3)</span></span><br><span class="line">    <span class="built_in">print</span>(kwargs)    <span class="comment"># &#123;&#x27;a&#x27;: 4, &#x27;b&#x27;: 5&#125;</span></span><br><span class="line"></span><br><span class="line">func(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, a=<span class="number">4</span>, b=<span class="number">5</span>)</span><br></pre></td></tr></table></figure><h3 id="Q7：Python-中如何实现多行注释？"><a href="#Q7：Python-中如何实现多行注释？" class="headerlink" title="Q7：Python 中如何实现多行注释？"></a>Q7：Python 中如何实现多行注释？</h3><p><strong>答案</strong>：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><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">这是多行字符串，</span></span><br><span class="line"><span class="string">可以用作文档字符串（docstring），</span></span><br><span class="line"><span class="string">也可以当作多行注释使用。</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br></pre></td></tr></table></figure><h3 id="Q8：pass-语句的作用？"><a href="#Q8：pass-语句的作用？" class="headerlink" title="Q8：pass 语句的作用？"></a>Q8：<code>pass</code> 语句的作用？</h3><p><strong>答案</strong>：<code>pass</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">not_implemented</span>():</span><br><span class="line">    <span class="keyword">pass</span>  <span class="comment"># <span class="doctag">TODO:</span> 稍后实现</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">EmptyClass</span>:</span><br><span class="line">    <span class="keyword">pass</span></span><br></pre></td></tr></table></figure><h3 id="Q9：Python-3-相比-Python-2-的重要变化？"><a href="#Q9：Python-3-相比-Python-2-的重要变化？" class="headerlink" title="Q9：Python 3 相比 Python 2 的重要变化？"></a>Q9：Python 3 相比 Python 2 的重要变化？</h3><p><strong>答案</strong>：</p><ul><li><code>print</code> 从语句变为函数</li><li>整数除法：<code>/</code> 返回浮点数，<code>//</code> 返回整数</li><li>默认字符串为 Unicode</li><li><code>range</code> 返回迭代器，不再返回列表</li><li>新增 <code>async/await</code> 语法</li><li>类型注解支持</li></ul><h3 id="Q10：如何判断一个对象是否可迭代？"><a href="#Q10：如何判断一个对象是否可迭代？" class="headerlink" title="Q10：如何判断一个对象是否可迭代？"></a>Q10：如何判断一个对象是否可迭代？</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> collections.abc <span class="keyword">import</span> Iterable</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">isinstance</span>([<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>], Iterable))  <span class="comment"># True</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">isinstance</span>(<span class="number">123</span>, Iterable))         <span class="comment"># False</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 或使用 hasattr</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">hasattr</span>([<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>], <span class="string">&#x27;__iter__&#x27;</span>))    <span class="comment"># True</span></span><br></pre></td></tr></table></figure><hr><h2 id="实战项目：命令行计算器"><a href="#实战项目：命令行计算器" class="headerlink" title="实战项目：命令行计算器"></a>实战项目：命令行计算器</h2><p>通过一个完整的小项目，综合运用本阶段学习的知识点。</p><h3 id="项目目标"><a href="#项目目标" class="headerlink" title="项目目标"></a>项目目标</h3><p>创建一个支持基本运算的命令行计算器，具备以下功能：</p><ul><li>加、减、乘、除四则运算</li><li>支持连续计算</li><li>友好的错误提示</li><li>输入 <code>quit</code> 退出程序</li></ul><h3 id="完整代码"><a href="#完整代码" class="headerlink" title="完整代码"></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><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></pre></td><td class="code"><pre><span class="line"><span class="comment">#!/usr/bin/env python3</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">命令行计算器 - 第一阶段实战项目</span></span><br><span class="line"><span class="string">知识点：变量、运算符、控制流、函数、异常处理、字符串格式化</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">add</span>(<span class="params">a, b</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;加法&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">return</span> a + b</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">subtract</span>(<span class="params">a, b</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;减法&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">return</span> a - b</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">multiply</span>(<span class="params">a, b</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;乘法&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">return</span> a * b</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">divide</span>(<span class="params">a, b</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;除法&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">if</span> b == <span class="number">0</span>:</span><br><span class="line">        <span class="keyword">raise</span> ValueError(<span class="string">&quot;除数不能为零&quot;</span>)</span><br><span class="line">    <span class="keyword">return</span> a / b</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">calculate</span>(<span class="params">a, op, b</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;根据运算符执行计算&quot;&quot;&quot;</span></span><br><span class="line">    operations = &#123;</span><br><span class="line">        <span class="string">&#x27;+&#x27;</span>: add,</span><br><span class="line">        <span class="string">&#x27;-&#x27;</span>: subtract,</span><br><span class="line">        <span class="string">&#x27;*&#x27;</span>: multiply,</span><br><span class="line">        <span class="string">&#x27;/&#x27;</span>: divide</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">if</span> op <span class="keyword">not</span> <span class="keyword">in</span> operations:</span><br><span class="line">        <span class="keyword">raise</span> ValueError(<span class="string">f&quot;不支持的运算符: <span class="subst">&#123;op&#125;</span>&quot;</span>)</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> operations[op](a, b)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">get_number</span>(<span class="params">prompt</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;获取用户输入的数字&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">        <span class="keyword">try</span>:</span><br><span class="line">            <span class="keyword">return</span> <span class="built_in">float</span>(<span class="built_in">input</span>(prompt))</span><br><span class="line">        <span class="keyword">except</span> ValueError:</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">&quot;请输入有效的数字！&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">get_operator</span>():</span><br><span class="line">    <span class="string">&quot;&quot;&quot;获取用户输入的运算符&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">        op = <span class="built_in">input</span>(<span class="string">&quot;请输入运算符 (+, -, *, /): &quot;</span>).strip()</span><br><span class="line">        <span class="keyword">if</span> op <span class="keyword">in</span> (<span class="string">&#x27;+&#x27;</span>, <span class="string">&#x27;-&#x27;</span>, <span class="string">&#x27;*&#x27;</span>, <span class="string">&#x27;/&#x27;</span>):</span><br><span class="line">            <span class="keyword">return</span> op</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;请输入有效的运算符: +, -, *, /&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">main</span>():</span><br><span class="line">    <span class="string">&quot;&quot;&quot;主函数&quot;&quot;&quot;</span></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;=&quot;</span> * <span class="number">40</span>)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;       命令行计算器 v1.0&quot;</span>)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;  输入 &#x27;quit&#x27; 或 &#x27;q&#x27; 退出程序&quot;</span>)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;=&quot;</span> * <span class="number">40</span>)</span><br><span class="line">    </span><br><span class="line">    history = []  <span class="comment"># 计算历史</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;\n&quot;</span> + <span class="string">&quot;-&quot;</span> * <span class="number">30</span>)</span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 获取第一个数字</span></span><br><span class="line">        first_input = <span class="built_in">input</span>(<span class="string">&quot;请输入第一个数字 (或输入 quit 退出): &quot;</span>).strip()</span><br><span class="line">        <span class="keyword">if</span> first_input.lower() <span class="keyword">in</span> (<span class="string">&#x27;quit&#x27;</span>, <span class="string">&#x27;q&#x27;</span>):</span><br><span class="line">            <span class="keyword">break</span></span><br><span class="line">        </span><br><span class="line">        <span class="keyword">try</span>:</span><br><span class="line">            a = <span class="built_in">float</span>(first_input)</span><br><span class="line">        <span class="keyword">except</span> ValueError:</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">&quot;请输入有效的数字！&quot;</span>)</span><br><span class="line">            <span class="keyword">continue</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 获取运算符</span></span><br><span class="line">        op = get_operator()</span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 获取第二个数字</span></span><br><span class="line">        b = get_number(<span class="string">&quot;请输入第二个数字: &quot;</span>)</span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 执行计算</span></span><br><span class="line">        <span class="keyword">try</span>:</span><br><span class="line">            result = calculate(a, op, b)</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">f&quot;\n结果: <span class="subst">&#123;a&#125;</span> <span class="subst">&#123;op&#125;</span> <span class="subst">&#123;b&#125;</span> = <span class="subst">&#123;result&#125;</span>&quot;</span>)</span><br><span class="line">            </span><br><span class="line">            <span class="comment"># 记录历史</span></span><br><span class="line">            history.append(<span class="string">f&quot;<span class="subst">&#123;a&#125;</span> <span class="subst">&#123;op&#125;</span> <span class="subst">&#123;b&#125;</span> = <span class="subst">&#123;result&#125;</span>&quot;</span>)</span><br><span class="line">            </span><br><span class="line">        <span class="keyword">except</span> ValueError <span class="keyword">as</span> e:</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">f&quot;错误: <span class="subst">&#123;e&#125;</span>&quot;</span>)</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 显示历史记录</span></span><br><span class="line">    <span class="keyword">if</span> history:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;\n&quot;</span> + <span class="string">&quot;=&quot;</span> * <span class="number">40</span>)</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;本次计算历史:&quot;</span>)</span><br><span class="line">        <span class="keyword">for</span> i, record <span class="keyword">in</span> <span class="built_in">enumerate</span>(history, <span class="number">1</span>):</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">f&quot;  <span class="subst">&#123;i&#125;</span>. <span class="subst">&#123;record&#125;</span>&quot;</span>)</span><br><span class="line">    </span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;\n感谢使用，再见！&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    main()</span><br></pre></td></tr></table></figure><h3 id="运行示例"><a href="#运行示例" class="headerlink" title="运行示例"></a>运行示例</h3><figure class="highlight plaintext"><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><span class="line">========================================</span><br><span class="line">       命令行计算器 v1.0</span><br><span class="line">  输入 &#x27;quit&#x27; 或 &#x27;q&#x27; 退出程序</span><br><span class="line">========================================</span><br><span class="line"></span><br><span class="line">------------------------------</span><br><span class="line">请输入第一个数字 (或输入 quit 退出): 10</span><br><span class="line">请输入运算符 (+, -, *, /): +</span><br><span class="line">请输入第二个数字: 5</span><br><span class="line"></span><br><span class="line">结果: 10.0 + 5.0 = 15.0</span><br><span class="line"></span><br><span class="line">------------------------------</span><br><span class="line">请输入第一个数字 (或输入 quit 退出): 100</span><br><span class="line">请输入运算符 (+, -, *, /): /</span><br><span class="line">请输入第二个数字: 0</span><br><span class="line"></span><br><span class="line">错误: 除数不能为零</span><br><span class="line"></span><br><span class="line">------------------------------</span><br><span class="line">请输入第一个数字 (或输入 quit 退出): quit</span><br><span class="line"></span><br><span class="line">========================================</span><br><span class="line">本次计算历史:</span><br><span class="line">  1. 10.0 + 5.0 = 15.0</span><br><span class="line"></span><br><span class="line">感谢使用，再见！</span><br></pre></td></tr></table></figure><h3 id="知识点覆盖"><a href="#知识点覆盖" class="headerlink" title="知识点覆盖"></a>知识点覆盖</h3><table><thead><tr><th>功能</th><th>涉及知识点</th></tr></thead><tbody><tr><td>函数定义</td><td><code>def</code>、参数、返回值、文档字符串</td></tr><tr><td>运算符</td><td>算术运算符、比较运算符</td></tr><tr><td>控制流</td><td><code>while</code>、<code>if</code>、<code>break</code>、<code>continue</code></td></tr><tr><td>异常处理</td><td><code>try/except</code>、<code>raise</code></td></tr><tr><td>字符串</td><td><code>input()</code>、<code>strip()</code>、<code>lower()</code>、f-string</td></tr><tr><td>数据结构</td><td>字典映射运算符、列表存储历史</td></tr><tr><td>模块入口</td><td><code>if __name__ == &quot;__main__&quot;</code></td></tr></tbody></table><h3 id="进阶练习"><a href="#进阶练习" class="headerlink" title="进阶练习"></a>进阶练习</h3><p>尝试为计算器添加以下功能：</p><ol><li>支持取模（<code>%</code>）和幂运算（<code>**</code>）</li><li>支持科学计数法输入（如 <code>1e10</code>）</li><li>添加 <code>history</code> 命令查看历史</li><li>支持从文件读取计算表达式</li><li>添加 <code>help</code> 命令显示帮助信息</li></ol><hr><h2 id="附录：第一阶段自检清单"><a href="#附录：第一阶段自检清单" class="headerlink" title="附录：第一阶段自检清单"></a>附录：第一阶段自检清单</h2><p>在继续学习第二阶段之前，请确保你能：</p><ul><li><input disabled="" type="checkbox"> 独立完成 Python 环境搭建，创建并激活虚拟环境</li><li><input disabled="" type="checkbox"> 解释 <code>is</code> 和 <code>==</code> 的区别，并正确使用</li><li><input disabled="" type="checkbox"> 说明 Python 代码从源码到执行的完整流程（编译→字节码→PVM）</li><li><input disabled="" type="checkbox"> 解释小整数池（-5~256）和字符串驻留机制</li><li><input disabled="" type="checkbox"> 写出列表推导式，并理解其执行过程</li><li><input disabled="" type="checkbox"> 说明 <code>dict</code> 的键为什么必须可哈希，以及哈希表的查找流程</li><li><input disabled="" type="checkbox"> 解释深拷贝与浅拷贝的区别，并能用代码验证</li><li><input disabled="" type="checkbox"> 理解 <code>Decimal</code> 和 <code>Fraction</code> 的使用场景</li><li><input disabled="" type="checkbox"> 区分可迭代对象和迭代器</li><li><input disabled="" type="checkbox"> 正确使用 <code>try/except</code> 处理 <code>input()</code> 的类型转换异常</li><li><input disabled="" type="checkbox"> 用 f-string 完成常见的格式化需求（对齐、精度、进制转换、调试输出）</li><li><input disabled="" type="checkbox"> 解释 <code>for-else</code> 的执行逻辑，并写出实际应用场景</li><li><input disabled="" type="checkbox"> 理解函数默认参数的可变对象陷阱，并能写出安全版本</li><li><input disabled="" type="checkbox"> 区分 <code>and/or</code> 的返回值与 <code>True/False</code></li><li><input disabled="" type="checkbox"> 使用 <code>logging</code> 模块替代 <code>print</code> 做日志输出</li><li><input disabled="" type="checkbox"> 理解 <code>@dataclass</code> 和 <code>Enum</code> 的基本用法</li><li><input disabled="" type="checkbox"> 使用海象运算符减少重复计算</li><li><input disabled="" type="checkbox"> 使用 <code>match/case</code> 进行模式匹配</li></ul><hr><blockquote><p><strong>工程师寄语</strong>：基础语法的熟练程度决定了你写代码的”手感”。不要急于求成，多写、多错、多总结。Python 的优雅不在于语法本身，而在于用简洁的代码表达清晰的意图。</p></blockquote>]]>
    </content>
    <id>https://itdxb.cn/2026/07/22/python/document/%E3%80%90Python%E3%80%91%E5%9F%BA%E7%A1%80%E8%AF%AD%E6%B3%95%E4%B8%8E%E6%A0%B8%E5%BF%83%E7%BC%96%E7%A8%8B%EF%BC%88%E7%AC%AC%E4%B8%80%E9%98%B6%E6%AE%B5%EF%BC%89/</id>
    <link href="https://itdxb.cn/2026/07/22/python/document/%E3%80%90Python%E3%80%91%E5%9F%BA%E7%A1%80%E8%AF%AD%E6%B3%95%E4%B8%8E%E6%A0%B8%E5%BF%83%E7%BC%96%E7%A8%8B%EF%BC%88%E7%AC%AC%E4%B8%80%E9%98%B6%E6%AE%B5%EF%BC%89/"/>
    <published>2026-07-22T02:00:00.000Z</published>
    <summary>建立 Python 编程的根基，掌握语言核心机制与开发环境配置。此阶段的重点是&quot;知其然且知其所以然&quot;，养成 Pythonic 的编码直觉。</summary>
    <title>【Python】基础语法与核心编程（第一阶段）</title>
    <updated>2026-07-24T03:13:39.019Z</updated>
  </entry>
  <entry>
    <author>
      <name>Super Bing</name>
    </author>
    <category term="公告" scheme="https://itdxb.cn/categories/%E5%85%AC%E5%91%8A/"/>
    <category term="公告" scheme="https://itdxb.cn/tags/%E5%85%AC%E5%91%8A/"/>
    <category term="毕设" scheme="https://itdxb.cn/tags/%E6%AF%95%E8%AE%BE/"/>
    <content>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><h2 id="欢迎访问毕设项目库！"><a href="#欢迎访问毕设项目库！" class="headerlink" title="欢迎访问毕设项目库！"></a>欢迎访问毕设项目库！</h2><p>这是一个收集和展示软件工程专业毕业设计项目的平台。</p><h2 id="功能特点"><a href="#功能特点" class="headerlink" title="功能特点"></a>功能特点</h2><ul><li><strong>项目搜索</strong> - 支持按名称、技术栈、业务领域搜索</li><li><strong>分类筛选</strong> - 支持按项目类型筛选</li><li><strong>技术展示</strong> - 清晰展示每个项目的技术栈</li></ul><h2 id="快速导航"><a href="#快速导航" class="headerlink" title="快速导航"></a>快速导航</h2><ul><li><a href="/LW-blog/projects/">查看全部项目</a></li><li><a href="/LW-blog/about/">关于本站</a></li><li><a href="/LW-blog/link/">交换友链</a></li></ul><hr><p>感谢您的访问！<br>Welcome to <a href="https://hexo.io/">Hexo</a>! This is your very first post. Check <a href="https://hexo.io/docs/">documentation</a> for more info. If you get any problems when using Hexo, you can find the answer in <a href="https://hexo.io/docs/troubleshooting.html">troubleshooting</a> or you can ask me on <a href="https://github.com/hexojs/hexo/issues">GitHub</a>.</p><h2 id="Quick-Start"><a href="#Quick-Start" class="headerlink" title="Quick Start"></a>Quick Start</h2><h3 id="Create-a-new-post"><a href="#Create-a-new-post" class="headerlink" title="Create a new post"></a>Create a new post</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo new <span class="string">&quot;My New Post&quot;</span></span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/writing.html">Writing</a></p><h3 id="Run-server"><a href="#Run-server" class="headerlink" title="Run server"></a>Run server</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo server</span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/server.html">Server</a></p><h3 id="Generate-static-files"><a href="#Generate-static-files" class="headerlink" title="Generate static files"></a>Generate static files</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo generate</span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/generating.html">Generating</a></p><h3 id="Deploy-to-remote-sites"><a href="#Deploy-to-remote-sites" class="headerlink" title="Deploy to remote sites"></a>Deploy to remote sites</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo deploy</span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/one-command-deployment.html">Deployment</a></p>]]>
    </content>
    <id>https://itdxb.cn/2026/05/04/hello-world/</id>
    <link href="https://itdxb.cn/2026/05/04/hello-world/"/>
    <published>2026-05-03T16:00:00.000Z</published>
    <summary>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" cla]]>
    </summary>
    <title>欢迎使用毕设项目库</title>
    <updated>2026-07-16T06:36:47.882Z</updated>
  </entry>
  <entry>
    <author>
      <name>Super Bing</name>
    </author>
    <category term="Node.js" scheme="https://itdxb.cn/categories/Node-js/"/>
    <category term="教程" scheme="https://itdxb.cn/tags/%E6%95%99%E7%A8%8B/"/>
    <category term="Node.js" scheme="https://itdxb.cn/tags/Node-js/"/>
    <content>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><h2 id="Node-js-模块化开发"><a href="#Node-js-模块化开发" class="headerlink" title="Node.js 模块化开发"></a><a href="#Node-js-%E6%A8%A1%E5%9D%97%E5%8C%96%E5%BC%80%E5%8F%91"></a>Node.js 模块化开发</h2><h3 id="Node-js运行环境搭建"><a href="#Node-js运行环境搭建" class="headerlink" title="Node.js运行环境搭建"></a><a href="#Node-js%E8%BF%90%E8%A1%8C%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA"></a>Node.js运行环境搭建</h3><p>Node.js是一个基于<strong>Chrome V8引擎</strong>的JavaScript代码运行<strong>环境</strong>，也可以说是一个运行时平台，提供了一些功能性的API，如<strong>文件操作API</strong>、<strong>网络通信API</strong>。<br>如果在浏览器运行JavaScript代码，浏览器就是JavaScript代码的运行环境；</p><blockquote></blockquote><p><strong>如果在Node.js平台运行JavaScript代码，Node.js就是JavaScript代码的运行环境。</strong></p><h4 id="下载和安装"><a href="#下载和安装" class="headerlink" title="下载和安装"></a><a href="#%E4%B8%8B%E8%BD%BD%E5%92%8C%E5%AE%89%E8%A3%85"></a>下载和安装</h4><p>打开Node.js官方网站，找到Node.js下载地址。</p><p>官网：<a href="https://nodejs.org/en">https://nodejs.org/en</a></p><p><img src="/../../../images/node02-01.png"></p><p><img src="/../../../images/node02-02.png"></p><h4 id="测试Node-js是否安装成功"><a href="#测试Node-js是否安装成功" class="headerlink" title="测试Node.js是否安装成功"></a><a href="#%E6%B5%8B%E8%AF%95Node-js%E6%98%AF%E5%90%A6%E5%AE%89%E8%A3%85%E6%88%90%E5%8A%9F"></a>测试Node.js是否安装成功</h4><p>按<strong>Windows+R</strong>组合键，打开“运行”对话框，输入<strong>cmd</strong>。</p><p><img src="/../../../images/node02-03.png"></p><p>单击<strong>确定</strong>按钮，或者直接按<strong>Enter</strong>键，会打开<strong>cmd命令提示符界面</strong>。</p><p><img src="/../../../images/node02-04.png"></p><p>在cmd命令提示符界面中，输入命令<strong>node -v</strong>，按<strong>Enter</strong>键，显示当前安装的Node.js版本。</p><p><img src="/../../../images/node02-05.png"></p><p>若想要退出cmd命令提示符界面，可以输入<strong>exit</strong>并按<strong>Enter</strong>键，或者单击cmd命令提示符界面右上角的“×”（关闭）按钮。</p><blockquote></blockquote><p>多学一招：PowerShell工具测试Node.js是否安装成功<br><img src="/../../../images/node02-06.png"><br><img src="/../../../images/node02-07.png"></p><h4 id="Node-js环境常见安装失败情况"><a href="#Node-js环境常见安装失败情况" class="headerlink" title="Node.js环境常见安装失败情况"></a><a href="#Node-js%E7%8E%AF%E5%A2%83%E5%B8%B8%E8%A7%81%E5%AE%89%E8%A3%85%E5%A4%B1%E8%B4%A5%E6%83%85%E5%86%B5"></a>Node.js环境常见安装失败情况</h4><p>不同用户使用的系统配置是不统一的，在一些系统配置中会有不稳定的配置，可能会导致Node.js环境安装失败。</p><p><a href="#%E9%94%99%E8%AF%AF%E4%BB%A3%E5%8F%B72503%E7%9A%84%E8%A7%A3%E5%86%B3%E6%96%B9%E6%B3%95"></a>错误代号2503的解决方法</p><p>在安装过程中，突然弹出了一个消息框，提示2503错误。<br><img src="/../../../images/node02-08.png"></p><blockquote></blockquote><p>解决方法</p><p>（1）使用管理员身份运行PowerShell命令提示符工具。<br><img src="/../../../images/node02-09.png"></p><p>（2）以管理员身份进入PowerShell命令提示符界面。<br><img src="/../../../images/node02-10.png"></p><p><a href="#%E6%89%A7%E8%A1%8C%E5%91%BD%E4%BB%A4%E6%8A%A5%E9%94%99"></a>执行命令报错</p><p>Node.js安装成功后，输入“node -v”命令进行验证Node运行环境是否安装成功时报错。<br><img src="/../../../images/node02-11.png"></p><p>在常规情况下，Node.js安装过程中，安装包会自动把Node.js的安装目录放入到系统的环境变量Path中，若是出现上图错误表明操作失败。<br><strong>解决方法是需要手动将Node.js安装目录添加到环境变量Path中。</strong></p><blockquote></blockquote><p>解决方法：以Windows 10操作系统为例</p><p>（1）首先找到Node.js的安装目录，本机的Node.js安装目录是C:\Program Files\nodejs，将该目录地址进行复制。</p><p>（2）右击“此电脑”图标，选择“属性”命令，进入“系统”界面，执行如下操作。</p><p><img src="/../../../images/node02-12.png"></p><h3 id="Node-js的基本使用"><a href="#Node-js的基本使用" class="headerlink" title="Node.js的基本使用"></a><a href="#Node-js%E7%9A%84%E5%9F%BA%E6%9C%AC%E4%BD%BF%E7%94%A8"></a>Node.js的基本使用</h3><h4 id="Node-js的组成"><a href="#Node-js的组成" class="headerlink" title="Node.js的组成"></a><a href="#Node-js%E7%9A%84%E7%BB%84%E6%88%90"></a>Node.js的组成</h4><p>JavaScript和Node.js的核心语法都是ECMAScript。</p><p><img src="/../../../images/node02-13.png"></p><p><img src="/../../../images/node02-14.png"></p><p>JavaScript在客户端和服务端实现不同功能</p><blockquote></blockquote><p>客户端</p><p>JavaScript需要依赖<strong>浏览器</strong>提供的JavaScript引擎解析执行，浏览器还提供了对DOM的解析，所以客户端的JavaScript不仅应用了核心语法ECMAScript，而且能<strong>操作DOM</strong>和<strong>BOM</strong>，常见的应用场景如用户交互、动画特效、表单验证、发送Ajax请求等。</p><blockquote></blockquote><p>服务器端</p><p>JavaScript不依赖浏览器，而是由<strong>特定的运行环境</strong>提供的JavaScript引擎解析执行，例如Node.js。服务器端的JavaScript应用了核心语法ECMAScript，但是<strong>不操作DOM和BOM</strong>，它常常用来做一些在客户端做不到的事情，例如操作数据库、操作文件等。另外，在客户端的Ajax操作只能发送请求，而接收请求和做出响应的操作就需要服务端的JavaScript来完成。</p><h4 id="Node-js基础语法"><a href="#Node-js基础语法" class="headerlink" title="Node.js基础语法"></a><a href="#Node-js%E5%9F%BA%E7%A1%80%E8%AF%AD%E6%B3%95"></a>Node.js基础语法</h4><p>通过node命令解析和执行一个js脚本文件的步骤如下：</p><ul><li><p>根据node命令指定的文件名称，读取js脚本文件。</p></li><li><p>解析和执行JavaScript代码。</p></li><li><p>将执行后的结果输出到命令行中。</p></li></ul><blockquote></blockquote><p>演示在Node.js中如何执行一个js脚本文件</p><p>在chapter02目录下新建helloworld.js文件，编写JavaScript代码</p><p>console.log(&#x27;hello world&#x27;);</p><p>打开命令行工具，切换到helloworld.js文件所在的目录，并输入“node helloworld.js”命令。</p><p><img src="/../../../images/node02-15.png"></p><h4 id="Node-js全局对象global"><a href="#Node-js全局对象global" class="headerlink" title="Node.js全局对象global"></a><a href="#Node-js%E5%85%A8%E5%B1%80%E5%AF%B9%E8%B1%A1global"></a>Node.js全局对象global</h4><p>在之前使用JavaScript的过程中，在浏览器中默认声明的变量、函数等都属于<strong>全局对象window</strong>，全局对象中的所有变量和函数在全局作用域内都是有效的。</p><p>例如，我们使用console.log()进行值的输出时，console.log()属于window对象的方法，又因为window是全局对象，所以在实际使用中可以省略掉window。</p><p><strong>Node.js代码的运行环境存在window对象吗?</strong></p><p>在Node.js代码的运行环境中没有DOM和BOM，因此也就<strong>不存在window对象</strong>。</p><p><strong>那么使用的console.log()是来自于哪里呢？</strong></p><p>在Node.js中，默认就是<strong>模块化</strong>的，默认声明的变量、函数都属于当前文件模块，都是私有的，只在当前模块作用域内可以使用。</p><p><strong>Node.js中是否只有模块作用域？</strong></p><p>答案是否定的，如果想在全局范围内为某个变量赋值，可以应用<strong>全局对象global</strong>。Node.js中的global对象类似于浏览器中的window对象，用于<strong>定义全局命名空间</strong>，所有全局变量（除了global本身以外）都是global对象的属性，在实际使用中可以省略global。</p><blockquote></blockquote><p>演示console.log()和setTimeout()方法在Node.js运行环境中的使用</p><p>在chapter02目录下新建global.js文件，编写JavaScript代码。</p><p>global.console.log(&#x27;我是global对象中的console.log()方法&#x27;);<br>global.setTimeout(() &#x3D;&gt; &#123;<br> console.log(&#x27;123&#x27;);<br>&#125;, 2000);</p><p>打开命令行工具，切换到global.js文件所在的目录，并输入“node global.js”命令<br><img src="/../../../images/node02-16.png"></p><h3 id="初识模块化开发"><a href="#初识模块化开发" class="headerlink" title="初识模块化开发"></a><a href="#%E5%88%9D%E8%AF%86%E6%A8%A1%E5%9D%97%E5%8C%96%E5%BC%80%E5%8F%91"></a>初识模块化开发</h3><p><strong>模块化是软件的一种开发方式</strong>，利用<strong>模块化</strong>可以把一个非常复杂的系统结构细化到具体的功能点，每个功能点看作一个<strong>模块</strong>，然后通过某种规则把这些小的模块组合到一起，构成模块化系统。下面将详细讲解为什么使用模块化开发和模块化的概念。</p><h4 id="传统JavaScript开发的弊端"><a href="#传统JavaScript开发的弊端" class="headerlink" title="传统JavaScript开发的弊端"></a><a href="#%E4%BC%A0%E7%BB%9FJavaScript%E5%BC%80%E5%8F%91%E7%9A%84%E5%BC%8A%E7%AB%AF"></a>传统JavaScript开发的弊端</h4><p>传统浏览器端JavaScript在使用的时候存在的两大问题</p><p><a href="#%E6%96%87%E4%BB%B6%E4%BE%9D%E8%B5%96"></a>文件依赖</p><p>在JavaScript中文件的依赖关系是由文件的<strong>引入先后顺序决定</strong>的。在开发过程中，一个页面可能需要多个文件依赖，但是仅从代码上是看不出来各个文件之间的依赖关系，这种依赖关系存在<strong>不确定性</strong>。如果更改文件的引入先后顺序，就很有可能导致程序错误。</p><p><a href="#%E5%91%BD%E5%90%8D%E5%86%B2%E7%AA%81"></a>命名冲突</p><p>在JavaScript中，文件与文件之间是<strong>完全开放的</strong>，并且语法<strong>本身不严谨</strong>，如果在后续引入的文件中声明了一个同名变量，则后面文件的变量会覆盖前面文件中的同名变量，这样会导致程序存在潜在的<strong>不确定性</strong>。</p><h4 id="模块化的概念"><a href="#模块化的概念" class="headerlink" title="模块化的概念"></a><a href="#%E6%A8%A1%E5%9D%97%E5%8C%96%E7%9A%84%E6%A6%82%E5%BF%B5"></a>模块化的概念</h4><p>现实生活中手机的模块化</p><p>从生产角度来看，模块化是一种生产方式，体现了以下两个特点:</p><ul><li><p>生产效率高：灵活架构，焦点分离；多人协作互不干扰；方便模块间组合、分解。</p></li><li><p>维护成本低：可分单元测试；方便单个模块功能调试、升级。<br><img src="/../../../images/node02-17.png"></p></li></ul><p>软件中的模块化开发</p><p>从程序开发角度，模块化是一种开发模式，有以下两个特点:</p><ul><li><p>生产效率高：方便代码重用，别人开发好的模块功能可以直接拿过来使用，不需要重复开发类似的功能。</p></li><li><p>维护成本低：软件开发周期中，由于需求经常发生变化，最长的阶段并不是开发阶段，而是维护阶段，使用模块化开发的方式更容易维护。<br><img src="/../../../images/node02-18.png"></p></li></ul><h3 id="模块成员的导入和导出"><a href="#模块成员的导入和导出" class="headerlink" title="模块成员的导入和导出"></a><a href="#%E6%A8%A1%E5%9D%97%E6%88%90%E5%91%98%E7%9A%84%E5%AF%BC%E5%85%A5%E5%92%8C%E5%AF%BC%E5%87%BA"></a>模块成员的导入和导出</h3><h4 id="exports和require"><a href="#exports和require" class="headerlink" title="exports和require()"></a><a href="#exports%E5%92%8Crequire"></a>exports和require()</h4><p>在模块化开发中，<strong>一个JavaScript文件就是一个模块</strong>，模块内部定义的变量和函数默认情况下在外部无法得到。如何得到模块内部定义的变量和函数呢？</p><p>Node.js为开发者提供了一个简单的模块系统，<strong>exports是模块公开的接口</strong>，**require()**用于从外部获取一个模块的接口，即获取模块的exports对象。</p><p>如何在一个文件模块中获取其他文件模块的内容？</p><ul><li><p>首先需要使用require()方法加载模块；</p></li><li><p>然后在被加载的模块中使用exports或者module.exports对象向外开放变量、函数等；require()函数的作用是加载文件并获取该文件中的module.exports对象接口。</p></li></ul><blockquote></blockquote><p>演示如何在Node.js中进行模块成员的导入和导出<br>在chapter02\demo01目录下，新建info.js文件作为被加载模块。</p><p>&#x2F;&#x2F; 声明一个add()函数用来实现加法功能<br>const add &#x3D; (n1, n2) &#x3D;&gt; n1 + n2;<br>&#x2F;&#x2F; exports对象向模块外开放add()函数<br>exports.add &#x3D; add;</p><p>在demo01目录下新建b.js文件，实现在b.js模块中导入info.js模块。</p><p>&#x2F;&#x2F; 模块导入时，模块的后缀.js可以省略<br>const info &#x3D; require(&#x27;.&#x2F;info&#x27;);<br>&#x2F;&#x2F; 结果为：30 <br>console.log(info.add(10, 20));</p><p>打开命令行工具，切换到b.js文件所在的目录，并输入“node b.js”命令。<br><img src="/../../../images/node02-19.png"></p><p>总结Node.js的模块化开发的步骤：</p><ul><li><p>通过exports对象对模块内部的成员进行导出。</p></li><li><p>通过require()方法对依赖的模块进行导入操作。</p></li></ul><h4 id="module-exports"><a href="#module-exports" class="headerlink" title="module.exports"></a><a href="#module-exports"></a>module.exports</h4><h4 id="exports和module-exports的区别"><a href="#exports和module-exports的区别" class="headerlink" title="exports和module.exports的区别"></a><a href="#exports%E5%92%8Cmodule-exports%E7%9A%84%E5%8C%BA%E5%88%AB"></a>exports和module.exports的区别</h4><h4 id="ES6中的export和import"><a href="#ES6中的export和import" class="headerlink" title="ES6中的export和import"></a><a href="#ES6%E4%B8%AD%E7%9A%84export%E5%92%8Cimport"></a>ES6中的export和import</h4><h3 id="Node-js系统模块"><a href="#Node-js系统模块" class="headerlink" title="Node.js系统模块"></a><a href="#Node-js%E7%B3%BB%E7%BB%9F%E6%A8%A1%E5%9D%97"></a>Node.js系统模块</h3><h3 id="Node-js第三方模块"><a href="#Node-js第三方模块" class="headerlink" title="Node.js第三方模块"></a><a href="#Node-js%E7%AC%AC%E4%B8%89%E6%96%B9%E6%A8%A1%E5%9D%97"></a>Node.js第三方模块</h3><h3 id="Node-js常用开发工具"><a href="#Node-js常用开发工具" class="headerlink" title="Node.js常用开发工具"></a><a href="#Node-js%E5%B8%B8%E7%94%A8%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7"></a>Node.js常用开发工具</h3><h3 id="在项目中使用gulp"><a href="#在项目中使用gulp" class="headerlink" title="在项目中使用gulp"></a><a href="#%E5%9C%A8%E9%A1%B9%E7%9B%AE%E4%B8%AD%E4%BD%BF%E7%94%A8gulp"></a>在项目中使用gulp</h3><h3 id="项目依赖管理"><a href="#项目依赖管理" class="headerlink" title="项目依赖管理"></a><a href="#%E9%A1%B9%E7%9B%AE%E4%BE%9D%E8%B5%96%E7%AE%A1%E7%90%86"></a>项目依赖管理</h3><h3 id="Node-js模块加载机制"><a href="#Node-js模块加载机制" class="headerlink" title="Node.js模块加载机制"></a><a href="#Node-js%E6%A8%A1%E5%9D%97%E5%8A%A0%E8%BD%BD%E6%9C%BA%E5%88%B6"></a>Node.js模块加载机制</h3>]]>
    </content>
    <id>https://itdxb.cn/2024/09/04/node.js/document/02-Node.js%20%E6%A8%A1%E5%9D%97%E5%8C%96%E5%BC%80%E5%8F%91/</id>
    <link href="https://itdxb.cn/2024/09/04/node.js/document/02-Node.js%20%E6%A8%A1%E5%9D%97%E5%8C%96%E5%BC%80%E5%8F%91/"/>
    <published>2024-09-03T16:00:00.000Z</published>
    <summary>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" cla]]>
    </summary>
    <title>02-Node.js 模块化开发</title>
    <updated>2026-07-16T06:36:47.998Z</updated>
  </entry>
  <entry>
    <author>
      <name>Super Bing</name>
    </author>
    <category term="Vue" scheme="https://itdxb.cn/categories/Vue/"/>
    <category term="教程" scheme="https://itdxb.cn/tags/%E6%95%99%E7%A8%8B/"/>
    <category term="Vue" scheme="https://itdxb.cn/tags/Vue/"/>
    <content>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><h2 id="v-model-收集数据"><a href="#v-model-收集数据" class="headerlink" title="v-model 收集数据"></a><a href="#v-model-%E6%94%B6%E9%9B%86%E6%95%B0%E6%8D%AE"></a>v-model 收集数据</h2><p><code>v-model</code> 是 Vue.js 提供的一个非常强大的双向数据绑定指令，主要用于表单控件中，实现数据的自动收集和同步更新。它在处理表单输入时极大地简化了开发过程。</p><h3 id="使用v-model-双向数据绑定-自动收集数据"><a href="#使用v-model-双向数据绑定-自动收集数据" class="headerlink" title="使用v-model(双向数据绑定)自动收集数据"></a><a href="#%E4%BD%BF%E7%94%A8v-model-%E5%8F%8C%E5%90%91%E6%95%B0%E6%8D%AE%E7%BB%91%E5%AE%9A-%E8%87%AA%E5%8A%A8%E6%94%B6%E9%9B%86%E6%95%B0%E6%8D%AE"></a>使用v-model(双向数据绑定)自动收集数据</h3><p><code>v-model</code> 可以用于各种表单元素，如 <code>&amp;lt;input&amp;gt;</code>, <code>&amp;lt;textarea&amp;gt;</code>, <code>&amp;lt;select&amp;gt;</code>，并且会根据元素类型自动选用合适的绑定方式。</p><ul><li>v-model的三个修饰符</li></ul><p><code>lazy</code>：失去焦点再收集数据</p><ul><li><p><code>number</code>：输入字符串转为有效的数字</p></li><li><p><code>trim</code>：输入首尾空格过滤</p></li></ul><h4 id="text"><a href="#text" class="headerlink" title="text"></a><a href="#text"></a>text</h4><p>&lt;input type&#x3D;&quot;text&quot; v-model&#x3D;&quot;message&quot;&gt;</p><p>new Vue(&#123;<br> el: &#x27;#app&#x27;,<br> data: &#123;<br> message: &#x27;&#x27;<br> &#125;<br>&#125;);</p><blockquote></blockquote><p>当用户在输入框中输入内容时，<code>message</code> 数据会自动更新。</p><blockquote></blockquote><p>示例代码</p><p>&lt;!DOCTYPE html&gt;<br>&lt;html&gt;</p><p>&lt;head&gt;<br> &lt;meta charset&#x3D;&quot;UTF-8&quot; &#x2F;&gt;<br> &lt;title&gt;收集表单数据&lt;&#x2F;title&gt;<br> &lt;script src&#x3D;&quot;<a href="https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.js&quot;&gt;&lt;/script">https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.js&quot;&gt;&lt;/script</a>&gt;<br>&lt;&#x2F;head&gt;</p><p>&lt;body&gt;<br> &lt;!–<br> 收集表单数据：<br> 若：&lt;input type&#x3D;&quot;text&quot;&#x2F;&gt;，则v-model收集的是value值，用户输入的就是value值。<br> –&gt;<br> &lt;!– 准备好一个容器–&gt;<br> &lt;div id&#x3D;&quot;root&quot;&gt;<br> &lt;form @submit.prevent&#x3D;&quot;demo&quot;&gt;<br> 账号：&lt;input type&#x3D;&quot;text&quot; v-model.trim&#x3D;&quot;userInfo.account&quot;&gt; &lt;br &#x2F;&gt;&lt;br &#x2F;&gt;<br> 密码：&lt;input type&#x3D;&quot;password&quot; v-model&#x3D;&quot;userInfo.password&quot;&gt; &lt;br &#x2F;&gt;&lt;br &#x2F;&gt;<br> 年龄：&lt;input type&#x3D;&quot;number&quot; v-model.number&#x3D;&quot;userInfo.age&quot;&gt; &lt;br &#x2F;&gt;&lt;br &#x2F;&gt;<br> &lt;button&gt;提交&lt;&#x2F;button&gt;<br> &lt;&#x2F;form&gt;<br> &lt;&#x2F;div&gt;<br>&lt;&#x2F;body&gt;</p><p>&lt;script type&#x3D;&quot;text&#x2F;javascript&quot;&gt;<br> Vue.config.productionTip &#x3D; false</p><p> new Vue(&#123;<br> el: &#x27;#root&#x27;,<br> data: &#123;<br> userInfo: &#123;<br> account: &#x27;&#x27;,<br> password: &#x27;&#x27;,<br> age: 18<br> &#125;<br> &#125;<br> &#125;)<br>&lt;&#x2F;script&gt;</p><p>&lt;&#x2F;html&gt;</p><p><img src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e0d54330c7674aaebb740bf3a8bb6510~tplv-k3u1fbpfcp-zoom-in-crop-mark:1512:0:0:0.awebp"></p><h4 id="textarea"><a href="#textarea" class="headerlink" title="textarea"></a><a href="#textarea"></a>textarea</h4><p>&lt;textarea v-model&#x3D;&quot;message&quot;&gt;&lt;&#x2F;textarea&gt;</p><blockquote></blockquote><p><code>v-model</code> 也适用于 <code>&amp;lt;textarea&amp;gt;</code>，效果与 <code>&amp;lt;input&amp;gt;</code> 类似。</p><h4 id="radio"><a href="#radio" class="headerlink" title="radio"></a><a href="#radio"></a>radio</h4><p><code>&amp;lt;input type=&amp;quot;radio&amp;quot;/&amp;gt;</code>，则v-model收集的是value值，且要给标签配置value值。</p><p>&lt;input type&#x3D;&quot;radio&quot; v-model&#x3D;&quot;picked&quot; value&#x3D;&quot;Option 1&quot;&gt;<br>&lt;input type&#x3D;&quot;radio&quot; v-model&#x3D;&quot;picked&quot; value&#x3D;&quot;Option 2&quot;&gt;<br>&lt;p&gt;选中的选项: &#123;&#123; picked &#125;&#125;&lt;&#x2F;p&gt;</p><blockquote></blockquote><p>当用户选择不同的选项时，picked 会自动更新为对应的 value 值。</p><blockquote></blockquote><p>示例代码</p><p>&lt;!DOCTYPE html&gt;<br>&lt;html&gt;</p><p>&lt;head&gt;<br> &lt;meta charset&#x3D;&quot;UTF-8&quot; &#x2F;&gt;<br> &lt;title&gt;收集表单数据&lt;&#x2F;title&gt;<br> &lt;script src&#x3D;&quot;<a href="https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.js&quot;&gt;&lt;/script">https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.js&quot;&gt;&lt;/script</a>&gt;<br>&lt;&#x2F;head&gt;</p><p>&lt;body&gt;<br> &lt;!–<br> 收集表单数据：<br> 若：&lt;input type&#x3D;&quot;radio&quot;&#x2F;&gt;，则v-model收集的是value值，且要给标签配置value值。</p><p> –&gt;<br> &lt;!– 准备好一个容器–&gt;<br> &lt;div id&#x3D;&quot;root&quot;&gt;<br> &lt;form @submit.prevent&#x3D;&quot;demo&quot;&gt;<br> 账号：&lt;input type&#x3D;&quot;text&quot; v-model.trim&#x3D;&quot;userInfo.account&quot;&gt; &lt;br &#x2F;&gt;&lt;br &#x2F;&gt;<br> 密码：&lt;input type&#x3D;&quot;password&quot; v-model&#x3D;&quot;userInfo.password&quot;&gt; &lt;br &#x2F;&gt;&lt;br &#x2F;&gt;<br> 年龄：&lt;input type&#x3D;&quot;number&quot; v-model.number&#x3D;&quot;userInfo.age&quot;&gt; &lt;br &#x2F;&gt;&lt;br &#x2F;&gt;<br> 性别：<br> 男&lt;input type&#x3D;&quot;radio&quot; name&#x3D;&quot;sex&quot; v-model&#x3D;&quot;userInfo.sex&quot; value&#x3D;&quot;male&quot;&gt;<br> 女&lt;input type&#x3D;&quot;radio&quot; name&#x3D;&quot;sex&quot; v-model&#x3D;&quot;userInfo.sex&quot; value&#x3D;&quot;female&quot;&gt; &lt;br &#x2F;&gt;&lt;br &#x2F;&gt;<br> &lt;button&gt;提交&lt;&#x2F;button&gt;<br> &lt;&#x2F;form&gt;<br> &lt;&#x2F;div&gt;<br>&lt;&#x2F;body&gt;</p><p>&lt;script type&#x3D;&quot;text&#x2F;javascript&quot;&gt;<br> Vue.config.productionTip &#x3D; false</p><p> new Vue(&#123;<br> el: &#x27;#root&#x27;,<br> data: &#123;<br> userInfo: &#123;<br> account: &#x27;&#x27;,<br> password: &#x27;&#x27;,<br> age: 18,<br> sex: &#x27;female&#x27;<br> &#125;<br> &#125;<br> &#125;)<br>&lt;&#x2F;script&gt;</p><p>&lt;&#x2F;html&gt;</p><p><img src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a3380a04284a4310a0a98e2a7ec3c167~tplv-k3u1fbpfcp-zoom-in-crop-mark:1512:0:0:0.awebp"></p><h4 id="checkbox"><a href="#checkbox" class="headerlink" title="checkbox"></a><a href="#checkbox"></a>checkbox</h4><p><code>&amp;lt;input type=&amp;quot;checkbox&amp;quot;/&amp;gt;</code></p><p>&lt;input type&#x3D;&quot;checkbox&quot; v-model&#x3D;&quot;isChecked&quot;&gt;<br>&lt;p&gt;复选框状态: &#123;&#123; isChecked &#125;&#125;&lt;&#x2F;p&gt;</p><blockquote></blockquote><p><code>isChecked</code> 会根据复选框的勾选状态自动更新为 <code>true</code> 或 <code>false</code>。</p><p>&lt;input type&#x3D;&quot;checkbox&quot; v-model&#x3D;&quot;checkedNames&quot; value&#x3D;&quot;Jack&quot;&gt;<br>&lt;input type&#x3D;&quot;checkbox&quot; v-model&#x3D;&quot;checkedNames&quot; value&#x3D;&quot;John&quot;&gt;<br>&lt;input type&#x3D;&quot;checkbox&quot; v-model&#x3D;&quot;checkedNames&quot; value&#x3D;&quot;Mike&quot;&gt;<br>&lt;p&gt;选中的名字: &#123;&#123; checkedNames &#125;&#125;&lt;&#x2F;p&gt;</p><blockquote></blockquote><p><code>checkedNames</code> 是一个数组，包含所有被选中的选项。</p><p>没有配置<code>input</code>的<code>value</code>属性，那么收集的就是<code>checked</code>（勾选 or 未勾选，是布尔值）</p><p>配置<code>input</code>的<code>value</code>属性:</p><ul><li><p><code>v-model</code>的初始值是非数组，那么收集的就是<code>checked</code>（勾选 or 未勾选，是布尔值）</p></li><li><p><code>v-model</code>的初始值是数组，那么收集的的就是<code>value</code>组成的数组！！！</p></li></ul><blockquote></blockquote><p>示例代码</p><p>&lt;!DOCTYPE html&gt;<br>&lt;html&gt;</p><p>&lt;head&gt;<br> &lt;meta charset&#x3D;&quot;UTF-8&quot; &#x2F;&gt;<br> &lt;title&gt;收集表单数据&lt;&#x2F;title&gt;<br> &lt;script src&#x3D;&quot;<a href="https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.js&quot;&gt;&lt;/script">https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.js&quot;&gt;&lt;/script</a>&gt;<br>&lt;&#x2F;head&gt;</p><p>&lt;body&gt;<br> &lt;!–<br> 收集表单数据：<br> 若：&lt;input type&#x3D;&quot;text&quot;&#x2F;&gt;，则v-model收集的是value值，用户输入的就是value值。<br> 若：&lt;input type&#x3D;&quot;radio&quot;&#x2F;&gt;，则v-model收集的是value值，且要给标签配置value值。<br> 若：&lt;input type&#x3D;&quot;checkbox&quot;&#x2F;&gt;<br> 1.没有配置input的value属性，那么收集的就是checked（勾选 or 未勾选，是布尔值）<br> 2.配置input的value属性:<br> (1)v-model的初始值是非数组，那么收集的就是checked（勾选 or 未勾选，是布尔值）<br> (2)v-model的初始值是数组，那么收集的的就是value组成的数组<br> 备注：v-model的三个修饰符：<br> lazy：失去焦点再收集数据<br> number：输入字符串转为有效的数字<br> trim：输入首尾空格过滤<br> –&gt;<br> &lt;!– 准备好一个容器–&gt;<br> &lt;div id&#x3D;&quot;root&quot;&gt;<br> &lt;form @submit.prevent&#x3D;&quot;demo&quot;&gt;<br> 账号：&lt;input type&#x3D;&quot;text&quot; v-model.trim&#x3D;&quot;userInfo.account&quot;&gt; &lt;br &#x2F;&gt;&lt;br &#x2F;&gt;<br> 密码：&lt;input type&#x3D;&quot;password&quot; v-model&#x3D;&quot;userInfo.password&quot;&gt; &lt;br &#x2F;&gt;&lt;br &#x2F;&gt;<br> 年龄：&lt;input type&#x3D;&quot;number&quot; v-model.number&#x3D;&quot;userInfo.age&quot;&gt; &lt;br &#x2F;&gt;&lt;br &#x2F;&gt;<br> 性别：<br> 男&lt;input type&#x3D;&quot;radio&quot; name&#x3D;&quot;sex&quot; v-model&#x3D;&quot;userInfo.sex&quot; value&#x3D;&quot;male&quot;&gt;<br> 女&lt;input type&#x3D;&quot;radio&quot; name&#x3D;&quot;sex&quot; v-model&#x3D;&quot;userInfo.sex&quot; value&#x3D;&quot;female&quot;&gt; &lt;br &#x2F;&gt;&lt;br &#x2F;&gt;<br> 爱好：<br> 学习&lt;input type&#x3D;&quot;checkbox&quot; v-model&#x3D;&quot;userInfo.hobby&quot; value&#x3D;&quot;study&quot;&gt;<br> 打游戏&lt;input type&#x3D;&quot;checkbox&quot; v-model&#x3D;&quot;userInfo.hobby&quot; value&#x3D;&quot;game&quot;&gt;<br> 吃饭&lt;input type&#x3D;&quot;checkbox&quot; v-model&#x3D;&quot;userInfo.hobby&quot; value&#x3D;&quot;eat&quot;&gt;<br> &lt;br &#x2F;&gt;&lt;br &#x2F;&gt;<br> 所属校区<br> &lt;select v-model&#x3D;&quot;userInfo.city&quot;&gt;<br> &lt;option value&#x3D;&quot;&quot;&gt;请选择校区&lt;&#x2F;option&gt;<br> &lt;option value&#x3D;&quot;beijing&quot;&gt;北京&lt;&#x2F;option&gt;<br> &lt;option value&#x3D;&quot;shanghai&quot;&gt;上海&lt;&#x2F;option&gt;<br> &lt;option value&#x3D;&quot;shenzhen&quot;&gt;深圳&lt;&#x2F;option&gt;<br> &lt;option value&#x3D;&quot;wuhan&quot;&gt;武汉&lt;&#x2F;option&gt;<br> &lt;&#x2F;select&gt;<br> &lt;br &#x2F;&gt;&lt;br &#x2F;&gt;<br> 其他信息：<br> &lt;textarea v-model.lazy&#x3D;&quot;userInfo.other&quot;&gt;&lt;&#x2F;textarea&gt; &lt;br &#x2F;&gt;&lt;br &#x2F;&gt;<br> &lt;input type&#x3D;&quot;checkbox&quot; v-model&#x3D;&quot;userInfo.agree&quot;&gt;阅读并接受&lt;a href&#x3D;&quot;<a href="http://www.atguigu.com&quot;&gt;《用户协议》&lt;/a">http://www.atguigu.com&quot;&gt;《用户协议》&lt;/a</a>&gt;<br> &lt;button&gt;提交&lt;&#x2F;button&gt;<br> &lt;&#x2F;form&gt;<br> &lt;&#x2F;div&gt;<br>&lt;&#x2F;body&gt;</p><p>&lt;script type&#x3D;&quot;text&#x2F;javascript&quot;&gt;<br> Vue.config.productionTip &#x3D; false</p><p> new Vue(&#123;<br> el: &#x27;#root&#x27;,<br> data: &#123;<br> userInfo: &#123;<br> account: &#x27;&#x27;,<br> password: &#x27;&#x27;,<br> age: 18,<br> sex: &#x27;female&#x27;,<br> hobby: [],<br> city: &#x27;beijing&#x27;,<br> other: &#x27;&#x27;,<br> agree: &#x27;&#x27;<br> &#125;<br> &#125;,<br> methods: &#123;<br> demo() &#123;<br> console.log(JSON.stringify(this.userInfo))<br> &#125;<br> &#125;<br> &#125;)<br>&lt;&#x2F;script&gt;</p><p>&lt;&#x2F;html&gt;</p><p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/87d52db0789345b7889646226a69c1da~tplv-k3u1fbpfcp-zoom-in-crop-mark:1512:0:0:0.awebp"></p><h3 id="v-model-的原理与自动收集数据"><a href="#v-model-的原理与自动收集数据" class="headerlink" title="v-model 的原理与自动收集数据"></a><a href="#v-model-%E7%9A%84%E5%8E%9F%E7%90%86%E4%B8%8E%E8%87%AA%E5%8A%A8%E6%94%B6%E9%9B%86%E6%95%B0%E6%8D%AE"></a>v-model 的原理与自动收集数据</h3><p><code>v-model</code> 实现了表单控件的双向数据绑定，这意味着数据的改变会自动反映在视图上，用户的输入也会自动更新到数据中。</p><h4 id="双向绑定的工作原理"><a href="#双向绑定的工作原理" class="headerlink" title="双向绑定的工作原理:"></a><a href="#%E5%8F%8C%E5%90%91%E7%BB%91%E5%AE%9A%E7%9A%84%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86"></a>双向绑定的工作原理:</h4><ul><li><p><strong>绑定输入事件</strong>：当用户输入内容时，<code>v-model</code> 会监听 <code>input</code>, <code>change</code>, <code>blur</code> 等事件，并将最新的值更新到 <code>Vue</code> 实例的 <code>data</code> 中。</p></li><li><p><strong>视图更新</strong>：当 <code>data</code> 中的值发生变化时，<code>Vue</code> 的响应式系统会自动更新视图中的值，使之与数据保持同步。</p></li></ul><h4 id="表单数据的自动更新"><a href="#表单数据的自动更新" class="headerlink" title="表单数据的自动更新"></a><a href="#%E8%A1%A8%E5%8D%95%E6%95%B0%E6%8D%AE%E7%9A%84%E8%87%AA%E5%8A%A8%E6%9B%B4%E6%96%B0"></a>表单数据的自动更新</h4><p>通过 <code>v-model</code>，Vue 可以自动处理表单元素的数据绑定，使得表单数据与应用数据模型保持一致。</p><p>组合使用 v-model:</p><p>在复杂表单中，你可以为每个输入元素使用 <code>v-model</code> 来绑定数据，从而简化数据收集和表单提交的过程。</p><p>&lt;div id&#x3D;&quot;app&quot;&gt;<br> &lt;form @submit.prevent&#x3D;&quot;submitForm&quot;&gt;<br> &lt;label for&#x3D;&quot;name&quot;&gt;名字:&lt;&#x2F;label&gt;<br> &lt;input type&#x3D;&quot;text&quot; v-model&#x3D;&quot;formData.name&quot; id&#x3D;&quot;name&quot;&gt;</p><p> &lt;label for&#x3D;&quot;age&quot;&gt;年龄:&lt;&#x2F;label&gt;<br> &lt;input type&#x3D;&quot;number&quot; v-model&#x3D;&quot;formData.age&quot; id&#x3D;&quot;age&quot;&gt;</p><p> &lt;label for&#x3D;&quot;gender&quot;&gt;性别:&lt;&#x2F;label&gt;<br> &lt;select v-model&#x3D;&quot;formData.gender&quot; id&#x3D;&quot;gender&quot;&gt;<br> &lt;option value&#x3D;&quot;male&quot;&gt;男&lt;&#x2F;option&gt;<br> &lt;option value&#x3D;&quot;female&quot;&gt;女&lt;&#x2F;option&gt;<br> &lt;&#x2F;select&gt;</p><p> &lt;button type&#x3D;&quot;submit&quot;&gt;提交&lt;&#x2F;button&gt;<br> &lt;&#x2F;form&gt;</p><p> &lt;p&gt;提交的数据: &#123;&#123; formData &#125;&#125;&lt;&#x2F;p&gt;<br>&lt;&#x2F;div&gt;</p><p>&lt;script&gt;<br> new Vue(&#123;<br> el: &#x27;#app&#x27;,<br> data: &#123;<br> formData: &#123;<br> name: &#x27;&#x27;,<br> age: null,<br> gender: &#x27;&#x27;<br> &#125;<br> &#125;,<br> methods: &#123;<br> submitForm() &#123;<br> console.log(this.formData);<br> alert(&#x27;表单已提交&#x27;);<br> &#125;<br> &#125;<br> &#125;);<br>&lt;&#x2F;script&gt;</p><blockquote></blockquote><p>在这个例子中，<code>formData</code> 中的每个字段都与表单输入元素绑定，表单数据会自动收集，并在提交时打印到控制台。</p>]]>
    </content>
    <id>https://itdxb.cn/2024/08/26/vue/document/%E3%80%90Vue%E3%80%91%E5%9F%BA%E7%A1%80%E7%B3%BB%E5%88%97%EF%BC%88%E4%B8%83%EF%BC%89v-model%20-%20%E8%87%AA%E5%8A%A8%E6%94%B6%E9%9B%86%E6%95%B0%E6%8D%AE%20-%20%E8%A1%A8%E5%8D%95%E6%95%B0%E6%8D%AE%E8%87%AA%E5%8A%A8%E6%9B%B4%E6%96%B0/</id>
    <link href="https://itdxb.cn/2024/08/26/vue/document/%E3%80%90Vue%E3%80%91%E5%9F%BA%E7%A1%80%E7%B3%BB%E5%88%97%EF%BC%88%E4%B8%83%EF%BC%89v-model%20-%20%E8%87%AA%E5%8A%A8%E6%94%B6%E9%9B%86%E6%95%B0%E6%8D%AE%20-%20%E8%A1%A8%E5%8D%95%E6%95%B0%E6%8D%AE%E8%87%AA%E5%8A%A8%E6%9B%B4%E6%96%B0/"/>
    <published>2024-08-25T16:00:00.000Z</published>
    <summary>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" cla]]>
    </summary>
    <title>【Vue】基础系列（七）v-model - 自动收集数据 - 表单数据自动更新</title>
    <updated>2026-07-16T06:36:48.180Z</updated>
  </entry>
  <entry>
    <author>
      <name>Super Bing</name>
    </author>
    <category term="Vue" scheme="https://itdxb.cn/categories/Vue/"/>
    <category term="教程" scheme="https://itdxb.cn/tags/%E6%95%99%E7%A8%8B/"/>
    <category term="Vue" scheme="https://itdxb.cn/tags/Vue/"/>
    <content>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script>]]>
    </content>
    <id>https://itdxb.cn/2024/08/26/vue/document/%E3%80%90Vue%E3%80%91%E5%9F%BA%E7%A1%80%E7%B3%BB%E5%88%97%EF%BC%88%E5%85%AB%EF%BC%89%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F%20-%20%E5%88%9D%E5%A7%8B%E5%8C%96%E6%98%BE%E7%A4%BA%20-%20%E6%9B%B4%E6%96%B0%E7%8A%B6%E6%80%81%20-%20%E6%AD%BB%E4%BA%A1%E7%8A%B6%E6%80%81%20-%20%E7%88%B6%E5%AD%90%E7%BB%84%E4%BB%B6/</id>
    <link href="https://itdxb.cn/2024/08/26/vue/document/%E3%80%90Vue%E3%80%91%E5%9F%BA%E7%A1%80%E7%B3%BB%E5%88%97%EF%BC%88%E5%85%AB%EF%BC%89%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F%20-%20%E5%88%9D%E5%A7%8B%E5%8C%96%E6%98%BE%E7%A4%BA%20-%20%E6%9B%B4%E6%96%B0%E7%8A%B6%E6%80%81%20-%20%E6%AD%BB%E4%BA%A1%E7%8A%B6%E6%80%81%20-%20%E7%88%B6%E5%AD%90%E7%BB%84%E4%BB%B6/"/>
    <published>2024-08-25T16:00:00.000Z</published>
    <summary>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" cla]]>
    </summary>
    <title>【Vue】基础系列（八）生命周期 - 初始化显示 - 更新状态 - 死亡状态 - 父子组件</title>
    <updated>2026-07-16T06:36:48.362Z</updated>
  </entry>
  <entry>
    <author>
      <name>Super Bing</name>
    </author>
    <category term="Vue" scheme="https://itdxb.cn/categories/Vue/"/>
    <category term="教程" scheme="https://itdxb.cn/tags/%E6%95%99%E7%A8%8B/"/>
    <category term="Vue" scheme="https://itdxb.cn/tags/Vue/"/>
    <content>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><h2 id="列表渲染"><a href="#列表渲染" class="headerlink" title="列表渲染"></a><a href="#%E5%88%97%E8%A1%A8%E6%B8%B2%E6%9F%93"></a>列表渲染</h2><p>在 Vue.js 中，列表渲染是一个常用的功能，通常通过 <code>v-for</code> 指令来实现</p><h3 id="列表显示"><a href="#列表显示" class="headerlink" title="列表显示"></a><a href="#%E5%88%97%E8%A1%A8%E6%98%BE%E7%A4%BA"></a>列表显示</h3><p><code>v-for</code> 指令用于遍历数组或对象，并生成一组相应的 DOM 元素。例如：</p><p>&lt;ul&gt;<br> &lt;li v-for&#x3D;&quot;item in items&quot; :key&#x3D;&quot;item.id&quot;&gt;&#123;&#123; item.name &#125;&#125;&lt;&#x2F;li&gt;<br>&lt;&#x2F;ul&gt;</p><blockquote></blockquote><p>在这个例子中，<code>v-for</code> 遍历 <code>items</code> 数组，每个 <code>item</code> 的内容被插入到 li 元素中。每个列表项提供一个唯一的key属性，key应该是独一无二的，比如一个字符串或数字类型的ID</p><p>v-for 指令</p><ul><li><p>用于展示列表数据</p></li><li><p>语法：<code>v-for=&amp;quot;(item, index) in xxx&amp;quot; :key=&amp;quot;yyy&amp;quot;</code></p></li><li><p>可遍历：数组、对象、字符串（用的很少）、指定次数（用的很少）</p></li></ul><p>数组: <code>(item, index)</code></p><ul><li><p>对象: <code>(value, key)</code></p></li><li><p>字符串：<code>(char, index)</code></p></li><li><p>数字：<code>(number, index)</code></p></li></ul><p>&lt;!DOCTYPE html&gt;<br>&lt;html&gt;<br>&lt;head&gt;<br>&lt;meta charset&#x3D;&quot;UTF-8&quot; &#x2F;&gt;<br>&lt;title&gt;基本列表&lt;&#x2F;title&gt;<br>&lt;script type&#x3D;&quot;text&#x2F;javascript&quot; src&#x3D;&quot;..&#x2F;js&#x2F;vue.js&quot;&gt;&lt;&#x2F;script&gt;<br>&lt;&#x2F;head&gt;<br>&lt;body&gt;<br>&lt;!– 准备好一个容器–&gt;<br>&lt;div id&#x3D;&quot;root&quot;&gt;<br>&lt;!– 遍历数组 –&gt;<br>&lt;h2&gt;人员列表（遍历数组）&lt;&#x2F;h2&gt;<br>&lt;ul&gt;<br>&lt;li v-for&#x3D;&quot;(p,index) of persons&quot; :key&#x3D;&quot;index&quot;&gt;<br>&#123;&#123;p.name&#125;&#125;-&#123;&#123;p.age&#125;&#125;<br>&lt;&#x2F;li&gt;<br>&lt;&#x2F;ul&gt;</p><pre><code>&amp;lt;!-- 遍历对象 --&amp;gt;&amp;lt;h2&amp;gt;汽车信息（遍历对象）&amp;lt;/h2&amp;gt;&amp;lt;ul&amp;gt;&amp;lt;li v-for=&amp;quot;(value,k) of car&amp;quot; :key=&amp;quot;k&amp;quot;&amp;gt;&amp;#123;&amp;#123;k&amp;#125;&amp;#125;-&amp;#123;&amp;#123;value&amp;#125;&amp;#125;&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&amp;lt;!-- 遍历字符串 --&amp;gt;&amp;lt;h2&amp;gt;测试遍历字符串（用得少）&amp;lt;/h2&amp;gt;&amp;lt;ul&amp;gt;&amp;lt;li v-for=&amp;quot;(char,index) of str&amp;quot; :key=&amp;quot;index&amp;quot;&amp;gt;&amp;#123;&amp;#123;char&amp;#125;&amp;#125;-&amp;#123;&amp;#123;index&amp;#125;&amp;#125;&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&amp;lt;!-- 遍历指定次数 --&amp;gt;&amp;lt;h2&amp;gt;测试遍历指定次数（用得少）&amp;lt;/h2&amp;gt;&amp;lt;ul&amp;gt;&amp;lt;li v-for=&amp;quot;(number,index) of 5&amp;quot; :key=&amp;quot;index&amp;quot;&amp;gt;&amp;#123;&amp;#123;index&amp;#125;&amp;#125;-&amp;#123;&amp;#123;number&amp;#125;&amp;#125;&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;Vue.config.productionTip = falsenew Vue(&amp;#123;el:&amp;#x27;#root&amp;#x27;,data:&amp;#123;persons:[&amp;#123;id:&amp;#x27;001&amp;#x27;,name:&amp;#x27;张三&amp;#x27;,age:18&amp;#125;,&amp;#123;id:&amp;#x27;002&amp;#x27;,name:&amp;#x27;李四&amp;#x27;,age:19&amp;#125;,&amp;#123;id:&amp;#x27;003&amp;#x27;,name:&amp;#x27;王五&amp;#x27;,age:20&amp;#125;],car:&amp;#123;name:&amp;#x27;奥迪A8&amp;#x27;,price:&amp;#x27;70万&amp;#x27;,color:&amp;#x27;黑色&amp;#x27;&amp;#125;,str:&amp;#x27;hello&amp;#x27;&amp;#125;&amp;#125;)&amp;lt;/script&amp;gt;</code></pre><p>&lt;&#x2F;html&gt;</p><h3 id="key的原理"><a href="#key的原理" class="headerlink" title="key的原理"></a><a href="#key%E7%9A%84%E5%8E%9F%E7%90%86"></a>key的原理</h3><h4 id="虚拟DOM中key的作用："><a href="#虚拟DOM中key的作用：" class="headerlink" title="虚拟DOM中key的作用："></a><a href="#%E8%99%9A%E6%8B%9FDOM%E4%B8%ADkey%E7%9A%84%E4%BD%9C%E7%94%A8%EF%BC%9A"></a>虚拟DOM中key的作用：</h4><blockquote></blockquote><p>key是虚拟DOM对象的标识，当数据发生变化时，Vue会根据【新数据】生成【新的虚拟DOM】, 随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较，比较规则如下：</p><h4 id="对比规则："><a href="#对比规则：" class="headerlink" title="对比规则："></a><a href="#%E5%AF%B9%E6%AF%94%E8%A7%84%E5%88%99%EF%BC%9A"></a>对比规则：</h4><ul><li>旧虚拟DOM中找到了与新虚拟DOM相同的<code>key</code>：</li></ul><p>若虚拟DOM中内容没变, 直接使用之前的真实DOM</p><ul><li><p>若虚拟DOM中内容变了, 则生成新的真实DOM，随后替换掉页面中之前的真实DOM</p></li><li><p>旧虚拟DOM中未找到与新虚拟DOM相同的<code>key</code>创建新的真实DOM，随后渲染到到页面。</p></li></ul><h4 id="用index作为key可能会引发的问题："><a href="#用index作为key可能会引发的问题：" class="headerlink" title="用index作为key可能会引发的问题："></a><a href="#%E7%94%A8index%E4%BD%9C%E4%B8%BAkey%E5%8F%AF%E8%83%BD%E4%BC%9A%E5%BC%95%E5%8F%91%E7%9A%84%E9%97%AE%E9%A2%98%EF%BC%9A"></a>用<code>index</code>作为<code>key</code>可能会引发的问题：</h4><ul><li><p>若对数据进行：逆序添加、逆序删除等破坏顺序操作: 会产生没有必要的真实DOM更新 &#x3D;&#x3D;&gt; 界面效果没问题, 但效率低</p></li><li><p>如果结构中还包含输入类的DOM： 会产生错误DOM更新 &#x3D;&#x3D;&gt; 界面有问题</p></li></ul><h4 id="开发中如何选择key"><a href="#开发中如何选择key" class="headerlink" title="开发中如何选择key:"></a><a href="#%E5%BC%80%E5%8F%91%E4%B8%AD%E5%A6%82%E4%BD%95%E9%80%89%E6%8B%A9key"></a>开发中如何选择<code>key</code>:</h4><ul><li><p>最好使用每条数据的唯一标识作为<code>key</code>, 比如id、手机号、身份证号、学号等唯一值。</p></li><li><p>如果不存在对数据的逆序添加、逆序删除等破坏顺序操作，仅用于渲染列表用于展示，使用<code>index</code>作为<code>key</code>是没有问题的。</p></li></ul><h3 id="Vue监视数据的原理"><a href="#Vue监视数据的原理" class="headerlink" title="Vue监视数据的原理"></a><a href="#Vue%E7%9B%91%E8%A7%86%E6%95%B0%E6%8D%AE%E7%9A%84%E5%8E%9F%E7%90%86"></a>Vue监视数据的原理</h3><p>vue会监视data中所有层次的数据。</p><p>如何监测对象中的数据？</p><blockquote></blockquote><p>通过setter实现监视，且要在new Vue时就传入要监测的数据。</p><ul><li><p>对象中后追加的属性，Vue默认不做响应式处理</p></li><li><p>如需给后添加的属性做响应式，请使用如下API：<code>Vue.set(target，propertyName/index，value)</code> 或 <code>vm.$set(target，propertyName/index，value)</code></p></li></ul><p>如何监测数组中的数据？ 通过包裹数组更新元素的方法实现，本质就是做了两件事：<br>+　调用原生对应的方法对数组进行更新。</p><ul><li>重新解析模板，进而更新页面。</li></ul><p>在Vue修改数组中的某个元素一定要用如下方法：</p><ul><li><p>使用这些API: <code>push()</code>、<code>pop()</code>、<code>shift()</code>、<code>unshift()</code>、<code>splice()</code>、<code>sort()</code>、<code>reverse()</code></p></li><li><p><code>Vue.set()</code> 或 <code>vm.$set()</code></p></li></ul><blockquote></blockquote><p>特别注意：<code>Vue.set()</code> 和 <code>vm.$set()</code> 不能给 <code>vm</code> 或 <code>vm</code> 的根数据对象 添加属性！！！</p><p>&lt;!DOCTYPE html&gt;<br>&lt;html&gt;</p><p>&lt;head&gt;<br> &lt;meta charset&#x3D;&quot;UTF-8&quot; &#x2F;&gt;<br> &lt;title&gt;总结数据监视&lt;&#x2F;title&gt;<br> &lt;style&gt;<br> button &#123;<br> margin-top: 10px;<br> &#125;<br> &lt;&#x2F;style&gt;<br> &lt;!– 引入Vue –&gt;<br> &lt;script type&#x3D;&quot;text&#x2F;javascript&quot; src&#x3D;&quot;..&#x2F;js&#x2F;vue.js&quot;&gt;&lt;&#x2F;script&gt;<br>&lt;&#x2F;head&gt;</p><p>&lt;body&gt;<br> &lt;!–</p><p> –&gt;<br> &lt;!– 准备好一个容器–&gt;<br> &lt;div id&#x3D;&quot;root&quot;&gt;<br> &lt;h1&gt;学生信息&lt;&#x2F;h1&gt;<br> &lt;button @click&#x3D;&quot;student.age++&quot;&gt;年龄+1岁&lt;&#x2F;button&gt; &lt;br &#x2F;&gt;<br> &lt;button @click&#x3D;&quot;addSex&quot;&gt;添加性别属性，默认值：男&lt;&#x2F;button&gt; &lt;br &#x2F;&gt;<br> &lt;button @click&#x3D;&quot;student.sex &#x3D; &#x27;未知&#x27; &quot;&gt;修改性别&lt;&#x2F;button&gt; &lt;br &#x2F;&gt;<br> &lt;button @click&#x3D;&quot;addFriend&quot;&gt;在列表首位添加一个朋友&lt;&#x2F;button&gt; &lt;br &#x2F;&gt;<br> &lt;button @click&#x3D;&quot;updateFirstFriendName&quot;&gt;修改第一个朋友的名字为：张三&lt;&#x2F;button&gt; &lt;br &#x2F;&gt;<br> &lt;button @click&#x3D;&quot;addHobby&quot;&gt;添加一个爱好&lt;&#x2F;button&gt; &lt;br &#x2F;&gt;<br> &lt;button @click&#x3D;&quot;updateHobby&quot;&gt;修改第一个爱好为：开车&lt;&#x2F;button&gt; &lt;br &#x2F;&gt;<br> &lt;button @click&#x3D;&quot;removeSmoke&quot;&gt;过滤掉爱好中的抽烟&lt;&#x2F;button&gt; &lt;br &#x2F;&gt;<br> &lt;h3&gt;姓名：&#123;&#123;student.name&#125;&#125;&lt;&#x2F;h3&gt;<br> &lt;h3&gt;年龄：&#123;&#123;student.age&#125;&#125;&lt;&#x2F;h3&gt;<br> &lt;h3 v-if&#x3D;&quot;student.sex&quot;&gt;性别：&#123;&#123;student.sex&#125;&#125;&lt;&#x2F;h3&gt;<br> &lt;h3&gt;爱好：&lt;&#x2F;h3&gt;<br> &lt;ul&gt;<br> &lt;li v-for&#x3D;&quot;(h,index) in student.hobby&quot; :key&#x3D;&quot;index&quot;&gt;<br> &#123;&#123;h&#125;&#125;<br> &lt;&#x2F;li&gt;<br> &lt;&#x2F;ul&gt;<br> &lt;h3&gt;朋友们：&lt;&#x2F;h3&gt;<br> &lt;ul&gt;<br> &lt;li v-for&#x3D;&quot;(f,index) in student.friends&quot; :key&#x3D;&quot;index&quot;&gt;<br> &#123;&#123;f.name&#125;&#125;–&#123;&#123;f.age&#125;&#125;<br> &lt;&#x2F;li&gt;<br> &lt;&#x2F;ul&gt;<br> &lt;&#x2F;div&gt;<br>&lt;&#x2F;body&gt;</p><p>&lt;script type&#x3D;&quot;text&#x2F;javascript&quot;&gt;<br> Vue.config.productionTip &#x3D; false &#x2F;&#x2F;阻止 vue 在启动时生成生产提示。</p><p> const vm &#x3D; new Vue(&#123;<br> el: &#x27;#root&#x27;,<br> data: &#123;<br> student: &#123;<br> name: &#x27;tom&#x27;,<br> age: 18,<br> hobby: [&#x27;抽烟&#x27;, &#x27;喝酒&#x27;, &#x27;烫头&#x27;],<br> friends: [&#123;<br> name: &#x27;jerry&#x27;,<br> age: 35<br> &#125;,<br> &#123;<br> name: &#x27;tony&#x27;,<br> age: 36<br> &#125;<br> ]<br> &#125;<br> &#125;,<br> methods: &#123;<br> addSex() &#123;<br> &#x2F;&#x2F; Vue.set(this.student,&#x27;sex&#x27;,&#x27;男&#x27;)<br> this.$set(this.student, &#x27;sex&#x27;, &#x27;男&#x27;)<br> &#125;,<br> addFriend() &#123;<br> this.student.friends.unshift(&#123;<br> name: &#x27;jack&#x27;,<br> age: 70<br> &#125;)<br> &#125;,<br> updateFirstFriendName() &#123;<br> this.student.friends[0].name &#x3D; &#x27;张三&#x27;<br> &#125;,<br> addHobby() &#123;<br> this.student.hobby.push(&#x27;学习&#x27;)<br> &#125;,<br> updateHobby() &#123;<br> &#x2F;&#x2F; this.student.hobby.splice(0,1,&#x27;开车&#x27;)<br> &#x2F;&#x2F; Vue.set(this.student.hobby,0,&#x27;开车&#x27;)<br> this.$set(this.student.hobby, 0, &#x27;开车&#x27;)<br> &#125;,<br> removeSmoke() &#123;<br> this.student.hobby &#x3D; this.student.hobby.filter((h) &#x3D;&gt; &#123;<br> return h !&#x3D;&#x3D; &#x27;抽烟&#x27;<br> &#125;)<br> &#125;<br> &#125;<br> &#125;)<br>&lt;&#x2F;script&gt;</p><p>&lt;&#x2F;html&gt;</p><p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c58c0e8fc9ac407c9126b171d7362ca4~tplv-k3u1fbpfcp-zoom-in-crop-mark:1512:0:0:0.awebp"></p><h3 id="数组更新检测"><a href="#数组更新检测" class="headerlink" title="数组更新检测"></a><a href="#%E6%95%B0%E7%BB%84%E6%9B%B4%E6%96%B0%E6%A3%80%E6%B5%8B"></a>数组更新检测</h3><p>Vue 重写了数组中的一系列改变数组内部数据的方法（先调用原生，再更新界面）</p><p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b00184c5443b4f8d981fb822d80066d3~tplv-k3u1fbpfcp-zoom-in-crop-mark:1512:0:0:0.awebp"></p><p>this.persons[index] &#x3D; newP;<br>&#x2F;&#x2F;并没有改变persons本身，数组内部发生了变化，但是没有调用变异方法，vue不会更新界面</p><p>也可以替换item</p><p>相比之下，也有非变更方法，例如 <code>filter()</code>、<code>concat()</code> 和 <code>slice()</code>。它们不会变更原始数组，而总是返回一个新数组。当使用非变更方法时，可以用新数组替换旧数组。</p><p>let fpersons &#x3D; persons.filter(<br> p &#x3D;&gt; p.name.includes(searchName)<br>)</p><p>&lt;body&gt;<br> &lt;div id&#x3D;&quot;demo&quot;&gt;<br> &lt;h2&gt;测试：v-for遍历数组&lt;&#x2F;h2&gt;<br> &lt;ul&gt;<br> &lt;li v-for&#x3D;&quot;(p, index) in persons&quot; :key&#x3D;&quot;index&quot;&gt;<br> &#123;&#123;index&#125;&#125;—&#123;&#123;p.name&#125;&#125;—&#123;&#123;p.age&#125;&#125;<br> &lt;button @click&#x3D;&quot;deleteP(index)&quot;&gt;删除&lt;&#x2F;button&gt;<br> &lt;button @click&#x3D;&quot;updateP(index, &#123;name:&#x27;Cat&#x27;, age: 20&#125;)&quot;&gt;更新&lt;&#x2F;button&gt;<br> &lt;&#x2F;li&gt;<br> &lt;&#x2F;ul&gt;</p><p> &lt;h2&gt;测试：v-for遍历对象&lt;&#x2F;h2&gt;<br> &lt;ul&gt;<br> &lt;li v-for&#x3D;&quot;(value, key) in persons[1]&quot; :key&#x3D;&quot;key&quot;&gt;<br> &#123;&#123;value&#125;&#125;—&#123;&#123;key&#125;&#125;<br> &lt;&#x2F;li&gt;<br> &lt;&#x2F;ul&gt;</p><p> &lt;&#x2F;div&gt;</p><p> &lt;script src&#x3D;&quot;..&#x2F;js&#x2F;vue.js&quot;&gt;&lt;&#x2F;script&gt;<br> &lt;script&gt;<br> &#x2F;&#x2F; Vue本身只是监视了persons的改变，没有监视数组内部数据的改变<br> &#x2F;&#x2F; Vue 重写了数组中的一系列改变数组内部数据的方法（先调用原生，再更新界面）<br> new Vue(&#123;<br> el: &#x27;#demo&#x27;,<br> data: &#123;<br> persons: [&#123;<br> name: &#x27;Tom&#x27;,<br> age: 18<br> &#125;,<br> &#123;<br> name: &#x27;Jack&#x27;,<br> age: 19<br> &#125;,<br> &#123;<br> name: &#x27;Marry&#x27;,<br> age: 16<br> &#125;,<br> &#123;<br> name: &#x27;Rose&#x27;,<br> age: 12<br> &#125;,<br> ]<br> &#125;,<br> methods: &#123;<br> deleteP(index) &#123;<br> &#x2F;&#x2F; 删除persons中指定idnex的p（有数据绑定）<br> this.persons.splice(index, 1);<br> &#125;,<br> updateP(index, newP) &#123;<br> &#x2F;&#x2F; this.persons[index] &#x3D; newP; &#x2F;&#x2F;数据（数组内部）变了，界面没有变化（没有数据绑定）<br> &#x2F;&#x2F;并没有改变persons本身，数组内部发生了变化，但是没有调用变异方法，vue不会更新界面<br> &#x2F;&#x2F; this.persons &#x3D; [] &#x2F;&#x2F;界面有变化，改变了persons<br> this.persons.splice(index, 1, newP);<br> &#125;<br> &#125;<br> &#125;)<br> &lt;&#x2F;script&gt;<br>&lt;&#x2F;body&gt;</p><p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/2547c14f20d84f8ab5124403c8dda069~tplv-k3u1fbpfcp-zoom-in-crop-mark:1512:0:0:0.awebp"></p><h3 id="过滤与排序"><a href="#过滤与排序" class="headerlink" title="过滤与排序"></a><a href="#%E8%BF%87%E6%BB%A4%E4%B8%8E%E6%8E%92%E5%BA%8F"></a>过滤与排序</h3><p>过滤操作</p><p>可以使用watch也可以使用计算属性，使用计算属性更加简单方便一点</p><p>&lt;!DOCTYPE html&gt;<br>&lt;html&gt;</p><p>&lt;head&gt;<br> &lt;meta charset&#x3D;&quot;UTF-8&quot; &#x2F;&gt;<br> &lt;title&gt;列表过滤&lt;&#x2F;title&gt;<br> &lt;script type&#x3D;&quot;text&#x2F;javascript&quot; src&#x3D;&quot;..&#x2F;js&#x2F;vue.js&quot;&gt;&lt;&#x2F;script&gt;<br>&lt;&#x2F;head&gt;</p><p>&lt;body&gt;<br> &lt;!– 准备好一个容器–&gt;<br> &lt;div id&#x3D;&quot;root&quot;&gt;<br> &lt;h2&gt;人员列表&lt;&#x2F;h2&gt;<br> &lt;input type&#x3D;&quot;text&quot; placeholder&#x3D;&quot;请输入名字&quot; v-model&#x3D;&quot;keyWord&quot;&gt;<br> &lt;ul&gt;<br> &lt;li v-for&#x3D;&quot;(p,index) of filPerons&quot; :key&#x3D;&quot;index&quot;&gt;<br> &#123;&#123;p.name&#125;&#125;-&#123;&#123;p.age&#125;&#125;-&#123;&#123;p.sex&#125;&#125;<br> &lt;&#x2F;li&gt;<br> &lt;&#x2F;ul&gt;<br> &lt;&#x2F;div&gt;</p><p> &lt;script type&#x3D;&quot;text&#x2F;javascript&quot;&gt;<br> Vue.config.productionTip &#x3D; false</p><p> &#x2F;&#x2F;用watch实现<br> &#x2F;&#x2F;#region<br> &#x2F;* new Vue(&#123;<br> el:&#x27;#root&#x27;,<br> data:&#123;<br> keyWord:&#x27;&#x27;,<br> persons:[<br> &#123;id:&#x27;001&#x27;,name:&#x27;马冬梅&#x27;,age:19,sex:&#x27;女&#x27;&#125;,<br> &#123;id:&#x27;002&#x27;,name:&#x27;周冬雨&#x27;,age:20,sex:&#x27;女&#x27;&#125;,<br> &#123;id:&#x27;003&#x27;,name:&#x27;周杰伦&#x27;,age:21,sex:&#x27;男&#x27;&#125;,<br> &#123;id:&#x27;004&#x27;,name:&#x27;温兆伦&#x27;,age:22,sex:&#x27;男&#x27;&#125;<br> ],<br> filPerons:[]<br> &#125;,<br> watch:&#123;<br> keyWord:&#123;<br> immediate:true,<br> handler(val)&#123;<br> this.filPerons &#x3D; this.persons.filter((p)&#x3D;&gt;&#123;<br> return p.name.indexOf(val) !&#x3D;&#x3D; -1<br> &#125;)<br> &#125;<br> &#125;<br> &#125;<br> &#125;) *&#x2F;<br> &#x2F;&#x2F;#endregion</p><p> &#x2F;&#x2F;用computed实现<br> new Vue(&#123;<br> el: &#x27;#root&#x27;,<br> data: &#123;<br> keyWord: &#x27;&#x27;,<br> persons: [&#123;<br> id: &#x27;001&#x27;,<br> name: &#x27;马冬梅&#x27;,<br> age: 19,<br> sex: &#x27;女&#x27;<br> &#125;,<br> &#123;<br> id: &#x27;002&#x27;,<br> name: &#x27;周冬雨&#x27;,<br> age: 20,<br> sex: &#x27;女&#x27;<br> &#125;,<br> &#123;<br> id: &#x27;003&#x27;,<br> name: &#x27;周杰伦&#x27;,<br> age: 21,<br> sex: &#x27;男&#x27;<br> &#125;,<br> &#123;<br> id: &#x27;004&#x27;,<br> name: &#x27;温兆伦&#x27;,<br> age: 22,<br> sex: &#x27;男&#x27;<br> &#125;<br> ]<br> &#125;,<br> computed: &#123;<br> filPerons() &#123;<br> return this.persons.filter((p) &#x3D;&gt; &#123;<br> return p.name.indexOf(this.keyWord) !&#x3D;&#x3D; -1<br> &#125;)<br> &#125;<br> &#125;<br> &#125;)<br> &lt;&#x2F;script&gt;</p><p>&lt;&#x2F;html&gt;</p><p>排序操作</p><p>computed:&#123;<br> filPerons()&#123;<br> const arr &#x3D; this.persons.filter((p)&#x3D;&gt;&#123;<br> return p.name.indexOf(this.keyWord) !&#x3D;&#x3D; -1<br> &#125;)<br> &#x2F;&#x2F;判断一下是否需要排序<br> if(this.sortType)&#123;<br> arr.sort((p1,p2)&#x3D;&gt;&#123;<br> return this.sortType &#x3D;&#x3D;&#x3D; 1 ? p2.age-p1.age : p1.age-p2.age<br> &#125;)<br> &#125;<br> return arr<br> &#125;<br>&#125;</p><blockquote></blockquote><p>例子</p><p>&lt;body&gt;<br> &lt;div id&#x3D;&quot;test&quot;&gt;<br> &lt;input type&#x3D;&quot;text&quot; v-model&#x3D;&quot;searchName&quot;&gt;<br> &lt;ul&gt;<br> &lt;li v-for&#x3D;&quot;(p, index) in filterPersons&quot; :key&#x3D;&quot;index&quot;&gt;<br> &#123;&#123;index&#125;&#125;—&#123;&#123;p.name&#125;&#125;—&#123;&#123;p.age&#125;&#125;<br> &lt;&#x2F;li&gt;<br> &lt;&#x2F;ul&gt;<br> &lt;button @click&#x3D;&quot;setOrderType(1)&quot;&gt;年龄升序&lt;&#x2F;button&gt;<br> &lt;button @click&#x3D;&quot;setOrderType(2)&quot;&gt;年龄降序&lt;&#x2F;button&gt;<br> &lt;button @click&#x3D;&quot;setOrderType(0)&quot;&gt;还原顺序&lt;&#x2F;button&gt;<br> &lt;&#x2F;div&gt;</p><p> &lt;script src&#x3D;&quot;..&#x2F;js&#x2F;vue.js&quot;&gt;&lt;&#x2F;script&gt;</p><p> &lt;script&gt;<br> new Vue(&#123;<br> el: &#x27;#test&#x27;,<br> data: &#123;<br> searchName: &#x27;&#x27;,<br> orderType: 0, &#x2F;&#x2F;0代表原本， 1代表升序， 2代表降序<br> persons: [&#123;<br> name: &#x27;Tom&#x27;,<br> age: 10<br> &#125;,<br> &#123;<br> name: &#x27;Jack&#x27;,<br> age: 16<br> &#125;,<br> &#123;<br> name: &#x27;Rose&#x27;,<br> age: 12<br> &#125;,<br> &#123;<br> name: &#x27;Aka&#x27;,<br> age: 18<br> &#125;<br> ]<br> &#125;,<br> computed: &#123;<br> filterPersons() &#123;<br> &#x2F;&#x2F; 1. 取出相关数据<br> const &#123;<br> searchName,<br> persons,<br> orderType<br> &#125; &#x3D; this; &#x2F;&#x2F; 解构赋值</p><p> let fPersons;</p><p> &#x2F;&#x2F; 2. 对persons进行过滤<br> fPersons &#x3D; persons.filter(p &#x3D;&gt; p.name.indexOf(searchName) !&#x3D;&#x3D; -1);</p><p> &#x2F;&#x2F; 3. 排序<br> if (orderType !&#x3D;&#x3D; 0) &#123;<br> fPersons.sort(function (p1, p2) &#123; &#x2F;&#x2F; 返回负数p1在前，返回正数p2在前<br> if (orderType &#x3D;&#x3D;&#x3D; 2) &#123;<br> return p2.age - p1.age; &#x2F;&#x2F; 降序<br> &#125; else &#123;<br> return p1.age - p2.age; &#x2F;&#x2F; 升序<br> &#125;<br> &#125;)<br> &#125;<br> return fPersons;<br> &#125;<br> &#125;,<br> methods: &#123;<br> setOrderType(orderType) &#123;<br> this.orderType &#x3D; orderType;<br> &#125;<br> &#125;<br> &#125;)<br> &lt;&#x2F;script&gt;<br>&lt;&#x2F;body&gt;</p><p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b738000369ee4b88bb1aafd216064946~tplv-k3u1fbpfcp-zoom-in-crop-mark:1512:0:0:0.awebp"></p>]]>
    </content>
    <id>https://itdxb.cn/2024/08/26/vue/document/%E3%80%90Vue%E3%80%91%E5%9F%BA%E7%A1%80%E7%B3%BB%E5%88%97%EF%BC%88%E4%BA%94%EF%BC%89%E5%88%97%E8%A1%A8%E6%B8%B2%E6%9F%93v-for%E3%80%81key%E7%9A%84%E5%8E%9F%E7%90%86%E3%80%81%E5%88%A0%E9%99%A4%E6%9B%BF%E6%8D%A2%E3%80%81%E8%BF%87%E6%BB%A4%E4%B8%8E%E6%8E%92%E5%BA%8F/</id>
    <link href="https://itdxb.cn/2024/08/26/vue/document/%E3%80%90Vue%E3%80%91%E5%9F%BA%E7%A1%80%E7%B3%BB%E5%88%97%EF%BC%88%E4%BA%94%EF%BC%89%E5%88%97%E8%A1%A8%E6%B8%B2%E6%9F%93v-for%E3%80%81key%E7%9A%84%E5%8E%9F%E7%90%86%E3%80%81%E5%88%A0%E9%99%A4%E6%9B%BF%E6%8D%A2%E3%80%81%E8%BF%87%E6%BB%A4%E4%B8%8E%E6%8E%92%E5%BA%8F/"/>
    <published>2024-08-25T16:00:00.000Z</published>
    <summary>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" cla]]>
    </summary>
    <title>【Vue】基础系列（五）列表渲染v-for、key的原理、删除替换、过滤与排序</title>
    <updated>2026-07-16T06:36:48.317Z</updated>
  </entry>
  <entry>
    <author>
      <name>Super Bing</name>
    </author>
    <category term="Vue" scheme="https://itdxb.cn/categories/Vue/"/>
    <category term="教程" scheme="https://itdxb.cn/tags/%E6%95%99%E7%A8%8B/"/>
    <category term="Vue" scheme="https://itdxb.cn/tags/Vue/"/>
    <content>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><h2 id="事件处理"><a href="#事件处理" class="headerlink" title="事件处理"></a><a href="#%E4%BA%8B%E4%BB%B6%E5%A4%84%E7%90%86"></a>事件处理</h2><p>在 Vue.js 中，事件处理是响应用户交互的核心功能之一。通过绑定事件监听器、使用事件修饰符和按键修饰符，可以轻松实现灵活的交互逻辑。</p><h3 id="绑定监听"><a href="#绑定监听" class="headerlink" title="绑定监听"></a><a href="#%E7%BB%91%E5%AE%9A%E7%9B%91%E5%90%AC"></a>绑定监听</h3><p>在 Vue 中，通过 v-on 指令或其缩写 @ 来绑定事件监听器。例如：</p><p>&lt;button @click&#x3D;&quot;handleClick&quot;&gt;点击我&lt;&#x2F;button&gt;</p><p>在这个例子中，当用户点击按钮时，会触发 handleClick 方法。</p><p>methods: &#123;<br> handleClick() &#123;<br> alert(&#x27;按钮被点击了&#x27;);<br> &#125;<br>&#125;</p><h4 id="事件的基本使用："><a href="#事件的基本使用：" class="headerlink" title="事件的基本使用："></a><a href="#%E4%BA%8B%E4%BB%B6%E7%9A%84%E5%9F%BA%E6%9C%AC%E4%BD%BF%E7%94%A8%EF%BC%9A"></a>事件的基本使用：</h4><ul><li>使用 <code>v-on:xxx</code> 或 <code>@xxx</code> 绑定事件，其中<code>xxx</code>是事件名；</li></ul><p>例如：<code>v-on:click</code> 或 <code>@click</code></p><ul><li><p>事件的回调需要配置在<code>methods</code>对象中，最终会在<code>vm</code>上</p></li><li><p><code>methods</code>中配置的函数，不要用箭头函数！否则<code>this</code>就不是<code>vm</code>了</p></li><li><p><code>methods</code>中配置的函数，都是被<code>Vue</code>所管理的函数，<code>this</code>的指向是<code>vm</code> 或 组件实例对象；</p></li><li><p><code>@click=&amp;quot;demo&amp;quot;</code> 和 <code>@click=&amp;quot;demo($event)&amp;quot;</code> 效果一致，但后者可以传参；</p></li></ul><p>事件对象 默认事件形参: <code>event</code> 隐含属性对象: <code>$event</code></p><ul><li><code>$event</code> 就是当前触发事件的元素，即使不传 <code>$event</code>，在回调函数中也可以使用 <code>event</code> 这个参数。</li></ul><p>v-on:xxx&#x3D;&quot;fun&quot;<br>@xxx&#x3D;&quot;fun&quot;<br>@xxx&#x3D;&quot;fun(参数)&quot;</p><blockquote></blockquote><p>示例代码</p><p>&lt;body&gt;<br> &lt;div id&#x3D;&quot;demo&quot;&gt;<br> &lt;h1&gt;1. 绑定监听&lt;&#x2F;h1&gt;<br> &lt;button @click&#x3D;&quot;test1&quot;&gt;test1&lt;&#x2F;button&gt;<br> &lt;button @click&#x3D;&quot;test2(&#x27;abc&#x27;)&quot;&gt;test2&lt;&#x2F;button&gt;<br> &lt;button @click&#x3D;&quot;test3&quot;&gt;test3&lt;&#x2F;button&gt;<br> &lt;button @click&#x3D;&quot;test4(123, $event)&quot;&gt;test4&lt;&#x2F;button&gt;<br> &lt;&#x2F;div&gt;<br> &lt;script src&#x3D;&quot;<a href="https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.js&quot;&gt;&lt;/script">https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.js&quot;&gt;&lt;/script</a>&gt;</p><p> &lt;script&gt;<br> new Vue(&#123;<br> el: &quot;#demo&quot;,<br> data: &#123;&#125;,<br> methods: &#123;<br> test1() &#123;<br> alert(&quot;hahah&quot;);<br> &#125;,<br> test2(msg) &#123;<br> alert(msg);<br> &#125;,<br> test3(event) &#123;<br> alert(event.target.innerHTML);<br> &#125;,<br> test4(number, event) &#123;<br> alert(number + &#x27;—&#x27; + event.target.innerHTML);<br> &#125;<br> &#125;<br> &#125;)<br> &lt;&#x2F;script&gt;<br>&lt;&#x2F;body&gt;</p><p><img src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/2b7990465aa44db2bc3bf1330c856f0c~tplv-k3u1fbpfcp-zoom-in-crop-mark:1512:0:0:0.awebp"></p><h3 id="事件修饰符"><a href="#事件修饰符" class="headerlink" title="事件修饰符"></a><a href="#%E4%BA%8B%E4%BB%B6%E4%BF%AE%E9%A5%B0%E7%AC%A6"></a>事件修饰符</h3><p>事件修饰符允许你修改事件处理程序的默认行为。这些修饰符包括 <code>.stop</code>, <code>.prevent</code>, <code>.capture</code>, <code>.self</code>, <code>.once</code> 等。</p><ul><li><p><code>prevent</code>：阻止默认事件（常用）；<code>event.preventDefault()</code></p></li><li><p><code>stop</code>：阻止事件冒泡（常用）；<code>event.stopPropagation()</code></p></li><li><p><code>once</code>：事件只触发一次（常用）；</p></li><li><p><code>capture</code>：使用事件的捕获模式；</p></li><li><p><code>self</code>：只有<code>event.target</code>是当前操作的元素时才触发事件； </p></li><li><p><code>passive</code>：事件的默认行为立即执行，无需等待事件回调执行完毕；</p></li></ul><p>&lt;!DOCTYPE html&gt;<br>&lt;html&gt;<br>&lt;head&gt;<br>&lt;meta charset&#x3D;&quot;UTF-8&quot; &#x2F;&gt;<br>&lt;title&gt;事件修饰符&lt;&#x2F;title&gt;<br>&lt;!– 引入Vue –&gt;<br>&lt;script type&#x3D;&quot;text&#x2F;javascript&quot; src&#x3D;&quot;..&#x2F;js&#x2F;vue.js&quot;&gt;&lt;&#x2F;script&gt;<br>&lt;style&gt;<br>*&#123;<br>margin-top: 20px;<br>&#125;<br>.demo1&#123;<br>height: 50px;<br>background-color: skyblue;<br>&#125;<br>.box1&#123;<br>padding: 5px;<br>background-color: skyblue;<br>&#125;<br>.box2&#123;<br>padding: 5px;<br>background-color: orange;<br>&#125;<br>.list&#123;<br>width: 200px;<br>height: 200px;<br>background-color: peru;<br>overflow: auto;<br>&#125;<br>li&#123;<br>height: 100px;<br>&#125;<br>&lt;&#x2F;style&gt;<br>&lt;&#x2F;head&gt;<br>&lt;body&gt;<br>&lt;!–<br>Vue中的事件修饰符：<br>1.prevent：阻止默认事件（常用）；<br>2.stop：阻止事件冒泡（常用）；<br>3.once：事件只触发一次（常用）；<br>4.capture：使用事件的捕获模式；<br>5.self：只有event.target是当前操作的元素时才触发事件；<br>6.passive：事件的默认行为立即执行，无需等待事件回调执行完毕；<br>–&gt;<br>&lt;!– 准备好一个容器–&gt;<br>&lt;div id&#x3D;&quot;root&quot;&gt;<br>&lt;h2&gt;欢迎来到&#123;&#123;name&#125;&#125;学习&lt;&#x2F;h2&gt;<br>&lt;!– 阻止默认事件（常用） –&gt;<br>&lt;a href&#x3D;&quot;<a href="http://www.baidu.com/">http://www.baidu.com</a>&quot; @click.prevent&#x3D;&quot;showInfo&quot;&gt;点我提示信息&lt;&#x2F;a&gt;</p><pre><code>&amp;lt;!-- 阻止事件冒泡（常用） 这将阻止点击事件冒泡到父元素。--&amp;gt;&amp;lt;div class=&amp;quot;demo1&amp;quot; @click=&amp;quot;showInfo&amp;quot;&amp;gt;&amp;lt;button @click.stop=&amp;quot;showInfo&amp;quot;&amp;gt;点我提示信息&amp;lt;/button&amp;gt;&amp;lt;!-- 修饰符可以连续写 --&amp;gt;&amp;lt;!-- &amp;lt;a href=&amp;quot;http://www.atguigu.com&amp;quot; @click.prevent.stop=&amp;quot;showInfo&amp;quot;&amp;gt;点我提示信息&amp;lt;/a&amp;gt; --&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;!-- 事件只触发一次（常用） --&amp;gt;&amp;lt;button @click.once=&amp;quot;showInfo&amp;quot;&amp;gt;点我提示信息&amp;lt;/button&amp;gt;&amp;lt;!-- 使用事件的捕获模式 --&amp;gt;&amp;lt;div class=&amp;quot;box1&amp;quot; @click.capture=&amp;quot;showMsg(1)&amp;quot;&amp;gt;div1&amp;lt;div class=&amp;quot;box2&amp;quot; @click=&amp;quot;showMsg(2)&amp;quot;&amp;gt;div2&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;!-- 只有event.target是当前操作的元素时才触发事件； --&amp;gt;&amp;lt;div class=&amp;quot;demo1&amp;quot; @click.self=&amp;quot;showInfo&amp;quot;&amp;gt;&amp;lt;button @click=&amp;quot;showInfo&amp;quot;&amp;gt;点我提示信息&amp;lt;/button&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;!-- 事件的默认行为立即执行，无需等待事件回调执行完毕； --&amp;gt;&amp;lt;ul @wheel.passive=&amp;quot;demo&amp;quot; class=&amp;quot;list&amp;quot;&amp;gt;&amp;lt;li&amp;gt;1&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;2&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;3&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;4&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。new Vue(&amp;#123;el:&amp;#x27;#root&amp;#x27;,data:&amp;#123;name:&amp;#x27;super&amp;#x27;&amp;#125;,methods:&amp;#123;showInfo(e)&amp;#123;alert(&amp;#x27;同学你好！&amp;#x27;)// console.log(e.target)&amp;#125;,showMsg(msg)&amp;#123;console.log(msg)&amp;#125;,demo()&amp;#123;for (let i = 0; i &amp;lt; 100000; i++) &amp;#123;console.log(&amp;#x27;#&amp;#x27;)&amp;#125;console.log(&amp;#x27;累坏了&amp;#x27;)&amp;#125;&amp;#125;&amp;#125;)&amp;lt;/script&amp;gt;</code></pre><p>&lt;&#x2F;html&gt;</p><h3 id="按键修饰符"><a href="#按键修饰符" class="headerlink" title="按键修饰符"></a><a href="#%E6%8C%89%E9%94%AE%E4%BF%AE%E9%A5%B0%E7%AC%A6"></a>按键修饰符</h3><p>Vue 提供了按键修饰符，用于监听键盘事件，并指定响应的按键。这些修饰符包括 <code>.enter</code>, <code>.tab</code>, <code>.delete</code>, <code>.esc</code>, <code>.space</code>, <code>.up</code>, <code>.down</code>, <code>.left</code>, <code>.right</code> 等。</p><h4 id="Vue中常用的按键别名："><a href="#Vue中常用的按键别名：" class="headerlink" title="Vue中常用的按键别名："></a><a href="#Vue%E4%B8%AD%E5%B8%B8%E7%94%A8%E7%9A%84%E6%8C%89%E9%94%AE%E5%88%AB%E5%90%8D%EF%BC%9A"></a>Vue中常用的按键别名：</h4><p>回车 &#x3D;&gt; enter<br>删除 &#x3D;&gt; delete (捕获“删除”和“退格”键)<br>退出 &#x3D;&gt; esc<br>空格 &#x3D;&gt; space<br>换行 &#x3D;&gt; tab (特殊，必须配合keydown去使用)<br>上 &#x3D;&gt; up<br>下 &#x3D;&gt; down<br>左 &#x3D;&gt; left<br>右 &#x3D;&gt; right</p><p>Vue未提供别名的按键，可以使用按键原始的key值去绑定，但注意要转为kebab-case（短横线命名）</p><p>系统修饰键（用法特殊）：<code>ctrl</code>、<code>alt</code>、<code>shift</code>、<code>meta</code></p><ul><li><p>配合<code>keyup</code>使用：按下修饰键的同时，再按下其他键，随后释放其他键，事件才被触发。</p></li><li><p>配合<code>keydown</code>使用：正常触发事件。</p></li></ul><p>也可以使用keyCode去指定具体的按键（不推荐）</p><p><code>Vue.config.keyCodes.自定义键名 = 键码</code>，可以去定制按键别名</p><p>&lt;!– 任何按键按下都会触发回调函数 –&gt;<br>&lt;textarea @keyup&#x3D;&quot;testKeyup&quot;&gt;&lt;&#x2F;textarea&gt;</p><p>&lt;!– 下面的两种写法效果是一致的 –&gt;<br>&lt;!– 使用按键码，回车键的keyCode是13 –&gt;<br>&lt;textarea @keyup.13&#x3D;&quot;testKeyup&quot;&gt;&lt;&#x2F;textarea&gt;</p><p>&lt;!– 使用按键修饰符，因为回车键比较常用，所以vue为他设置了名称，可以直接使用enter来代替 –&gt;<br>&lt;textarea @keyup.enter&#x3D;&quot;testKeyup&quot;&gt;&lt;&#x2F;textarea&gt;</p><blockquote></blockquote><p>示例代码</p><p>&lt;body&gt;<br> &lt;div id&#x3D;&quot;demo&quot;&gt;<br> &lt;h1&gt;3. 按键修饰符&lt;&#x2F;h1&gt;<br> &lt;input type&#x3D;&quot;text&quot; @keyup.13&#x3D;&quot;test8&quot;&gt;<br> &lt;input type&#x3D;&quot;text&quot; @keyup.enter&#x3D;&quot;test8&quot;&gt;<br> &lt;&#x2F;div&gt;</p><p> &lt;script src&#x3D;&quot;<a href="https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.js&quot;&gt;&lt;/script">https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.js&quot;&gt;&lt;/script</a>&gt;</p><p> &lt;script&gt;<br> new Vue(&#123;<br> el: &quot;#demo&quot;,<br> data: &#123;&#125;,<br> methods: &#123;<br> test8(event) &#123;<br> &#x2F;&#x2F; if(event.keyCode&#x3D;&#x3D;&#x3D;13)&#123; &#x2F;&#x2F; 原来的做法<br> &#x2F;&#x2F; alert(event.target.value);<br> &#x2F;&#x2F; &#125;<br> alert(event.target.value);<br> &#125;<br> &#125;<br> &#125;)<br> &lt;&#x2F;script&gt;<br>&lt;&#x2F;body&gt;</p><p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/74c3c96ab4264998b56a57a76720ceef~tplv-k3u1fbpfcp-zoom-in-crop-mark:1512:0:0:0.awebp"></p>]]>
    </content>
    <id>https://itdxb.cn/2024/08/26/vue/document/%E3%80%90Vue%E3%80%91%E5%9F%BA%E7%A1%80%E7%B3%BB%E5%88%97%EF%BC%88%E5%85%AD%EF%BC%89%E4%BA%8B%E4%BB%B6%E5%A4%84%E7%90%86%20-%20%E7%BB%91%E5%AE%9A%E7%9B%91%E5%90%AC%20-%20%E4%BA%8B%E4%BB%B6%E4%BF%AE%E9%A5%B0%E7%AC%A6%20-%20%E6%8C%89%E9%94%AE%E4%BF%AE%E9%A5%B0%E7%AC%A6/</id>
    <link href="https://itdxb.cn/2024/08/26/vue/document/%E3%80%90Vue%E3%80%91%E5%9F%BA%E7%A1%80%E7%B3%BB%E5%88%97%EF%BC%88%E5%85%AD%EF%BC%89%E4%BA%8B%E4%BB%B6%E5%A4%84%E7%90%86%20-%20%E7%BB%91%E5%AE%9A%E7%9B%91%E5%90%AC%20-%20%E4%BA%8B%E4%BB%B6%E4%BF%AE%E9%A5%B0%E7%AC%A6%20-%20%E6%8C%89%E9%94%AE%E4%BF%AE%E9%A5%B0%E7%AC%A6/"/>
    <published>2024-08-25T16:00:00.000Z</published>
    <summary>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" cla]]>
    </summary>
    <title>【Vue】基础系列（六）事件处理 - 绑定监听 - 事件修饰符 - 按键修饰符</title>
    <updated>2026-07-16T06:36:48.416Z</updated>
  </entry>
  <entry>
    <author>
      <name>Super Bing</name>
    </author>
    <category term="Vue" scheme="https://itdxb.cn/categories/Vue/"/>
    <category term="教程" scheme="https://itdxb.cn/tags/%E6%95%99%E7%A8%8B/"/>
    <category term="Vue" scheme="https://itdxb.cn/tags/Vue/"/>
    <content>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><h2 id="样式绑定"><a href="#样式绑定" class="headerlink" title="样式绑定"></a><a href="#%E6%A0%B7%E5%BC%8F%E7%BB%91%E5%AE%9A"></a>样式绑定</h2><p>在应用界面中, 某个(些)元素的样式是变化的，class&#x2F;style 绑定就是专门用来实现动态样式效果的技术</p><h3 id="class-绑定"><a href="#class-绑定" class="headerlink" title="class 绑定"></a><a href="#class-%E7%BB%91%E5%AE%9A"></a>class 绑定</h3><p>:class&#x3D;&#x27;xxx&#x27; &#x2F;&#x2F; xxx可以是字符串、对象、数组。</p><h4 id="字符串"><a href="#字符串" class="headerlink" title="字符串"></a><a href="#%E5%AD%97%E7%AC%A6%E4%B8%B2"></a>字符串</h4><p>表达式是字符串: <code>&amp;#39;classA&amp;#39;</code></p><blockquote></blockquote><p>适用于：类名不确定，要动态获取</p><p>&lt;!DOCTYPE html&gt;<br>&lt;html&gt;</p><p>&lt;head&gt;<br> &lt;meta charset&#x3D;&quot;UTF-8&quot; &#x2F;&gt;<br> &lt;title&gt;绑定样式&lt;&#x2F;title&gt;<br> &lt;style&gt;<br> .basic &#123;<br> width: 400px;<br> height: 100px;<br> border: 1px solid black;<br> &#125;</p><p> .happy &#123;<br> border: 4px solid red;<br> ;<br> background-color: rgba(255, 255, 0, 0.644);<br> background: linear-gradient(30deg, yellow, pink, orange, yellow);<br> &#125;</p><p> .sad &#123;<br> border: 4px dashed rgb(2, 197, 2);<br> background-color: gray;<br> &#125;</p><p> .normal &#123;<br> background-color: skyblue;<br> &#125;</p><p> &lt;&#x2F;style&gt;<br> &lt;script src&#x3D;&quot;<a href="https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.js&quot;&gt;&lt;/script">https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.js&quot;&gt;&lt;/script</a>&gt;<br>&lt;&#x2F;head&gt;</p><p>&lt;body&gt;</p><p> &lt;!– 准备好一个容器–&gt;<br> &lt;div id&#x3D;&quot;root&quot;&gt;<br> &lt;!– 绑定class样式–字符串写法，适用于：样式的类名不确定，需要动态指定 –&gt;<br> &lt;div class&#x3D;&quot;basic&quot; :class&#x3D;&quot;mood&quot; @click&#x3D;&quot;changeMood&quot;&gt;&#123;&#123;name&#125;&#125;&lt;&#x2F;div&gt;<br> &lt;&#x2F;div&gt;<br>&lt;&#x2F;body&gt;</p><p>&lt;script type&#x3D;&quot;text&#x2F;javascript&quot;&gt;<br> Vue.config.productionTip &#x3D; false</p><p> const vm &#x3D; new Vue(&#123;<br> el: &#x27;#root&#x27;,<br> data: &#123;<br> name: &#x27;YK菌&#x27;,<br> mood: &#x27;normal&#x27;<br> &#125;,<br> methods: &#123;<br> changeMood() &#123;<br> const arr &#x3D; [&#x27;happy&#x27;, &#x27;sad&#x27;, &#x27;normal&#x27;]<br> const index &#x3D; Math.floor(Math.random() * 3)<br> this.mood &#x3D; arr[index]<br> &#125;<br> &#125;,<br> &#125;)<br>&lt;&#x2F;script&gt;</p><p>&lt;&#x2F;html&gt;</p><p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4eee1a78fed3496881944df62afe04ee~tplv-k3u1fbpfcp-zoom-in-crop-mark:1512:0:0:0.awebp"></p><h4 id="对象"><a href="#对象" class="headerlink" title="对象"></a><a href="#%E5%AF%B9%E8%B1%A1"></a>对象</h4><p>表达式是对象: <code>&amp;#123;classA:isA, classB: isB&amp;#125;</code></p><blockquote></blockquote><p>适用于：要绑定多个样式，个数不确定，名字也不确定</p><p>对象语法用于根据条件添加或移除类名。<strong>对象的键是类名，值是布尔值</strong>，布尔值为 true 时应用该类名，为 false 时移除该类名。</p><p>&lt;div v-bind:class&#x3D;&quot;&#123; active: isActive, &#x27;text-danger&#x27;: hasError &#125;&quot;&gt;&lt;&#x2F;div&gt;</p><blockquote></blockquote><p>在上面的例子中，如果 <code>isActive</code> 为 true，<code>active</code> 类将被添加到 div 中。同样，如果 <code>hasError</code> 为 true，<code>text-danger</code> 类将被添加。</p><p>&lt;!DOCTYPE html&gt;<br>&lt;html&gt;</p><p>&lt;head&gt;<br> &lt;meta charset&#x3D;&quot;UTF-8&quot; &#x2F;&gt;<br> &lt;title&gt;绑定样式&lt;&#x2F;title&gt;<br> &lt;style&gt;<br> .basic &#123;<br> width: 400px;<br> height: 100px;<br> border: 1px solid black;<br> &#125;</p><p> .yk1 &#123;<br> background-color: yellowgreen;<br> &#125;</p><p> .yk2 &#123;<br> font-size: 30px;<br> text-shadow: 2px 2px 10px red;<br> &#125;</p><p> .yk3 &#123;<br> border-radius: 20px;<br> &#125;<br> &lt;&#x2F;style&gt;<br> &lt;script src&#x3D;&quot;<a href="https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.js&quot;&gt;&lt;/script">https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.js&quot;&gt;&lt;/script</a>&gt;<br>&lt;&#x2F;head&gt;</p><p>&lt;body&gt;</p><p> &lt;!– 准备好一个容器–&gt;<br> &lt;div id&#x3D;&quot;root&quot;&gt;<br> &lt;!– 绑定class样式–数组写法，适用于：要绑定的样式个数不确定、名字也不确定 –&gt;<br> &lt;div class&#x3D;&quot;basic&quot; :class&#x3D;&quot;classArr&quot;&gt;&#123;&#123;name&#125;&#125;&lt;&#x2F;div&gt; &lt;br &#x2F;&gt;&lt;br &#x2F;&gt;<br> &lt;&#x2F;div&gt;<br>&lt;&#x2F;body&gt;</p><p>&lt;script type&#x3D;&quot;text&#x2F;javascript&quot;&gt;<br> Vue.config.productionTip &#x3D; false</p><p> const vm &#x3D; new Vue(&#123;<br> el: &#x27;#root&#x27;,<br> data: &#123;<br> name: &#x27;YK菌&#x27;,<br> classArr: [&#x27;yk1&#x27;, &#x27;yk2&#x27;, &#x27;yk3&#x27;]<br> &#125;,<br> &#125;)<br>&lt;&#x2F;script&gt;</p><p>&lt;&#x2F;html&gt;</p><p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/238e4af4135a497abf87f2a97e585a9a~tplv-k3u1fbpfcp-zoom-in-crop-mark:1512:0:0:0.awebp"></p><h4 id="数组"><a href="#数组" class="headerlink" title="数组"></a><a href="#%E6%95%B0%E7%BB%84"></a>数组</h4><p>表达式是数组: <code>[&amp;#39;classA&amp;#39;, &amp;#39;classB&amp;#39;]</code></p><blockquote></blockquote><p>适用于：要绑定多个样式，个数确定，名字也确定，但不确定用不用</p><p>&lt;!DOCTYPE html&gt;<br>&lt;html&gt;</p><p>&lt;head&gt;<br> &lt;meta charset&#x3D;&quot;UTF-8&quot; &#x2F;&gt;<br> &lt;title&gt;绑定样式&lt;&#x2F;title&gt;<br> &lt;style&gt;<br> .basic &#123;<br> width: 400px;<br> height: 100px;<br> border: 1px solid black;<br> &#125;</p><p> .yk1 &#123;<br> background-color: yellowgreen;<br> &#125;</p><p> .yk2 &#123;<br> font-size: 30px;<br> text-shadow: 2px 2px 10px red;<br> &#125;</p><p> .yk3 &#123;<br> border-radius: 20px;<br> &#125;<br> &lt;&#x2F;style&gt;<br> &lt;script src&#x3D;&quot;<a href="https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.js&quot;&gt;&lt;/script">https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.js&quot;&gt;&lt;/script</a>&gt;<br>&lt;&#x2F;head&gt;</p><p>&lt;body&gt;</p><p> &lt;!– 准备好一个容器–&gt;<br> &lt;div id&#x3D;&quot;root&quot;&gt;<br> &lt;!– 绑定class样式–对象写法，适用于：要绑定的样式个数确定、名字也确定，但要动态决定用不用 –&gt;<br> &lt;div class&#x3D;&quot;basic&quot; :class&#x3D;&quot;classObj&quot;&gt;&#123;&#123;name&#125;&#125;&lt;&#x2F;div&gt;<br> &lt;&#x2F;div&gt;<br>&lt;&#x2F;body&gt;</p><p>&lt;script type&#x3D;&quot;text&#x2F;javascript&quot;&gt;<br> Vue.config.productionTip &#x3D; false</p><p> const vm &#x3D; new Vue(&#123;<br> el: &#x27;#root&#x27;,<br> data: &#123;<br> name: &#x27;YK菌&#x27;,<br> classObj: &#123;<br> yk1: true,<br> yk2: false,<br> yk3: true<br> &#125;<br> &#125;<br> &#125;)<br>&lt;&#x2F;script&gt;</p><p>&lt;&#x2F;html&gt;</p><p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/46d37ebc7911496bae1830d76ac93250~tplv-k3u1fbpfcp-zoom-in-crop-mark:1512:0:0:0.awebp"></p><h3 id="style-绑定"><a href="#style-绑定" class="headerlink" title="style 绑定"></a><a href="#style-%E7%BB%91%E5%AE%9A"></a>style 绑定</h3><p>:style&#x3D;&quot;&#123; color: activeColor, fontSize: fontSize + &#x27;px&#x27; &#125;&quot;</p><blockquote></blockquote><p>其中 <code>activeColor</code>&#x2F;<code>fontSize</code> 是 data 属性</p><p>:style&#x3D;&quot;&#123;fontSize: xxx&#125;&quot;其中xxx是动态值。<br>:style&#x3D;&quot;[a,b]&quot;其中a、b是样式对象。</p><h4 id="对象-1"><a href="#对象-1" class="headerlink" title="对象"></a><a href="#%E5%AF%B9%E8%B1%A1-1"></a>对象</h4><p>&lt;!DOCTYPE html&gt;<br>&lt;html&gt;</p><p>&lt;head&gt;<br> &lt;meta charset&#x3D;&quot;UTF-8&quot; &#x2F;&gt;<br> &lt;title&gt;绑定样式&lt;&#x2F;title&gt;<br> &lt;style&gt;<br> .basic &#123;<br> width: 400px;<br> height: 100px;<br> border: 1px solid black;<br> &#125;<br> &lt;&#x2F;style&gt;<br> &lt;script src&#x3D;&quot;<a href="https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.js&quot;&gt;&lt;/script">https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.js&quot;&gt;&lt;/script</a>&gt;<br>&lt;&#x2F;head&gt;</p><p>&lt;body&gt;</p><p> &lt;!– 准备好一个容器–&gt;<br> &lt;div id&#x3D;&quot;root&quot;&gt;</p><p> &lt;!– 绑定style样式–对象写法 –&gt;<br> &lt;div class&#x3D;&quot;basic&quot; :style&#x3D;&quot;styleObj, styleObj2&quot;&gt;&#123;&#123;name&#125;&#125;&lt;&#x2F;div&gt;</p><p> &lt;&#x2F;div&gt;<br>&lt;&#x2F;body&gt;</p><p>&lt;script type&#x3D;&quot;text&#x2F;javascript&quot;&gt;<br> Vue.config.productionTip &#x3D; false</p><p> const vm &#x3D; new Vue(&#123;<br> el: &#x27;#root&#x27;,<br> data: &#123;<br> name: &#x27;YK菌&#x27;,<br> styleObj: &#123;<br> fontSize: &#x27;40px&#x27;,<br> color: &#x27;red&#x27;,<br> &#125;,<br> styleObj2: &#123;<br> backgroundColor: &#x27;orange&#x27;<br> &#125;,<br> &#125;,<br> &#125;)<br>&lt;&#x2F;script&gt;</p><p>&lt;&#x2F;html&gt;</p><p><img src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1790eb8f0f2d424d85a9b9f2c2e3a6b8~tplv-k3u1fbpfcp-zoom-in-crop-mark:1512:0:0:0.awebp"></p><h4 id="数组-1"><a href="#数组-1" class="headerlink" title="数组"></a><a href="#%E6%95%B0%E7%BB%84-1"></a>数组</h4><p>&lt;!DOCTYPE html&gt;<br>&lt;html&gt;</p><p>&lt;head&gt;<br> &lt;meta charset&#x3D;&quot;UTF-8&quot; &#x2F;&gt;<br> &lt;title&gt;绑定样式&lt;&#x2F;title&gt;<br> &lt;style&gt;<br> .basic &#123;<br> width: 400px;<br> height: 100px;<br> border: 1px solid black;<br> &#125;<br> &lt;&#x2F;style&gt;<br> &lt;script src&#x3D;&quot;<a href="https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.js&quot;&gt;&lt;/script">https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.js&quot;&gt;&lt;/script</a>&gt;<br>&lt;&#x2F;head&gt;</p><p>&lt;body&gt;</p><p> &lt;!– 准备好一个容器–&gt;<br> &lt;div id&#x3D;&quot;root&quot;&gt;</p><p> &lt;!– 绑定style样式–数组写法 –&gt;<br> &lt;div class&#x3D;&quot;basic&quot; :style&#x3D;&quot;styleArr&quot;&gt;&#123;&#123;name&#125;&#125;&lt;&#x2F;div&gt;</p><p> &lt;&#x2F;div&gt;<br>&lt;&#x2F;body&gt;</p><p>&lt;script type&#x3D;&quot;text&#x2F;javascript&quot;&gt;<br> Vue.config.productionTip &#x3D; false</p><p> const vm &#x3D; new Vue(&#123;<br> el: &#x27;#root&#x27;,<br> data: &#123;<br> name: &#x27;YK菌&#x27;,<br> styleArr: [&#123;<br> fontSize: &#x27;40px&#x27;,<br> color: &#x27;blue&#x27;,<br> &#125;,<br> &#123;<br> backgroundColor: &#x27;gray&#x27;<br> &#125;<br> ]<br> &#125;,<br> &#125;)<br>&lt;&#x2F;script&gt;</p><p>&lt;&#x2F;html&gt;</p><p><img src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/7cafcd03c94f4f8b95956e9d935c68c5~tplv-k3u1fbpfcp-zoom-in-crop-mark:1512:0:0:0.awebp"></p>]]>
    </content>
    <id>https://itdxb.cn/2024/08/26/vue/document/%E3%80%90Vue%E3%80%91%E5%9F%BA%E7%A1%80%E7%B3%BB%E5%88%97%EF%BC%88%E5%9B%9B%EF%BC%89%E6%A0%B7%E5%BC%8F%E7%BB%91%E5%AE%9A%20-%20class%20-%20style/</id>
    <link href="https://itdxb.cn/2024/08/26/vue/document/%E3%80%90Vue%E3%80%91%E5%9F%BA%E7%A1%80%E7%B3%BB%E5%88%97%EF%BC%88%E5%9B%9B%EF%BC%89%E6%A0%B7%E5%BC%8F%E7%BB%91%E5%AE%9A%20-%20class%20-%20style/"/>
    <published>2024-08-25T16:00:00.000Z</published>
    <summary>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" cla]]>
    </summary>
    <title>【Vue】基础系列（四）样式绑定 - class - style</title>
    <updated>2026-07-16T06:36:48.471Z</updated>
  </entry>
  <entry>
    <author>
      <name>Super Bing</name>
    </author>
    <category term="Vue" scheme="https://itdxb.cn/categories/Vue/"/>
    <category term="教程" scheme="https://itdxb.cn/tags/%E6%95%99%E7%A8%8B/"/>
    <category term="Vue" scheme="https://itdxb.cn/tags/Vue/"/>
    <content>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><h3 id="数据代理"><a href="#数据代理" class="headerlink" title="数据代理"></a><a href="#%E6%95%B0%E6%8D%AE%E4%BB%A3%E7%90%86"></a>数据代理</h3><p>在Vue.js中，<strong>数据代理是Vue实现响应式数据的核心机制之一</strong>。它通过使用JavaScript的<strong>Object.defineProperty</strong>（在Vue 3中则是Proxy）来拦截对对象属性的访问和修改，从而实现数据和视图的双向绑定。这使得开发者可以更方便地使用简洁的语法来访问和操作数据，而Vue会自动更新相应的视图。</p><h4 id="数据代理的概念"><a href="#数据代理的概念" class="headerlink" title="数据代理的概念"></a><a href="#%E6%95%B0%E6%8D%AE%E4%BB%A3%E7%90%86%E7%9A%84%E6%A6%82%E5%BF%B5"></a>数据代理的概念</h4><p>数据代理的核心是将<strong>组件的data对象中的属性代理到Vue实例上</strong>。这意味着你可以通过<code>this.propertyName</code>直接访问和修改data中的属性，而Vue会自动更新视图。</p><h4 id="数据代理的实现原理"><a href="#数据代理的实现原理" class="headerlink" title="数据代理的实现原理"></a><a href="#%E6%95%B0%E6%8D%AE%E4%BB%A3%E7%90%86%E7%9A%84%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86"></a>数据代理的实现原理</h4><p>在Vue 2中，数据代理是通过<code>Object.defineProperty</code>实现的。Vue在实例化时，会遍历data对象中的所有属性，并为每个属性设置<code>getter</code>和<code>setter</code>。当你访问或修改这些属性时，Vue会自动触发相应的DOM更新。</p><h4 id="数据代理的使用"><a href="#数据代理的使用" class="headerlink" title="数据代理的使用"></a><a href="#%E6%95%B0%E6%8D%AE%E4%BB%A3%E7%90%86%E7%9A%84%E4%BD%BF%E7%94%A8"></a>数据代理的使用</h4><blockquote></blockquote><p>示例代码</p><p>&lt;!DOCTYPE html&gt;<br>&lt;html&gt;<br>&lt;head&gt;<br> &lt;title&gt;Vue Data Proxy Example&lt;&#x2F;title&gt;<br> &lt;script src&#x3D;&quot;<a href="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js&quot;&gt;&lt;/script">https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js&quot;&gt;&lt;/script</a>&gt;<br>&lt;&#x2F;head&gt;<br>&lt;body&gt;<br> &lt;div id&#x3D;&quot;app&quot;&gt;<br> &lt;p&gt;Message: &#123;&#123; message &#125;&#125;&lt;&#x2F;p&gt;<br> &lt;input v-model&#x3D;&quot;message&quot; placeholder&#x3D;&quot;Edit message&quot;&gt;<br> &lt;&#x2F;div&gt;</p><p> &lt;script&gt;<br> new Vue(&#123;<br> el: &#x27;#app&#x27;,<br> data: &#123;<br> message: &#x27;Hello Vue!&#x27;<br> &#125;<br> &#125;);<br> &lt;&#x2F;script&gt;<br>&lt;&#x2F;body&gt;<br>&lt;&#x2F;html&gt;</p><blockquote></blockquote><p>解释：在这个示例中，<code>message</code> 是定义在 data 对象中的属性。通过Vue的数据代理机制，你可以直接通过 <code>&amp;#123;&amp;#123; message &amp;#125;&amp;#125;</code> 访问它，或者通过 <code>v-model</code> 绑定到输入框上进行修改。<br>Vue会自动代理data中的<code>message</code>属性，使得你可以通过<code>this.message</code>来访问它，并在视图中使用<code>&amp;#123;&amp;#123; message &amp;#125;&amp;#125;</code>绑定进行显示。</p><h4 id="数据代理的工作流程"><a href="#数据代理的工作流程" class="headerlink" title="数据代理的工作流程"></a><a href="#%E6%95%B0%E6%8D%AE%E4%BB%A3%E7%90%86%E7%9A%84%E5%B7%A5%E4%BD%9C%E6%B5%81%E7%A8%8B"></a>数据代理的工作流程</h4><p>当你在模板中引用<code>&amp;#123;&amp;#123; message &amp;#125;&amp;#125;</code>时，Vue实际上是访问了<code>this.message</code>，并通过数据代理机制去data对象中获取对应的值。</p><p>当你在输入框中输入新内容时，<code>v-model</code>指令会触发<code>message</code>属性的<code>setter</code>，Vue捕捉到这个变化后，会重新渲染与message相关的部分视图。</p><h4 id="数据代理的好处"><a href="#数据代理的好处" class="headerlink" title="数据代理的好处"></a><a href="#%E6%95%B0%E6%8D%AE%E4%BB%A3%E7%90%86%E7%9A%84%E5%A5%BD%E5%A4%84"></a>数据代理的好处</h4><ul><li><p>简洁性：你可以通过简洁的语法（例如this.message）直接访问数据，而不需要每次都通过<code>data</code>对象。</p></li><li><p>响应性：数据代理使得数据与视图之间的双向绑定更加直观和简单，Vue会自动处理数据变化引起的视图更新。</p></li><li><p>可维护性：通过数据代理，你可以将数据与视图的逻辑清晰地分离，代码更易于维护和理解。</p></li></ul><h2 id="计算属性与监视属性"><a href="#计算属性与监视属性" class="headerlink" title="计算属性与监视属性"></a><a href="#%E8%AE%A1%E7%AE%97%E5%B1%9E%E6%80%A7%E4%B8%8E%E7%9B%91%E8%A7%86%E5%B1%9E%E6%80%A7"></a>计算属性与监视属性</h2><p>在Vue.js中，计算属性（computed properties）和监视属性（watchers）是两种常用的<strong>响应式特性</strong>，用于在<strong>数据发生变化时自动更新页面的内容</strong>。它们虽然在功能上有相似之处，但适用于不同的场景。</p><h3 id="计算属性（Computed-Properties）"><a href="#计算属性（Computed-Properties）" class="headerlink" title="计算属性（Computed Properties）"></a><a href="#%E8%AE%A1%E7%AE%97%E5%B1%9E%E6%80%A7%EF%BC%88Computed-Properties%EF%BC%89"></a>计算属性（Computed Properties）</h3><h4 id="功能"><a href="#功能" class="headerlink" title="功能"></a><a href="#%E5%8A%9F%E8%83%BD"></a>功能</h4><p><strong>计算属性</strong>是基于已存在的响应式数据计算出一个新值的属性。它会根据依赖的数据自动更新，并且有缓存机制，只有当依赖的数据发生变化时才会重新计算。</p><p>计算属性适合用于<strong>基于其他数据动态计算出新值</strong>的场景，比如对数据进行格式化、合并、筛选等操作。</p><h4 id="特点"><a href="#特点" class="headerlink" title="特点"></a><a href="#%E7%89%B9%E7%82%B9"></a>特点</h4><p><strong>缓存</strong>：计算属性的结果会被缓存，直到它的依赖发生变化。这使得它在性能上比直接在模板中使用复杂表达式更优。</p><p><strong>简洁</strong>：计算属性通常以<code>getter</code>和<code>setter</code>形式定义，<code>getter</code>用于获取计算值，<code>setter</code>用于响应用户的输入或其他操作。</p><blockquote></blockquote><p>插值语法示例代码</p><p>&lt;!DOCTYPE html&gt;<br>&lt;html&gt;<br>&lt;head&gt;<br>&lt;meta charset&#x3D;&quot;UTF-8&quot; &#x2F;&gt;<br>&lt;title&gt;姓名案例_插值语法实现&lt;&#x2F;title&gt;<br>&lt;!– 引入Vue –&gt;<br>&lt;script type&#x3D;&quot;text&#x2F;javascript&quot; src&#x3D;&quot;..&#x2F;js&#x2F;vue.js&quot;&gt;&lt;&#x2F;script&gt;<br>&lt;&#x2F;head&gt;<br>&lt;body&gt;<br>&lt;!– 准备好一个容器–&gt;<br>&lt;div id&#x3D;&quot;root&quot;&gt;<br>姓：&lt;input type&#x3D;&quot;text&quot; v-model&#x3D;&quot;firstName&quot;&gt; &lt;br&#x2F;&gt;&lt;br&#x2F;&gt;<br>名：&lt;input type&#x3D;&quot;text&quot; v-model&#x3D;&quot;lastName&quot;&gt; &lt;br&#x2F;&gt;&lt;br&#x2F;&gt;<br>全名：&lt;span&gt;&#123;&#123;firstName&#125;&#125;-&#123;&#123;lastName&#125;&#125;&lt;&#x2F;span&gt;<br>&lt;&#x2F;div&gt;<br>&lt;&#x2F;body&gt;</p><pre><code>&amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。new Vue(&amp;#123;el:&amp;#x27;#root&amp;#x27;,data:&amp;#123;firstName:&amp;#x27;张&amp;#x27;,lastName:&amp;#x27;三&amp;#x27;&amp;#125;&amp;#125;)&amp;lt;/script&amp;gt;</code></pre><p>&lt;&#x2F;html&gt;</p><blockquote></blockquote><p>methods实现示例代码</p><p>&lt;!DOCTYPE html&gt;<br>&lt;html&gt;<br>&lt;head&gt;<br>&lt;meta charset&#x3D;&quot;UTF-8&quot; &#x2F;&gt;<br>&lt;title&gt;姓名案例_methods实现&lt;&#x2F;title&gt;<br>&lt;!– 引入Vue –&gt;<br>&lt;script type&#x3D;&quot;text&#x2F;javascript&quot; src&#x3D;&quot;..&#x2F;js&#x2F;vue.js&quot;&gt;&lt;&#x2F;script&gt;<br>&lt;&#x2F;head&gt;<br>&lt;body&gt;<br>&lt;!– 准备好一个容器–&gt;<br>&lt;div id&#x3D;&quot;root&quot;&gt;<br>姓：&lt;input type&#x3D;&quot;text&quot; v-model&#x3D;&quot;firstName&quot;&gt; &lt;br&#x2F;&gt;&lt;br&#x2F;&gt;<br>名：&lt;input type&#x3D;&quot;text&quot; v-model&#x3D;&quot;lastName&quot;&gt; &lt;br&#x2F;&gt;&lt;br&#x2F;&gt;<br>全名：&lt;span&gt;&#123;&#123;fullName()&#125;&#125;&lt;&#x2F;span&gt;<br>&lt;&#x2F;div&gt;<br>&lt;&#x2F;body&gt;</p><pre><code>&amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。new Vue(&amp;#123;el:&amp;#x27;#root&amp;#x27;,data:&amp;#123;firstName:&amp;#x27;张&amp;#x27;,lastName:&amp;#x27;三&amp;#x27;&amp;#125;,methods: &amp;#123;fullName()&amp;#123;console.log(&amp;#x27;@---fullName&amp;#x27;)return this.firstName + &amp;#x27;-&amp;#x27; + this.lastName&amp;#125;&amp;#125;,&amp;#125;)&amp;lt;/script&amp;gt;</code></pre><p>&lt;&#x2F;html&gt;</p><blockquote></blockquote><p>计算属性实现示例代码</p><p>&lt;!DOCTYPE html&gt;<br>&lt;html&gt;<br>&lt;head&gt;<br>&lt;meta charset&#x3D;&quot;UTF-8&quot; &#x2F;&gt;<br>&lt;title&gt;姓名案例_计算属性实现&lt;&#x2F;title&gt;<br>&lt;!– 引入Vue –&gt;<br>&lt;script type&#x3D;&quot;text&#x2F;javascript&quot; src&#x3D;&quot;..&#x2F;js&#x2F;vue.js&quot;&gt;&lt;&#x2F;script&gt;<br>&lt;&#x2F;head&gt;<br>&lt;body&gt;<br>&lt;!– 准备好一个容器–&gt;<br>&lt;div id&#x3D;&quot;root&quot;&gt;<br>姓：&lt;input type&#x3D;&quot;text&quot; v-model&#x3D;&quot;firstName&quot;&gt; &lt;br&#x2F;&gt;&lt;br&#x2F;&gt;<br>名：&lt;input type&#x3D;&quot;text&quot; v-model&#x3D;&quot;lastName&quot;&gt; &lt;br&#x2F;&gt;&lt;br&#x2F;&gt;<br>测试：&lt;input type&#x3D;&quot;text&quot; v-model&#x3D;&quot;x&quot;&gt; &lt;br&#x2F;&gt;&lt;br&#x2F;&gt;<br>全名：&lt;span&gt;&#123;&#123;fullName&#125;&#125;&lt;&#x2F;span&gt; &lt;br&#x2F;&gt;&lt;br&#x2F;&gt;<br>&lt;!– 全名：&lt;span&gt;&#123;&#123;fullName&#125;&#125;&lt;&#x2F;span&gt; &lt;br&#x2F;&gt;&lt;br&#x2F;&gt;<br>全名：&lt;span&gt;&#123;&#123;fullName&#125;&#125;&lt;&#x2F;span&gt; &lt;br&#x2F;&gt;&lt;br&#x2F;&gt;<br>全名：&lt;span&gt;&#123;&#123;fullName&#125;&#125;&lt;&#x2F;span&gt; –&gt;<br>&lt;&#x2F;div&gt;<br>&lt;&#x2F;body&gt;</p><pre><code>&amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。const vm = new Vue(&amp;#123;el:&amp;#x27;#root&amp;#x27;,data:&amp;#123;firstName:&amp;#x27;张&amp;#x27;,lastName:&amp;#x27;三&amp;#x27;,x:&amp;#x27;你好&amp;#x27;&amp;#125;,methods: &amp;#123;demo()&amp;#123;&amp;#125;&amp;#125;,computed:&amp;#123;fullName:&amp;#123;//get有什么作用？当有人读取fullName时，get就会被调用，且返回值就作为fullName的值//get什么时候调用？1.初次读取fullName时。2.所依赖的数据发生变化时。get()&amp;#123;console.log(&amp;#x27;get被调用了&amp;#x27;)// console.log(this) //此处的this是vmreturn this.firstName + &amp;#x27;-&amp;#x27; + this.lastName&amp;#125;,//set什么时候调用? 当fullName被修改时。set(value)&amp;#123;console.log(&amp;#x27;set&amp;#x27;,value)const arr = value.split(&amp;#x27;-&amp;#x27;)this.firstName = arr[0]this.lastName = arr[1]&amp;#125;&amp;#125;&amp;#125;&amp;#125;)&amp;lt;/script&amp;gt;</code></pre><p>&lt;&#x2F;html&gt;</p><blockquote></blockquote><p>解释：fullName 是一个计算属性，它通过 firstName 和 lastName 动态计算得出。如果用户输入一个新的全名，它也会自动更新 firstName 和 lastName。</p><blockquote></blockquote><p>计算属性实现简写</p><p>&lt;!DOCTYPE html&gt;<br>&lt;html&gt;<br>&lt;head&gt;<br>&lt;meta charset&#x3D;&quot;UTF-8&quot; &#x2F;&gt;<br>&lt;title&gt;姓名案例_计算属性实现&lt;&#x2F;title&gt;<br>&lt;!– 引入Vue –&gt;<br>&lt;script type&#x3D;&quot;text&#x2F;javascript&quot; src&#x3D;&quot;..&#x2F;js&#x2F;vue.js&quot;&gt;&lt;&#x2F;script&gt;<br>&lt;&#x2F;head&gt;<br>&lt;body&gt;<br>&lt;!– 准备好一个容器–&gt;<br>&lt;div id&#x3D;&quot;root&quot;&gt;<br>姓：&lt;input type&#x3D;&quot;text&quot; v-model&#x3D;&quot;firstName&quot;&gt; &lt;br&#x2F;&gt;&lt;br&#x2F;&gt;<br>名：&lt;input type&#x3D;&quot;text&quot; v-model&#x3D;&quot;lastName&quot;&gt; &lt;br&#x2F;&gt;&lt;br&#x2F;&gt;<br>全名：&lt;span&gt;&#123;&#123;fullName&#125;&#125;&lt;&#x2F;span&gt; &lt;br&#x2F;&gt;&lt;br&#x2F;&gt;<br>&lt;&#x2F;div&gt;<br>&lt;&#x2F;body&gt;</p><pre><code>&amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。const vm = new Vue(&amp;#123;el:&amp;#x27;#root&amp;#x27;,data:&amp;#123;firstName:&amp;#x27;张&amp;#x27;,lastName:&amp;#x27;三&amp;#x27;,&amp;#125;,computed:&amp;#123;//完整写法/* fullName:&amp;#123;get()&amp;#123;console.log(&amp;#x27;get被调用了&amp;#x27;)return this.firstName + &amp;#x27;-&amp;#x27; + this.lastName&amp;#125;,set(value)&amp;#123;console.log(&amp;#x27;set&amp;#x27;,value)const arr = value.split(&amp;#x27;-&amp;#x27;)this.firstName = arr[0]this.lastName = arr[1]&amp;#125;&amp;#125; *///简写fullName()&amp;#123;console.log(&amp;#x27;get被调用了&amp;#x27;)return this.firstName + &amp;#x27;-&amp;#x27; + this.lastName&amp;#125;&amp;#125;&amp;#125;)&amp;lt;/script&amp;gt;</code></pre><p>&lt;&#x2F;html&gt;</p><h4 id="原理"><a href="#原理" class="headerlink" title="原理"></a><a href="#%E5%8E%9F%E7%90%86"></a>原理</h4><p>底层借助了<code>Objcet.defineproperty</code>方法提供的<code>getter</code>和<code>setter</code>。</p><h4 id="get函数什么时候执行？"><a href="#get函数什么时候执行？" class="headerlink" title="get函数什么时候执行？"></a><a href="#get%E5%87%BD%E6%95%B0%E4%BB%80%E4%B9%88%E6%97%B6%E5%80%99%E6%89%A7%E8%A1%8C%EF%BC%9F"></a>get函数什么时候执行？</h4><ul><li><p>初次读取时会执行一次。</p></li><li><p>当依赖的数据发生改变时会被再次调用。</p></li></ul><h4 id="优势"><a href="#优势" class="headerlink" title="优势"></a><a href="#%E4%BC%98%E5%8A%BF"></a>优势</h4><p>与methods实现相比，内部有<strong>缓存机制（复用）</strong>，效率更高，调试方便。</p><blockquote></blockquote><p>计算属性最终会出现在vm上，直接读取使用即可。<br> 如果计算属性要被修改，那必须写<code>set</code>函数去响应修改，且<code>set</code>中要引起计算时依赖的数据发生改变。</p><h3 id="监视属性（Watchers）"><a href="#监视属性（Watchers）" class="headerlink" title="监视属性（Watchers）"></a><a href="#%E7%9B%91%E8%A7%86%E5%B1%9E%E6%80%A7%EF%BC%88Watchers%EF%BC%89"></a>监视属性（Watchers）</h3><h4 id="功能-1"><a href="#功能-1" class="headerlink" title="功能"></a><a href="#%E5%8A%9F%E8%83%BD-1"></a>功能</h4><p>监视属性用于<strong>监听某个数据属性的变化</strong>，并在其变化时执行回调函数。适合用于执行异步操作或在数据变化时需要执行复杂逻辑的场景。</p><h4 id="特点-1"><a href="#特点-1" class="headerlink" title="特点"></a><a href="#%E7%89%B9%E7%82%B9-1"></a>特点</h4><p><strong>灵活性</strong>：可以监听单个数据属性的变化，也可以监听多个数据属性，甚至可以使用深度监听（deep）来监控对象内部的变化。</p><p><strong>无缓存</strong>：与计算属性不同，监视属性没有缓存机制，每次数据变化都会触发回调。</p><h4 id="语法："><a href="#语法：" class="headerlink" title="语法："></a><a href="#%E8%AF%AD%E6%B3%95%EF%BC%9A"></a>语法：</h4><ul><li><p>new Vue时传入watch配置</p></li><li><p>通过vm.$watch监视</p></li></ul><blockquote></blockquote><p>天气案例示例代码(计算属性)</p><p>&lt;!DOCTYPE html&gt;<br>&lt;html&gt;<br>&lt;head&gt;<br>&lt;meta charset&#x3D;&quot;UTF-8&quot; &#x2F;&gt;<br>&lt;title&gt;天气案例&lt;&#x2F;title&gt;<br>&lt;!– 引入Vue –&gt;<br>&lt;script type&#x3D;&quot;text&#x2F;javascript&quot; src&#x3D;&quot;..&#x2F;js&#x2F;vue.js&quot;&gt;&lt;&#x2F;script&gt;<br>&lt;&#x2F;head&gt;<br>&lt;body&gt;<br>&lt;!– 准备好一个容器–&gt;<br>&lt;div id&#x3D;&quot;root&quot;&gt;<br>&lt;h2&gt;今天天气很&#123;&#123;info&#125;&#125;&lt;&#x2F;h2&gt;<br>&lt;!– 绑定事件的时候：@xxx&#x3D;&quot;yyy&quot; yyy可以写一些简单的语句 –&gt;<br>&lt;!– &lt;button @click&#x3D;&quot;isHot &#x3D; !isHot&quot;&gt;切换天气&lt;&#x2F;button&gt; –&gt;<br>&lt;button @click&#x3D;&quot;changeWeather&quot;&gt;切换天气&lt;&#x2F;button&gt;<br>&lt;&#x2F;div&gt;<br>&lt;&#x2F;body&gt;</p><pre><code>&amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。const vm = new Vue(&amp;#123;el:&amp;#x27;#root&amp;#x27;,data:&amp;#123;isHot:true,&amp;#125;,computed:&amp;#123;info()&amp;#123;return this.isHot ? &amp;#x27;炎热&amp;#x27; : &amp;#x27;凉爽&amp;#x27;&amp;#125;&amp;#125;,methods: &amp;#123;changeWeather()&amp;#123;this.isHot = !this.isHot&amp;#125;&amp;#125;,&amp;#125;)&amp;lt;/script&amp;gt;</code></pre><p>&lt;&#x2F;html&gt;</p><blockquote></blockquote><p>天气案例示例代码(监视属性)</p><p>&lt;!DOCTYPE html&gt;<br>&lt;html&gt;<br>&lt;head&gt;<br>&lt;meta charset&#x3D;&quot;UTF-8&quot; &#x2F;&gt;<br>&lt;title&gt;天气案例_监视属性&lt;&#x2F;title&gt;<br>&lt;!– 引入Vue –&gt;<br>&lt;script type&#x3D;&quot;text&#x2F;javascript&quot; src&#x3D;&quot;..&#x2F;js&#x2F;vue.js&quot;&gt;&lt;&#x2F;script&gt;<br>&lt;&#x2F;head&gt;<br>&lt;body&gt;<br>&lt;!–<br>监视属性watch：<br>1.当被监视的属性变化时, 回调函数自动调用, 进行相关操作<br>2.监视的属性必须存在，才能进行监视！！<br>3.监视的两种写法：<br>(1).new Vue时传入watch配置<br>(2).通过vm.$watch监视<br> –&gt;<br>&lt;!– 准备好一个容器–&gt;<br>&lt;div id&#x3D;&quot;root&quot;&gt;<br>&lt;h2&gt;今天天气很&#123;&#123;info&#125;&#125;&lt;&#x2F;h2&gt;<br>&lt;button @click&#x3D;&quot;changeWeather&quot;&gt;切换天气&lt;&#x2F;button&gt;<br>&lt;&#x2F;div&gt;<br>&lt;&#x2F;body&gt;</p><pre><code>&amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。const vm = new Vue(&amp;#123;el:&amp;#x27;#root&amp;#x27;,data:&amp;#123;isHot:true,&amp;#125;,computed:&amp;#123;info()&amp;#123;return this.isHot ? &amp;#x27;炎热&amp;#x27; : &amp;#x27;凉爽&amp;#x27;&amp;#125;&amp;#125;,methods: &amp;#123;changeWeather()&amp;#123;this.isHot = !this.isHot&amp;#125;&amp;#125;,/* watch:&amp;#123;isHot:&amp;#123;immediate:true, //初始化时让handler调用一下//handler什么时候调用？当isHot发生改变时。handler(newValue,oldValue)&amp;#123;console.log(&amp;#x27;isHot被修改了&amp;#x27;,newValue,oldValue)&amp;#125;&amp;#125;&amp;#125; */&amp;#125;)vm.$watch(&amp;#x27;isHot&amp;#x27;,&amp;#123;immediate:true, //初始化时让handler调用一下//handler什么时候调用？当isHot发生改变时。handler(newValue,oldValue)&amp;#123;console.log(&amp;#x27;isHot被修改了&amp;#x27;,newValue,oldValue)&amp;#125;&amp;#125;)&amp;lt;/script&amp;gt;</code></pre><p>&lt;&#x2F;html&gt;</p><h4 id="深度监视"><a href="#深度监视" class="headerlink" title="深度监视"></a><a href="#%E6%B7%B1%E5%BA%A6%E7%9B%91%E8%A7%86"></a>深度监视</h4><ul><li><p>Vue中的watch默认不监测对象内部值的改变（一层）。</p></li><li><p>配置deep:true可以监测对象内部值改变（多层）。</p></li></ul><blockquote></blockquote><p>备注：(1).Vue自身可以监测对象内部值的改变，但Vue提供的watch默认不可以！(2).使用watch时根据数据的具体结构，决定是否采用深度监视。</p><p>&lt;!DOCTYPE html&gt;<br>&lt;html&gt;<br>&lt;head&gt;<br>&lt;meta charset&#x3D;&quot;UTF-8&quot; &#x2F;&gt;<br>&lt;title&gt;天气案例_深度监视&lt;&#x2F;title&gt;<br>&lt;!– 引入Vue –&gt;<br>&lt;script type&#x3D;&quot;text&#x2F;javascript&quot; src&#x3D;&quot;..&#x2F;js&#x2F;vue.js&quot;&gt;&lt;&#x2F;script&gt;<br>&lt;&#x2F;head&gt;<br>&lt;body&gt;<br>&lt;!– 准备好一个容器–&gt;<br>&lt;div id&#x3D;&quot;root&quot;&gt;<br>&lt;h2&gt;今天天气很&#123;&#123;info&#125;&#125;&lt;&#x2F;h2&gt;<br>&lt;button @click&#x3D;&quot;changeWeather&quot;&gt;切换天气&lt;&#x2F;button&gt;<br>&lt;hr&#x2F;&gt;<br>&lt;h3&gt;a的值是:&#123;&#123;numbers.a&#125;&#125;&lt;&#x2F;h3&gt;<br>&lt;button @click&#x3D;&quot;numbers.a++&quot;&gt;点我让a+1&lt;&#x2F;button&gt;<br>&lt;h3&gt;b的值是:&#123;&#123;numbers.b&#125;&#125;&lt;&#x2F;h3&gt;<br>&lt;button @click&#x3D;&quot;numbers.b++&quot;&gt;点我让b+1&lt;&#x2F;button&gt;<br>&lt;button @click&#x3D;&quot;numbers &#x3D; &#123;a:666,b:888&#125;&quot;&gt;彻底替换掉numbers&lt;&#x2F;button&gt;<br>&#123;&#123;numbers.c.d.e&#125;&#125;<br>&lt;&#x2F;div&gt;<br>&lt;&#x2F;body&gt;</p><pre><code>&amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。const vm = new Vue(&amp;#123;el:&amp;#x27;#root&amp;#x27;,data:&amp;#123;isHot:true,numbers:&amp;#123;a:1,b:1,c:&amp;#123;d:&amp;#123;e:100&amp;#125;&amp;#125;&amp;#125;&amp;#125;,computed:&amp;#123;info()&amp;#123;return this.isHot ? &amp;#x27;炎热&amp;#x27; : &amp;#x27;凉爽&amp;#x27;&amp;#125;&amp;#125;,methods: &amp;#123;changeWeather()&amp;#123;this.isHot = !this.isHot&amp;#125;&amp;#125;,watch:&amp;#123;isHot:&amp;#123;// immediate:true, //初始化时让handler调用一下//handler什么时候调用？当isHot发生改变时。handler(newValue,oldValue)&amp;#123;console.log(&amp;#x27;isHot被修改了&amp;#x27;,newValue,oldValue)&amp;#125;&amp;#125;,//监视多级结构中某个属性的变化/* &amp;#x27;numbers.a&amp;#x27;:&amp;#123;handler()&amp;#123;console.log(&amp;#x27;a被改变了&amp;#x27;)&amp;#125;&amp;#125; *///监视多级结构中所有属性的变化numbers:&amp;#123;deep:true,handler()&amp;#123;console.log(&amp;#x27;numbers改变了&amp;#x27;)&amp;#125;&amp;#125;&amp;#125;&amp;#125;)&amp;lt;/script&amp;gt;</code></pre><p>&lt;&#x2F;html&gt;</p><h4 id="监视属性简写"><a href="#监视属性简写" class="headerlink" title="监视属性简写"></a><a href="#%E7%9B%91%E8%A7%86%E5%B1%9E%E6%80%A7%E7%AE%80%E5%86%99"></a>监视属性简写</h4><p>&lt;!DOCTYPE html&gt;<br>&lt;html&gt;<br>&lt;head&gt;<br>&lt;meta charset&#x3D;&quot;UTF-8&quot; &#x2F;&gt;<br>&lt;title&gt;天气案例_监视属性_简写&lt;&#x2F;title&gt;<br>&lt;!– 引入Vue –&gt;<br>&lt;script type&#x3D;&quot;text&#x2F;javascript&quot; src&#x3D;&quot;..&#x2F;js&#x2F;vue.js&quot;&gt;&lt;&#x2F;script&gt;<br>&lt;&#x2F;head&gt;<br>&lt;body&gt;<br>&lt;!– 准备好一个容器–&gt;<br>&lt;div id&#x3D;&quot;root&quot;&gt;<br>&lt;h2&gt;今天天气很&#123;&#123;info&#125;&#125;&lt;&#x2F;h2&gt;<br>&lt;button @click&#x3D;&quot;changeWeather&quot;&gt;切换天气&lt;&#x2F;button&gt;<br>&lt;&#x2F;div&gt;<br>&lt;&#x2F;body&gt;</p><pre><code>&amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。const vm = new Vue(&amp;#123;el:&amp;#x27;#root&amp;#x27;,data:&amp;#123;isHot:true,&amp;#125;,computed:&amp;#123;info()&amp;#123;return this.isHot ? &amp;#x27;炎热&amp;#x27; : &amp;#x27;凉爽&amp;#x27;&amp;#125;&amp;#125;,methods: &amp;#123;changeWeather()&amp;#123;this.isHot = !this.isHot&amp;#125;&amp;#125;,watch:&amp;#123;//正常写法/* isHot:&amp;#123;// immediate:true, //初始化时让handler调用一下// deep:true,//深度监视handler(newValue,oldValue)&amp;#123;console.log(&amp;#x27;isHot被修改了&amp;#x27;,newValue,oldValue)&amp;#125;&amp;#125;, *///简写/* isHot(newValue,oldValue)&amp;#123;console.log(&amp;#x27;isHot被修改了&amp;#x27;,newValue,oldValue,this)&amp;#125; */&amp;#125;&amp;#125;)//正常写法/* vm.$watch(&amp;#x27;isHot&amp;#x27;,&amp;#123;immediate:true, //初始化时让handler调用一下deep:true,//深度监视handler(newValue,oldValue)&amp;#123;console.log(&amp;#x27;isHot被修改了&amp;#x27;,newValue,oldValue)&amp;#125;&amp;#125;) *///简写/* vm.$watch(&amp;#x27;isHot&amp;#x27;,(newValue,oldValue)=&amp;gt;&amp;#123;console.log(&amp;#x27;isHot被修改了&amp;#x27;,newValue,oldValue,this)&amp;#125;) */&amp;lt;/script&amp;gt;</code></pre><p>&lt;&#x2F;html&gt;</p><h3 id="计算属性-vs-监视属性"><a href="#计算属性-vs-监视属性" class="headerlink" title="计算属性 vs 监视属性"></a><a href="#%E8%AE%A1%E7%AE%97%E5%B1%9E%E6%80%A7-vs-%E7%9B%91%E8%A7%86%E5%B1%9E%E6%80%A7"></a>计算属性 vs 监视属性</h3><p>1.computed能完成的功能，watch都可以完成。</p><p>2.watch能完成的功能，computed不一定能完成，例如：watch可以进行异步操作。</p><p>两个重要的小原则：</p><ul><li><p>所被Vue管理的函数，最好写成普通函数，这样this的指向才是vm 或 组件实例对象</p></li><li><p>所有不被Vue所管理的函数（定时器的回调函数、ajax的回调函数等、Promise的回调函数），最好写成箭头函数，这样this的指向才是vm 或 组件实例对象</p></li></ul><blockquote></blockquote><p>计算属性变量在computed中定义，监视属性监听的是已经在 data 中定义的变量, 当该变量变化时，会触发 watch 中的方法.</p><blockquote></blockquote><p>computed 具有缓存功能，可以监听对象某个具体属性。watch可以进行深度监听，监听对象的变化</p><blockquote></blockquote><p>计算属性是声明式的描述一个值依赖了其他值，依赖的值改变后重新计算结果更新DOM。监视属性的是定义的变量，当定义的值发生变化时，执行相对应的函数</p>]]>
    </content>
    <id>https://itdxb.cn/2024/08/23/vue/document/%E3%80%90Vue%E3%80%91%E5%9F%BA%E7%A1%80%E7%B3%BB%E5%88%97%EF%BC%88%E4%B8%89%EF%BC%89%E8%AE%A1%E7%AE%97%E5%B1%9E%E6%80%A7%E4%B8%8E%E7%9B%91%E8%A7%86%E5%B1%9E%E6%80%A7/</id>
    <link href="https://itdxb.cn/2024/08/23/vue/document/%E3%80%90Vue%E3%80%91%E5%9F%BA%E7%A1%80%E7%B3%BB%E5%88%97%EF%BC%88%E4%B8%89%EF%BC%89%E8%AE%A1%E7%AE%97%E5%B1%9E%E6%80%A7%E4%B8%8E%E7%9B%91%E8%A7%86%E5%B1%9E%E6%80%A7/"/>
    <published>2024-08-22T16:00:00.000Z</published>
    <summary>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" cla]]>
    </summary>
    <title>【Vue】基础系列（三）计算属性与监视属性</title>
    <updated>2026-07-16T06:36:48.224Z</updated>
  </entry>
</feed>
