> ## Documentation Index
> Fetch the complete documentation index at: https://docs.akria.net/llms.txt
> Use this file to discover all available pages before exploring further.

# Day 08 | Python 基础补漏

> Python 虚拟环境、类型注解、列表推导式与上下文管理器

<Info>
  **今日目标**：Python 是运维工程师的"瑞士军刀"。今天我们不从零学起，而是重点掌握那些在 Infra 开发中 80% 会用到的 **高频语法** 和 **标准工程结构**。今天不只是写代码，而是要彻底理解 **"为什么需要虚拟环境"**、**"类型注解的价值"** 以及 **"上下文管理器如何防止资源泄漏"**。
</Info>

## <span style={{ display: 'inline-flex', alignItems: 'center', gap: '8px' }}><Icon icon="book" size={26} color="#059669" /> 学习内容 (30 mins)</span>

在开始写代码前，先搞懂这些核心概念，否则后面的代码你会看得云里雾里。

<AccordionGroup>
  <Accordion title="虚拟环境与包管理 (15 mins)" icon="box">
    **什么是虚拟环境？**

    虚拟环境是一个独立的 Python 运行环境，它有自己的解释器和包安装目录，与系统 Python 完全隔离。

    **为什么必须使用虚拟环境？**

    * **避免依赖冲突**：不同项目可能需要不同版本的同一个包（如 `requests 2.25.1` vs `requests 2.28.0`）
    * **项目隔离**：每个项目的依赖互不干扰，不会污染系统 Python
    * **可重现性**：通过 `requirements.txt` 可以精确复现项目环境
    * **团队协作**：团队成员可以安装完全相同的依赖版本

    **虚拟环境工具对比**：

    * **venv**（推荐）：
      * 优点：Python 3.3+ 内置，无需额外安装，轻量级
      * 缺点：功能相对简单
      * 适用场景：大多数项目，特别是运维脚本
    * **Conda**：
      * 优点：可以管理 Python 版本本身，适合数据科学
      * 缺点：体积大，启动慢
      * 适用场景：数据科学、机器学习项目

    **包管理最佳实践**：

    * **安装包**：`pip install package_name`
    * **冻结依赖**：`pip freeze > requirements.txt`（记录所有已安装包的精确版本）
    * **安装依赖**：`pip install -r requirements.txt`（从文件安装）
    * **为什么需要冻结**：确保开发、测试、生产环境使用相同版本的包，避免"在我机器上能跑"的问题
  </Accordion>

  <Accordion title="Python 高频语法 (15 mins)" icon="code">
    **类型注解 (Type Hints)**

    * **是什么**：在函数参数和返回值上标注类型信息
    * **为什么需要**：
      * IDE 智能提示：输入参数时能看到类型，减少错误
      * 代码可读性：一眼看出函数期望什么类型
      * 静态检查：使用 `mypy` 可以在运行前发现类型错误
      * 文档作用：类型本身就是最好的文档
    * **语法**：`def function_name(param: type) -> return_type:`
    * **注意**：Python 是动态语言，类型注解不会在运行时检查，只是提示作用

    **f-string（格式化字符串）**

    * **是什么**：Python 3.6+ 引入的字符串格式化语法
    * **为什么优于旧方法**：
      * 可读性强：`f"Hello {name}"` 比 `"Hello {}".format(name)` 更直观
      * 性能更好：f-string 在编译时优化，比 `%` 和 `.format()` 更快
      * 支持表达式：`f"Result: {x + y}"` 可以直接计算
    * **语法**：`f"文本 {变量} 文本"`

    **上下文管理器 (Context Manager)**

    * **是什么**：使用 `with` 语句自动管理资源的获取和释放
    * **为什么必须使用**：
      * **防止资源泄漏**：文件、数据库连接、网络连接等资源必须正确关闭
      * **异常安全**：即使发生异常，`with` 也会确保资源被释放
      * **代码简洁**：不需要手动写 `try-finally`
    * **原理**：`with open()` 会在进入时调用 `__enter__()`，退出时调用 `__exit__()`
    * **常见场景**：
      * 文件操作：`with open("file.txt") as f:`
      * 数据库连接：`with db.get_cursor() as cursor:`
      * 锁：`with lock:`
  </Accordion>
