编程进阶网 编程进阶网
首页
  • 计算机原理
  • 操作系统
  • 网络协议
  • 数据库原理
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 性能优化
  • 编程原理
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 基础认知
  • 线性结构
  • 树与哈希
  • 工业级实现
  • 算法思想
  • 实战与综合
  • 算法题考核
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++开发技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript高级
  • Android库解读
  • Android专栏
  • Android智能硬件
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Linux实践开发
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接

杨充

专注编程 · 终身学习者
首页
  • 计算机原理
  • 操作系统
  • 网络协议
  • 数据库原理
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 性能优化
  • 编程原理
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 基础认知
  • 线性结构
  • 树与哈希
  • 工业级实现
  • 算法思想
  • 实战与综合
  • 算法题考核
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++开发技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript高级
  • Android库解读
  • Android专栏
  • Android智能硬件
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Linux实践开发
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接
  • ScriptHub 脚本工具箱
  • Python

    • Python 从入门到实战
    • 入门与基础类型
    • 序列与集合类型
    • 流程控制与函数
    • 面向对象与工程
    • 爬虫全流程实战
    • 数据分析三件套
    • 办公自动化实战
    • 开发环境与规范
      • 8.1 虚拟环境
        • 8.1.1 虚拟环境
        • 8.1.2 venv 使用
        • 8.1.3 pip 依赖
        • 8.1.4 依赖冲突
        • 8.1.5 现代方案
      • 8.2 代码规范
        • 8.2.1 PEP 8 规则
        • 8.2.2 命名约定
        • 8.2.3 文档规范
        • 8.2.4 类型注解
        • 8.2.5 格式化工具
      • 8.3 新手陷阱
      • 8.4 综合思考题
    • 调试与性能优化
    • 部署与并发实战
    • 函数高级特性剖析:装饰器 / 生成器 / 上下文管理器
    • 并发底层原理揭秘
    • 面向对象与类型系统:元类 / 描述符 / 鸭子类型
    • 解释器源码初探
  • Shell-Bash

  • 工具脚本

  • ScriptHub
  • Python
杨充
2019-04-16
目录

开发环境与规范

# 第 8 章 开发环境与规范

# 目录介绍

  • 8.1 虚拟环境与依赖管理
    • 8.1.1 为什么需要虚拟环境?
    • 8.1.2 venv:Python 内置的虚拟环境
    • 8.1.3 pip 依赖管理实战
    • 8.1.4 依赖冲突的本质与解决
    • 8.1.5 pipenv / poetry 现代方案
  • 8.2 代码规范与风格
    • 8.2.1 PEP 8 核心规则速查
    • 8.2.2 命名约定全景
    • 8.2.3 文档字符串(docstring)规范
    • 8.2.4 类型注解(Type Hints)
    • 8.2.5 自动格式化工具链
  • 8.3 新手陷阱 Top 5
  • 8.4 综合思考题

# 8.1 虚拟环境

# 8.1.1 虚拟环境

假设你有两个项目:

  • 项目 A(老项目):依赖 Django==3.2
  • 项目 B(新项目):依赖 Django==5.0

如果你把它们全装到全局 Python——只有一个 Django 能活下来。这就是"依赖地狱"。

🔑 虚拟环境就是为每个项目创建一个隔离的 Python 空间——每个项目有自己的 python、自己的 pip、自己的依赖包,互不干扰:

全局 Python (/usr/bin/python3)
├── 虚拟环境 A (.venv_a/)        ← Django 3.2 + requests 2.28
│   ├── python → /usr/bin/python3(符号链接)
│   └── lib/site-packages/
│       ├── django==3.2
│       └── requests==2.28
│
├── 虚拟环境 B (.venv_b/)        ← Django 5.0 + httpx 0.27
│   └── lib/site-packages/
│       ├── django==5.0
│       └── httpx==0.27
│
└── 全局 site-packages/          ← 保持干净,不装项目依赖
1
2
3
4
5
6
7
8
9
10
11
12
13

# 8.1.2 venv 使用

Python 3.3+ 自带 venv——零安装成本:

# ===== 创建虚拟环境 =====
# 进入项目目录
cd my_project

# 创建虚拟环境(名字通常叫 .venv 或 venv)
python3 -m venv .venv          # 点号开头——Git 默认忽略

# 目录结构:
# .venv/
# ├── bin/          (activate, python, pip...)
# ├── lib/          (site-packages 在这里)
# └── pyvenv.cfg

