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

学习内容 (30 mins)

在开始写代码前,先搞懂这些核心概念,否则后面的代码你会看得云里雾里。
什么是虚拟环境?虚拟环境是一个独立的 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(从文件安装)
  • 为什么需要冻结:确保开发、测试、生产环境使用相同版本的包,避免”在我机器上能跑”的问题
类型注解 (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:

代码任务 (90 mins)

1

环境准备

在开始之前,确保你的环境已准备好:
  • Python 环境:需要 Python 3.7+(推荐 3.9+)
  • 文本编辑器:继续使用 nano 或 vi/vim(如果还不熟悉,参考 Day 01 的编辑器说明)
  • 检查 Python 版本python3 --version
# 检查 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
2

任务 A:创建虚拟环境

在项目目录下创建一个独立的 Python 环境。
# 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
验证步骤
  1. 检查 Python 路径:which python 应该指向 .venv/bin/python
  2. 检查 pip 路径:which pip 应该指向 .venv/bin/pip
  3. 检查提示符:命令行前面应该有 (.venv) 标识
常见错误
  • 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 文件是否存在
3

任务 B:包管理与依赖冻结

安装一个常用包并导出依赖列表。
# 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
为什么需要 requirements.txt?
  • 团队协作:团队成员可以安装完全相同的依赖
  • 环境复现:生产环境可以精确复现开发环境
  • 版本锁定:避免因依赖更新导致的”昨天还能跑,今天就不行了”
验证步骤
  1. 检查 requirements.txt 文件是否存在
  2. 检查文件内容是否包含 requests== 开头的行
  3. 尝试在新虚拟环境中安装:pip install -r requirements.txt
4

任务 C:编写 Python 脚本 - 类型注解与 f-string

创建 08_syntax_review.py,练习类型注解和 f-string。
#!/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']
代码解释
  • 类型注解def calculate_memory(total: int, usage: float) -> str: 明确标注参数和返回值类型
  • f-stringf"文本 {变量}" 比字符串拼接更清晰
  • 列表推导式[表达式 for 变量 in 列表 if 条件] 一行完成筛选和转换
运行脚本
# 确保虚拟环境已激活
python 08_syntax_review.py

# 或者显式使用 python3
python3 08_syntax_review.py
验证步骤
  1. 脚本能正常运行,无语法错误
  2. 输出包含内存使用率计算结果
  3. 输出包含 f-string 格式化的服务器信息
  4. 输出包含列表推导式筛选的结果
常见错误
  • SyntaxError: invalid syntax - 可能是 Python 版本太老(f-string 需要 3.6+)
  • NameError: name 'x' is not defined - 变量未定义,检查变量名拼写
  • TypeError: unsupported operand type(s) - 类型不匹配,检查类型注解是否正确
5

任务 D:上下文管理器 - 文件操作

创建 08_file_context.py,演示 with 语句的正确用法。
#!/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)} 行")
为什么必须使用 with
  • 自动关闭:即使发生异常,文件也会被关闭
  • 防止泄漏:避免”Too many open files”错误
  • 代码简洁:不需要手动写 try-finally
文件模式说明
  • "r":只读(文件必须存在)
  • "w":写入(覆盖,文件不存在则创建)
  • "a":追加(在文件末尾添加,文件不存在则创建)
  • "r+":读写(文件必须存在)
运行脚本
python 08_file_context.py
验证步骤
  1. 脚本运行后,检查 app.log 文件是否存在
  2. 查看文件内容:cat app.log
  3. 确认文件包含写入的日志行
  4. 确认错误计数正确
常见错误
  • FileNotFoundError: [Errno 2] No such file or directory - 读取模式(“r”)时文件不存在
  • PermissionError: [Errno 13] Permission denied - 没有文件写入权限
  • UnicodeDecodeError - 文件编码问题,尝试指定编码:open(file, "r", encoding="utf-8")

拓展任务 (30 mins)

挑战 1:列表推导式进阶

任务:使用列表推导式,从列表 ['apple', 'banana', 'cherry', 'date'] 中选出:
  • 长度大于 5 的单词
  • 包含字母 ‘n’ 的单词
  • 同时满足以上两个条件,并转为大写
提示
# 可以组合多个条件
result = [word.upper() for word in words if len(word) > 5 and 'n' in word]

挑战 2:类型注解实践

任务:为以下函数添加类型注解:
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 应该是什么类型?

今日产出物

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

参考代码

查看参考代码

在 GitHub 查看完整的示例代码

在线运行

使用在线编辑器测试代码

实际应用场景

虚拟环境在运维中的应用

  • 多项目隔离:不同运维脚本使用不同版本的库,互不干扰
  • 生产环境复现:通过 requirements.txt 在生产环境安装相同依赖
  • CI/CD 集成:自动化部署时自动创建虚拟环境并安装依赖
  • 版本锁定:避免因依赖更新导致的意外问题

类型注解与上下文管理器的最佳实践

  • 类型注解:在函数参数和返回值上标注类型,提高代码可读性和 IDE 支持
  • f-string:优先使用 f-string 进行字符串格式化,代码更清晰
  • 上下文管理器:所有资源操作(文件、数据库、网络)都必须使用 with 语句
  • 列表推导式:简单筛选和转换优先使用列表推导式,代码更 Pythonic
与 Day 09 的关联:今天学习的虚拟环境和基础语法,明天会学习更高级的数据结构操作(字典、集合)和装饰器。打好今天的基础,明天的学习会更轻松。

上一周: Shell收官

Day 07 | 第一阶段复盘

下一天: 数据结构进阶

Day 09 | 数据结构与装饰器