</AccordionGroup>

***

## <span style={{ display: 'inline-flex', alignItems: 'center', gap: '8px' }}><Icon icon="code" size={26} color="#059669" /> 代码任务 (90 mins)</span>

<Steps>
  <Step title="环境准备">
    在开始之前，确保你的环境已准备好：

    * **Python 环境**：需要 Python 3.7+（推荐 3.9+）
    * **文本编辑器**：继续使用 nano 或 vi/vim（如果还不熟悉，参考 Day 01 的编辑器说明）
    * **检查 Python 版本**：`python3 --version`

    <CodeGroup>
      ```bash 检查 Python 版本 theme={null}
      # 检查 Python 3 是否已安装
      python3 --version
      # 应该输出类似: Python 3.9.6

      # 如果没有安装，macOS 使用 Homebrew:
      # brew install python3

      # Linux (Ubuntu/Debian):
      # sudo apt update && sudo apt install python3 python3-pip python3-venv
      ```
    </CodeGroup>
  </Step>

  <Step title="任务 A：创建虚拟环境">
    在项目目录下创建一个独立的 Python 环境。

    <CodeGroup>
      ```bash 创建虚拟环境 theme={null}
      # 1. 进入项目目录（如果还没有）
      cd ~/insightful-ops

      # 2. 创建虚拟环境（.venv 是约定俗成的目录名）
      python3 -m venv .venv
      # 这会创建一个 .venv 目录，包含独立的 Python 解释器和 pip

      # 3. 激活虚拟环境（Mac/Linux）
      source .venv/bin/activate
      # 激活后，命令行提示符前面会显示 (.venv)

      # 4. 验证激活成功
      which python
      # 应该输出: .../insightful-ops/.venv/bin/python
      # 而不是系统的 /usr/bin/python3

      # 5. 检查 pip 版本
      pip --version
      # 应该显示虚拟环境中的 pip
      ```

      ```bash 退出虚拟环境 theme={null}
      # 当不需要使用虚拟环境时，可以退出
      deactivate
      # 提示符前的 (.venv) 会消失
      ```
    </CodeGroup>

    **验证步骤**：

    1. 检查 Python 路径：`which python` 应该指向 `.venv/bin/python`
    2. 检查 pip 路径：`which pip` 应该指向 `.venv/bin/pip`
    3. 检查提示符：命令行前面应该有 `(.venv)` 标识

    **常见错误**：

    <Warning>
      * ❌ `python3: command not found` - Python 3 未安装，需要先安装
      * ❌ `No module named venv` - Python 版本太老（需要 3.3+），或安装不完整
      * ❌ `source: command not found` - Windows 系统，需要使用 `Scripts\activate.bat`（但建议使用 WSL）
      * ❌ 激活后 `which python` 还是系统路径 - 可能激活失败，检查 `.venv/bin/activate` 文件是否存在
    </Warning>
  </Step>

  <Step title="任务 B：包管理与依赖冻结">
    安装一个常用包并导出依赖列表。

    <CodeGroup>
      ```bash 安装包并冻结依赖 theme={null}
      # 1. 确保虚拟环境已激活（提示符前有 (.venv)）

      # 2. 安装 requests 库（HTTP 请求库，运维常用）
      pip install requests
      # 这会自动安装 requests 及其依赖（如 urllib3, certifi 等）

      # 3. 查看已安装的包
      pip list
      # 会显示所有已安装的包及其版本

      # 4. 冻结依赖到文件（这是标准操作！）
      pip freeze > requirements.txt

      # 5. 查看 requirements.txt 内容
      cat requirements.txt
      # 应该看到类似:
      # certifi==2023.7.22
      # charset-normalizer==3.2.0
      # idna==3.4
      # requests==2.31.0
      # urllib3==2.0.4
      ```

      ```bash 从 requirements.txt 安装依赖 theme={null}
      # 模拟在新环境中安装依赖
      # 1. 创建新的虚拟环境
      python3 -m venv .venv2
      source .venv2/bin/activate

      # 2. 从文件安装所有依赖
      pip install -r requirements.txt

      # 3. 验证安装
      pip list
      # 应该看到和原环境相同的包版本
      ```
    </CodeGroup>

    **为什么需要 requirements.txt？**

    * **团队协作**：团队成员可以安装完全相同的依赖
    * **环境复现**：生产环境可以精确复现开发环境
    * **版本锁定**：避免因依赖更新导致的"昨天还能跑，今天就不行了"

    **验证步骤**：

    1. 检查 `requirements.txt` 文件是否存在
    2. 检查文件内容是否包含 `requests==` 开头的行
    3. 尝试在新虚拟环境中安装：`pip install -r requirements.txt`
  </Step>

  <Step title="任务 C：编写 Python 脚本 - 类型注解与 f-string">
    创建 `08_syntax_review.py`，练习类型注解和 f-string。

    <CodeGroup>
      ```python 08_syntax_review.py theme={null}
      #!/usr/bin/env python3
      """
      Day 08 - Python 语法复习脚本
      演示类型注解、f-string 和列表推导式
      """

      # ========== 1. 类型注解示例 ==========
      def calculate_memory(total: int, usage: float) -> str:
          """
          计算内存使用率
          
          参数:
              total: 总内存（MB）
              usage: 已使用内存（MB）
          
          返回:
              格式化的使用率字符串
          """
          # 计算百分比：usage / total * 100
          # :.2% 表示格式化为百分比，保留 2 位小数
          percentage = usage / total
          return f"Memory Usage: {percentage:.2%}"

      # 测试类型注解函数
      result = calculate_memory(total=8192, usage=4096)
      print(result)  # 输出: Memory Usage: 50.00%

      # ========== 2. f-string 示例 ==========
      server_name = "web-01"
      cpu_usage = 75.5
      memory_usage = 60.2

      # 旧方法（不推荐）
      old_way = "Server: " + server_name + ", CPU: " + str(cpu_usage) + "%"
      print(f"旧方法: {old_way}")

      # f-string 方法（推荐）
      new_way = f"Server: {server_name}, CPU: {cpu_usage}%, Memory: {memory_usage}%"
      print(f"新方法: {new_way}")

      # f-string 支持表达式
      x = 10
      y = 20
      print(f"计算结果: {x} + {y} = {x + y}")  # 输出: 计算结果: 10 + 20 = 30

      # ========== 3. 列表推导式示例 ==========
      # 传统 for 循环方式
      servers = ["web-01", "db-01", "web-02", "cache-01", "web-03"]
      web_servers_old = []
      for server in servers:
          if server.startswith("web"):
              web_servers_old.append(server)
      print(f"传统方式: {web_servers_old}")  # ['web-01', 'web-02', 'web-03']

      # 列表推导式（一行搞定，更 Pythonic）
      web_servers_new = [s for s in servers if s.startswith("web")]
      print(f"列表推导式: {web_servers_new}")  # ['web-01', 'web-02', 'web-03']

      # 列表推导式还可以做转换
      web_servers_upper = [s.upper() for s in servers if s.startswith("web")]
      print(f"转大写: {web_servers_upper}")  # ['WEB-01', 'WEB-02', 'WEB-03']
      ```
    </CodeGroup>

    **代码解释**：

    * **类型注解**：`def calculate_memory(total: int, usage: float) -> str:` 明确标注参数和返回值类型
    * **f-string**：`f"文本 {变量}"` 比字符串拼接更清晰
    * **列表推导式**：`[表达式 for 变量 in 列表 if 条件]` 一行完成筛选和转换

    **运行脚本**：

    ```bash theme={null}
    # 确保虚拟环境已激活
    python 08_syntax_review.py

    # 或者显式使用 python3
    python3 08_syntax_review.py
    ```

    **验证步骤**：

    1. 脚本能正常运行，无语法错误
    2. 输出包含内存使用率计算结果
    3. 输出包含 f-string 格式化的服务器信息
    4. 输出包含列表推导式筛选的结果

    **常见错误**：

    <Warning>
      * ❌ `SyntaxError: invalid syntax` - 可能是 Python 版本太老（f-string 需要 3.6+）
      * ❌ `NameError: name 'x' is not defined` - 变量未定义，检查变量名拼写
      * ❌ `TypeError: unsupported operand type(s)` - 类型不匹配，检查类型注解是否正确
    </Warning>
  </Step>

  <Step title="任务 D：上下文管理器 - 文件操作">
    创建 `08_file_context.py`，演示 `with` 语句的正确用法。

    <CodeGroup>
      ```python 08_file_context.py theme={null}
      #!/usr/bin/env python3
      """
      Day 08 - 上下文管理器示例
      演示为什么必须使用 with 语句处理文件
      """

      log_file = "app.log"

      # ========== 错误示例：不使用 with ==========
      # 这种方式容易导致文件句柄泄漏
      # f = open(log_file, "w")
      # f.write("ERROR: Connection reset\n")
      # # 如果这里发生异常，文件不会被关闭！
      # f.close()

      # ========== 正确示例：使用 with ==========
      # 1. 写入日志文件
      print("=== 写入日志 ===")
      with open(log_file, "w") as f:
          # with 语句会自动调用 f.__enter__()
          f.write("[2026-01-31 10:00:01] INFO: Service started\n")
          f.write("[2026-01-31 10:05:23] ERROR: Database connection timeout\n")
          f.write("[2026-01-31 10:07:00] INFO: Health check passed\n")
          # 退出 with 块时，自动调用 f.__exit__() 关闭文件
          # 即使发生异常，文件也会被正确关闭

      print(f"✅ 日志已写入 {log_file}")

      # 2. 读取并分析日志
      print("\n=== 读取日志 ===")
      error_count = 0
      with open(log_file, "r") as f:
          # 逐行读取（内存友好，适合大文件）
          for line in f:
              # strip() 去除行尾的换行符
              line = line.strip()
              if "ERROR" in line:
                  error_count += 1
                  print(f"🚨 发现错误: {line}")

      print(f"\n总共发现 {error_count} 个错误")

      # 3. 追加日志（不覆盖原有内容）
      print("\n=== 追加日志 ===")
      with open(log_file, "a") as f:  # 'a' 表示 append（追加）
          f.write("[2026-01-31 10:10:00] INFO: New log entry\n")

      # 验证追加成功
      with open(log_file, "r") as f:
          lines = f.readlines()
          print(f"日志文件共有 {len(lines)} 行")
      ```
    </CodeGroup>

    **为什么必须使用 `with`？**

    * **自动关闭**：即使发生异常，文件也会被关闭
    * **防止泄漏**：避免"Too many open files"错误
    * **代码简洁**：不需要手动写 `try-finally`

    **文件模式说明**：

    * `"r"`：只读（文件必须存在）
    * `"w"`：写入（覆盖，文件不存在则创建）
    * `"a"`：追加（在文件末尾添加，文件不存在则创建）
    * `"r+"`：读写（文件必须存在）

    **运行脚本**：

    ```bash theme={null}
    python 08_file_context.py
    ```

    **验证步骤**：

    1. 脚本运行后，检查 `app.log` 文件是否存在
    2. 查看文件内容：`cat app.log`
    3. 确认文件包含写入的日志行
    4. 确认错误计数正确

    **常见错误**：

    <Warning>
      * ❌ `FileNotFoundError: [Errno 2] No such file or directory` - 读取模式（"r"）时文件不存在
      * ❌ `PermissionError: [Errno 13] Permission denied` - 没有文件写入权限
      * ❌ `UnicodeDecodeError` - 文件编码问题，尝试指定编码：`open(file, "r", encoding="utf-8")`
    </Warning>
  </Step>