# ===== 激活虚拟环境 =====
# Linux / macOS
source .venv/bin/activate
# Windows
.venv\Scripts\activate

# 激活后终端前会出现 (.venv) 提示符
# (.venv) $   ← 表示当前在虚拟环境中

# ===== 确认环境 =====
which python              # 应该指向 .venv/bin/python
which pip                 # 应该指向 .venv/bin/pip
python --version          # 虚拟环境中的 Python 版本

# ===== 退出虚拟环境 =====
deactivate

# ===== 删除虚拟环境(简单粗暴——删目录就行) =====
rm -rf .venv              # 不留任何痕迹
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

🔑 .venv 目录不要提交到 Git——加到 .gitignore:

# .gitignore
.venv/
venv/
env/
__pycache__/
*.pyc
.env
1
2
3
4
5
6
7

VS Code 配置——自动识别 .venv:

// .vscode/settings.json
{
    "python.defaultInterpreterPath": "${workspaceFolder}/.venv/bin/python",
    "python.terminal.activateEnvironment": true
}
1
2
3
4
5

# 8.1.3 pip 依赖

# ===== 基本操作 =====
pip install requests                          # 安装最新版
pip install requests==2.31.0                  # 指定版本
pip install "Django>=4.2,<5.0"                # 版本范围
pip install -r requirements.txt               # 批量安装

pip uninstall requests -y                     # 卸载(-y 跳过确认)
pip list                                      # 列出已安装
pip show requests                             # 查看某个包的详细信息
pip check                                     # 检查依赖冲突

# ===== 导出 / 恢复依赖 =====
pip freeze > requirements.txt                 # 导出当前环境所有包
pip install -r requirements.txt               # 在新环境恢复一模一样的依赖

# requirements.txt 示例:
# Django==5.0.6
# requests==2.31.0
# pandas==2.2.2
# openpyxl==3.1.5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

⚠️ pip freeze 的陷阱——它会导出所有安装的包,包括间接依赖:

# pip freeze 导出的内容——包含了"你没手动装但 A 需要 B 所以自动装了"的包
asgiref==3.8.1          ← Django 的依赖——不是你手动装的
Django==5.0.6           ← ✅ 你手动装的
sqlparse==0.5.1         ← Django 的依赖
tzdata==2024.1          ← 系统时区数据

# 更好的做法:手动维护 requirements.in(顶层依赖)
# Django==5.0.6        ← 只写你直接依赖的包
# requests==2.31.0
# 然后用 pip-compile 自动生成完整的 requirements.txt(见 pip-tools)
1
2
3
4
5
6
7
8
9
10

🔑 更好的实践——pip-tools:

pip install pip-tools

# 1. 手动维护 requirements.in(只写顶层依赖)
echo "Django>=4.2" > requirements.in
echo "requests" >> requirements.in

# 2. pip-compile 自动生成完整的 requirements.txt(含所有子依赖 + 锁定版本)
pip-compile requirements.in

# 3. 安装
pip-sync requirements.txt   # 和 pip install -r 的区别:会卸载不在文件中的包!
1
2
3
4
5
6
7
8
9
10
11

# 8.1.4 依赖冲突

场景复现:

pip install package-a==1.0    # package-a 需要 requests>=2.25,<2.30
pip install package-b==2.0    # package-b 需要 requests>=2.30
# ❌ ERROR: Cannot install package-a and package-b
#    因为 requests 版本范围冲突:<2.30 vs >=2.30
1
2
3
4

解决方案:

方案 适用场景 复杂度
① 升级/降级版本 能找到兼容版本时 低
② 用虚拟环境隔离 两个项目完全独立 低
③ pip install --force-reinstall 强制覆盖冲突包(危险) 中
④ 用 pip check 定位冲突 快速诊断 低
# 诊断工具
pip check            # 检查当前环境是否有冲突
pipdeptree           # 可视化依赖树 ← pip install pipdeptree
pipdeptree -r        # 反向依赖树:哪些包依赖了 requests?
1
2
3
4

# 8.1.5 现代方案

pip + requirements.txt 是传统方案——pipenv 和 poetry 提供了锁定文件 + 自动激活等现代能力:

# ===== pipenv =====
pip install pipenv

pipenv install requests            # 安装 + 自动更新 Pipfile
pipenv install --dev pytest        # 开发依赖
pipenv shell                       # 激活虚拟环境(自动创建)
pipenv lock                        # 生成 Pipfile.lock(确定版本)
pipenv install --deploy            # 生产环境:严格按 lock 安装
exit                               # 退出

# Pipfile 示例:
# [[source]]
# url = "https://pypi.org/simple"
# [packages]
# requests = "*"
# [dev-packages]
# pytest = "*"

# ===== poetry =====(最推荐——全功能项目管理) =====
pip install poetry

poetry new my_project              # 创建新项目(含目录结构)
poetry add requests                # 添加依赖
poetry add --dev pytest black      # 开发依赖
poetry install                     # 安装所有依赖
poetry shell                       # 激活虚拟环境
poetry run python main.py          # 不激活也能跑
poetry lock                        # 锁定版本
poetry update                      # 更新依赖
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

🔑 poetry 比 pip 多了什么:

功能 pip + venv poetry
虚拟环境 手动 python -m venv 自动创建和管理
依赖声明 requirements.txt(手动写) pyproject.toml(结构化)
依赖锁定 requirements.txt(需手动更新) poetry.lock(自动锁定子依赖版本)
发布到 PyPI 手动 setup.py + twine poetry publish 一条命令
冲突解决 基础 约束求解器——更准确地发现/解决冲突
# poetry 的 pyproject.toml 示例
# [tool.poetry]
# name = "my_project"
# version = "0.1.0"
#
# [tool.poetry.dependencies]
# python = "^3.10"
# requests = "^2.31"
# pandas = "^2.2"
#
# [tool.poetry.dev-dependencies]
# pytest = "^8.0"
# black = "^24.0"
1
2
3
4
5
6
7
8
9
10
11
12
13

📌 建议:个人脚本用 pip + venv + requirements.txt(最简);团队项目用 poetry(pyproject.toml + lock)。

# 8.2 代码规范

# 8.2.1 PEP 8 规则

PEP 8 是 Python 官方风格指南——全 Python 社区遵循同一份规范:

# ===== 一、缩进:4 个空格——不用 Tab =====
def calculate_total(items):
    total = 0
    for item in items:             # 4 空格
        if item.price > 0:         # 8 空格
            total += item.price    # 12 空格

# ===== 二、行长度:≤ 79 字符(文档 ≤ 72)=====
# ❌ 太长
users = session.query(User).filter(User.age > 18).order_by(User.name.desc()).all()

# ✅ 用括号隐式续行
users = (
    session.query(User)
    .filter(User.age > 18)
    .order_by(User.name.desc())
    .all()
)

# ===== 三、空行:函数间 2 行,类内方法间 1 行 =====
import os                           # import 后 2 行空行
import sys


def func_a():                       # 顶层函数间 2 行空行
    pass


def func_b():
    pass


class MyClass:
    def method_a(self):             # 类内方法间 1 行空行
        pass

    def method_b(self):
        pass

# ===== 四、空格规则 =====
x = 1                    # ✅ 等号两边空格
y = x * 2 + 3            # ✅ 运算符两边空格
d = {"a": 1, "b": 2}     # ✅ 冒号后空格,前不要
func(a=1, b=2)           # ✅ 关键字参数等号两边无空格
lst = [1, 2, 3]          # ✅ 逗号后空格
# x=1                    # ❌ 无空格

# ===== 五、import 顺序:标准库 → 第三方 → 本地 =====
# ① 标准库
import os
import sys
from datetime import datetime

# ② 第三方库
import numpy as np
import requests
from openpyxl import load_workbook

# ③ 本地模块
from my_project.models import User
from my_project.utils import helpers