</Steps>

***

## <span style={{ display: 'inline-flex', alignItems: 'center', gap: '8px' }}><Icon icon="lightbulb" size={26} color="#059669" /> 拓展任务 (30 mins)</span>

<CardGroup cols={2}>
  <Card title="挑战 1：列表推导式进阶" icon="code">
    **任务**：使用列表推导式，从列表 `['apple', 'banana', 'cherry', 'date']` 中选出：

    * 长度大于 5 的单词
    * 包含字母 'n' 的单词
    * 同时满足以上两个条件，并转为大写

    **提示**：

    ```python theme={null}
    # 可以组合多个条件
    result = [word.upper() for word in words if len(word) > 5 and 'n' in word]
    ```
  </Card>

  <Card title="挑战 2：类型注解实践" icon="check">
    **任务**：为以下函数添加类型注解：

    ```python theme={null}
    def process_servers(servers, threshold):
        return [s for s in servers if s['cpu'] > threshold]
    ```

    **提示**：

    * 使用 `List[Dict[str, Any]]` 需要导入：`from typing import List, Dict, Any`
    * 思考：`threshold` 应该是什么类型？
  </Card>
</CardGroup>

***

## <span style={{ display: 'inline-flex', alignItems: 'center', gap: '8px' }}><Icon icon="file" size={26} color="#059669" /> 今日产出物</span>