# ===== 六、逗号尾随 =====
# 多行列表最后加逗号——方便用 Git 看每次只改一行
config = {
    "host": "localhost",
    "port": 8080,
    "debug": True,        # ← 这个逗号让 git diff 更干净
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69

# 8.2.2 命名约定

类型 约定 示例
变量 / 函数 snake_case user_name, calculate_total()
类名 CamelCase StudentRecord, BankAccount
常量 UPPER_SNAKE_CASE MAX_SIZE, DEFAULT_TIMEOUT
模块 / 文件 snake_case user_service.py, data_loader.py
包 / 目录 snake_case(无下划线更好) models, data_analysis
私有(约定) _leading_underscore _internal_method(), _cache
名称改写 __dunder(双下划线开头) __private_attr
避免 单字符 l, O, I 和数字 1/0 难以区分
# ✅ PEP 8 风格
class UserProfile:
    MAX_LOGIN_ATTEMPTS = 5                          # 类常量

    def __init__(self, user_name: str, email: str):
        self.user_name = user_name                   # 实例属性
        self._login_attempts = 0                     # 保护属性
        self.__reset_token = None                    # 名称改写(§4.1.3)

    def is_account_locked(self) -> bool:             # 布尔方法用 is_ 前缀
        return self._login_attempts >= self.MAX_LOGIN_ATTEMPTS

# ❌ 不规范——混合了多种命名风格
class userProfile:                                    # 类名应为 CamelCase
    maxLoginAttempts = 5                              # 常量应为 UPPER_CASE

    def IsAccountLocked(self):                        # 函数应为 snake_case
        ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

🔑 命名不是美学问题——是维护成本问题:

  • calculate_total_tax_for_order() 看一眼就知道做什么
  • cttfo() 猜 30 秒——然后钻进去读代码才知道

# 8.2.3 文档规范

Python 的 docstring 用 """...""",写在函数/类/模块的第一行:

def connect_database(host: str, port: int = 5432,
                     user: str = "admin",
                     database: str = "main") -> "DatabaseConnection":
    """连接到 PostgreSQL 数据库。

    本函数会尝试连接三次,如果全部失败则抛出 DatabaseError。

    Args:
        host: 数据库主机地址。
        port: 端口号,默认 5432。
        user: 用户名。
        database: 数据库名。

    Returns:
        DatabaseConnection: 连接对象,可以执行 SQL 查询。

    Raises:
        ConnectionError: 三次重试后仍然无法连接。

    Examples:
        >>> conn = connect_database("db.example.com", user="reader")
        >>> conn.execute("SELECT COUNT(*) FROM users")
        1523
    """
    ...  # 实现略
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

🔑 三种主流风格:

风格 适用 示例
Google 风格 企业项目 Args: / Returns: / Raises: ——可读性最好
Sphinx 风格 文档生成 :param: / :return: ——Sphinx 原生
Numpy 风格 科学计算 Parameters\n----------\n

本教程统一使用 Google 风格——因为它简单、清晰、IDE 支持最好。

# 8.2.4 类型注解

Python 3.5+ 支持类型注解——不会被运行时强制检查,但 IDE 和 mypy 能发现类型错误:

# 基本类型注解
def greet(name: str, age: int) -> str:        # 参数类型:冒号后;返回类型:箭头后
    return f"{name} 今年 {age} 岁"

# 集合类型(Python 3.9+)
def process_scores(scores: list[int]) -> dict[str, float]:
    return {
        "max": max(scores),
        "avg": sum(scores) / len(scores),
    }

# 可选类型
from typing import Optional
def find_user(user_id: int) -> Optional[dict]:
    """可能查不到——返回 None"""
    ...

# 联合类型(Python 3.10+)
def parse_value(raw: str) -> int | float | str:
    try:
        return int(raw)
    except ValueError:
        try:
            return float(raw)
        except ValueError:
            return raw

# 自定义类型别名
from typing import TypeAlias
UserId: TypeAlias = int
JsonDict: TypeAlias = dict[str, "JsonValue"]   # 递归类型需加引号

# 实际开发中的完整注解
from dataclasses import dataclass

@dataclass
class Order:
    order_id: int
    customer: str
    amount: float
    items: list[str]       # 不是 list——IDE 能推断出是字符串列表
    paid: bool = False

def get_high_value_orders(orders: list[Order], threshold: float = 1000.0) -> list[Order]:
    return [o for o in orders if o.amount >= threshold]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

🔑 类型注解的价值:

# 没有类型注解——IDE 无法推断
def process(data):
    data.    # ← IDE 无法自动补全!不知道 data 是什么类型

# 有类型注解——IDE 知道一切
def process(data: list[Order]):
    data[0].amou    # ← IDE 自动补全 .amount!
1
2
3
4
5
6
7

静态检查——mypy:

pip install mypy
mypy my_project/            # 静态分析——不运行代码就发现类型错误

# mypy 输出示例:
# main.py:15: error: Argument 1 to "greet" has incompatible type "int"; expected "str"
1
2
3
4
5

# 8.2.5 格式化工具

人工遵守 PEP 8 太费脑——用工具自动格式化:

pip install black flake8 isort mypy pre-commit

# ===== black:代码格式化(不可协商的风格) =====
black .                        # 格式化当前目录所有 Python 文件
black --check .                # 只检查,不改(适合 CI)
black --line-length 100 .      # 自定义行宽(默认 88)

# 格式化前:
x =    {  'a':1,'b':  2,}
result=some_function(arg1,arg2,arg3,    arg4)

# 格式化后:
x = {"a": 1, "b": 2}
result = some_function(arg1, arg2, arg3, arg4)

# ===== isort:import 排序 =====
isort .                        # 自动排序所有 import

# 排序前:
from my_project.models import User
import os
import requests
from datetime import datetime

# 排序后:
import os
from datetime import datetime

import requests

from my_project.models import User

# ===== flake8:代码检查(发现未使用的 import、未定义的变量等)=====
flake8 .                       # 检查但不修改

# ===== 一条命令格式化全部 =====
black . && isort . && flake8 .
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

🔑 pre-commit:Git 提交前自动检查——最佳实践:

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/psf/black
    rev: 24.4.0
    hooks:
      - id: black
        language_version: python3.12

  - repo: https://github.com/PyCQA/isort
    rev: 5.13.2
    hooks:
      - id: isort

  - repo: https://github.com/PyCQA/flake8
    rev: 7.0.0
    hooks:
      - id: flake8
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
pip install pre-commit
pre-commit install              # 安装到当前仓库的 .git/hooks/
# 以后每次 git commit 都会自动跑 black + isort + flake8——
# 不规范就拒绝提交
1
2
3
4

工具链速查:

工具 作用 是否修改代码
black 自动格式化——统一风格 ✅ 修改
isort import 排序分组 ✅ 修改
flake8 检查 PEP 8 违规 ❌ 只报错
mypy 静态类型检查 ❌ 只报错
pre-commit Git 提交钩子——串联以上工具 ❌ 拒绝提交

# 8.3 新手陷阱

# 陷阱 说明
1 全局 pip install pip install 不加虚拟环境——污染全局 Python,不同项目的依赖互相冲突
2 忘记激活虚拟环境 pip install 后发现装到了全局——每次新开终端先 source .venv/bin/activate
3 pip freeze 导出所有包 连间接依赖都锁死——跨平台时包不兼容。用 pip-compile 或只维护顶层依赖
4 命名用 l / O / I l = 1 看起来像 l = l——永远避免单字符变量使用易混淆字母
5 类型注解只在参数写 list def f(items: list) 没有元素类型——IDE 不知道 items[0] 是什么。写 list[str]

陷阱 3 详解——跨平台 freeze 的坑:

# macOS 上 pip freeze 导出
# torch==2.1.0        ← macOS 版
# 到 Linux 服务器 pip install -r requirements.txt
# ERROR: No matching distribution found for torch==2.1.0

# ✅ 解决方案:只写 torch>=2.0(语义化版本),不加平台后缀
1
2
3
4
5
6

# 8.4 综合思考题

  1. poetry vs pip + venv 的团队规模边界:一个人的项目用 pip + venv 够用,5 个人的团队呢?20 个人呢?在多大团队规模下,poetry 的 pyproject.toml + lock 机制成为必要?

  2. 类型注解的"假安全":Python 的 list[int] 在运行时完全不检查——func([1, "hello"]) 不会报错。那类型注解的价值到底在哪?为什么 Python 不学 TypeScript 那样强制运行时检查?

  3. black 的"不可协商"哲学:black 只暴露了 --line-length 一个配置——所有其他风格都不可配置。这引来了很多争论——如果你不喜欢 88 字符的行宽怎么办?这种"独裁式"设计对团队有什么好处?

  4. PEP 8 的 trade-off:PEP 8 规定行宽 ≤ 79 字符——这个数字来自 1980 年代的 80 列终端。现在超宽显示器普及,为什么社区仍然建议 79 字符?这和"并排比较两个文件"有什么关系?

  5. 依赖锁定 vs 依赖范围:requests==2.31.0(精确锁定)和 requests>=2.31,<3.0(范围声明)——前者保证部署一致性,后者允许安全补丁自动升级。二者各适用于什么场景?在 requirements.in + requirements.txt 双文件方案中,如何实现"开发锁精确版本,部署留升级余地"?

#Python#技巧
上次更新: 2026/06/17, 12:47:39
办公自动化实战
调试与性能优化

← 办公自动化实战 调试与性能优化→

最近更新
01
信号崩溃快速排查
06-15
02
CoreDump破案
06-15
03
perf火焰图实战
06-15
更多文章>
Theme by Vdoing | Copyright © 2019-2026 杨充 | MIT License | 桂ICP备2024034950号 | 桂公网安备45142202000030
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式