* `.venv/` - Python 虚拟环境目录
* `requirements.txt` - 项目依赖列表
* `08_syntax_review.py` - 类型注解和 f-string 示例
* `08_file_context.py` - 上下文管理器示例
* `app.log` - 日志文件（由脚本生成）

***

## <span style={{ display: 'inline-flex', alignItems: 'center', gap: '8px' }}><Icon icon="github" size={26} color="#059669" /> 参考代码</span>

<CardGroup cols={2}>
  <Card title="查看参考代码" icon="code" href="https://github.com/akriamail/insightful-ops/tree/main/docs/mintlify/scripts/training/30daystudy/week-2/day-08">
    在 GitHub 查看完整的示例代码

    <br />

    <small>包含 Python 脚本和 requirements.txt</small>
  </Card>

  <Card title="在线运行" icon="play" href="https://replit.com">
    使用在线编辑器测试代码

    <br />

    <small>无需本地环境配置</small>
  </Card>
</CardGroup>

***

## 实际应用场景

<CardGroup cols={2}>
  <Card title="虚拟环境在运维中的应用" icon="server">
    * **多项目隔离**：不同运维脚本使用不同版本的库，互不干扰
    * **生产环境复现**：通过 `requirements.txt` 在生产环境安装相同依赖
    * **CI/CD 集成**：自动化部署时自动创建虚拟环境并安装依赖
    * **版本锁定**：避免因依赖更新导致的意外问题
  </Card>

  <Card title="类型注解与上下文管理器的最佳实践" icon="code">
    * **类型注解**：在函数参数和返回值上标注类型，提高代码可读性和 IDE 支持
    * **f-string**：优先使用 f-string 进行字符串格式化，代码更清晰
    * **上下文管理器**：所有资源操作（文件、数据库、网络）都必须使用 `with` 语句
    * **列表推导式**：简单筛选和转换优先使用列表推导式，代码更 Pythonic
  </Card>
</CardGroup>

<Tip>
  **与 Day 09 的关联**：今天学习的虚拟环境和基础语法，明天会学习更高级的数据结构操作（字典、集合）和装饰器。打好今天的基础，明天的学习会更轻松。
</Tip>

***

<CardGroup cols={2}>
  <Card title="上一周: Shell收官" href="/training/30daystudy/day-07">
    Day 07 | 第一阶段复盘
  </Card>

  <Card title="下一天: 数据结构进阶" href="/training/30daystudy/day-09">
    Day 09 | 数据结构与装饰器
  </Card>
</CardGroup>
