目录

前言

一、Python 版本选择与版本兼容

1.1 为什么不能"随便用系统自带的 Python"

1.2 2026 年版本怎么选

1.3 版本兼容三件套

二、uv、虚拟环境与依赖管理

2.1 依赖地狱是怎么来的

2.2 传统方案链路:venv + pip + pip-tools

2.3 现代方案:uv 一把梭

2.4 一次 uv sync 背后发生了什么

2.5 常见踩坑

三、pyproject.toml

3.1 它解决了什么:碎片化配置的终结

3.2 三大核心区块

3.3 从最小可用到生产可用

3.4 高频字段速查表

四、包、模块与导入机制

4.1 先厘清三个概念:模块、包、命名空间包

4.2 导入机制怎么工作

4.3 包结构关系图

4.4 两大经典报错拆解

4.5 常见导入报错速查表

五、Python 项目目录结构

5.1 两种主流布局:flat vs src

5.2 一个生产级目录结构(推荐模板)

5.3 项目目录组织图

5.4 可选目录与常见坏结构

六、总结:一条主线,串起五件套


前言

很多人 Python 写得挺溜,可一旦要把脚本做成"项目",问题就来了:

  • 同一台电脑,A 项目要 requests==2.28,B 项目要 requests==2.31,装谁谁打架;
  • 拉下同事的代码一跑,ImportErrorModuleNotFoundError 满屏飞;
  • 自己的项目里几十个 .py 全堆在根目录,文件名起得像 utils_v2_final.py
  • 依赖列表昨天还能跑,今天 pip install 一下就崩——因为没人锁过版本。

这些坑其实都不是"语法不行",而是没系统接触过工程化。Python 这门语言入门简单,但要把一个项目做"规范、可维护、能协作",得补上一组工程化基本功。这篇文章就把其中最核心的五件套一次讲透。

它们不是并列关系,而是一条递进主线:

版本定地基 → 环境做隔离 → 依赖锁版本 → 配置收一处 → 结构定规矩


一、Python 版本选择与版本兼容

1.1 为什么不能"随便用系统自带的 Python"

很多人初学时直接用 macOS / Linux 系统自带的那个 python3,觉得省事。但系统 Python 是给操作系统自己用的,它的版本由发行版决定,你动它(升级、装全局包)可能影响系统工具;而且系统自带版本往往偏旧,新语法、新标准库用不了。

正确的姿势是:自己装一份独立管理的 Python(用 uv、pyenv、官方安装包等),项目爱用哪个版本就用哪个,和系统 Python 彻底解耦。

1.2 2026 年版本怎么选

讲版本离不开一个词:EOL(End of Life,生命周期终点)。Python 每个版本官方会给大约 5 年支持期,过期后不再有安全补丁。选版本第一原则就是——别选已经 EOL 或快 EOL 的版本

截至 2026 年,主流版本的现状如下:

Python 版本

发布年份

EOL 时间

定位(2026 年视角)

关键新特性

3.9

2020

2025-10(已 EOL)

不再推荐

字典合并运算符 |

3.10

2021

2026-10(临近 EOL)

仅在维护老项目时碰

结构化模式匹配 match/case

3.11

2022

2027-10

兼容性下限常用值

解释器提速 10–60%

3.12

2023

2028-10

求稳首选

更好的错误提示、f-string 语法放宽

3.13

2024

2029-10

生产新项目首选

实验性 free-threading、改进 REPL

3.14

2025

2030-10

较新,可尝鲜

模板字符串、尾调用解释器

一句话建议:新项目主选 3.13;团队求稳、依赖还没完全适配 3.13 的,选 3.12requires-python 下限设到 3.113.10 是性价比比较高的兼容区间。

生活化类比:选 Python 版本就像给房子打地基。地基版本定了,上面盖的楼(你的代码、依赖库)才稳。而老房子(老旧第三方库)能不能撑住新地基,得看"兼容性"——这就是下一节要解决的。

1.3 版本兼容三件套

工程项目要同时回答两个问题:这个项目至少需要哪个版本的 Python? 以及 运行时如何根据版本做差异化处理? 三件套分别解决:

# ① 在 pyproject.toml 里锁下限 —— 声明式,安装阶段就拦截不兼容版本
# [project]
# requires-python = ">=3.11"

# ② 运行时检查版本 —— 程序内根据版本走不同逻辑
import sys

if sys.version_info < (3, 12):
    # 3.12 之前的写法
    from typing_extensions import TypedDict
else:
    # 3.12+ 已内置
    from typing import TypedDict

# ③ 用 __future__ 提前"借用"新版本的语义(如注解延迟求值)
from __future__ import annotations

三者各司其职:requires-python闸门(早期拦截),sys.version_info开关(运行时分流),__future__预尝鲜(让旧版本用上新语义)。工程项目里,第一个最常用、最重要。

这一章是 P1,"熟悉"即可,不用死记。重点是把"不能用系统 Python、要选仍在支持的版本、用 requires-python 锁下限"这三件事记牢,剩下都是查文档的事。


二、uv、虚拟环境与依赖管理

这是全文最重的一章。Python 工程化的几乎所有早期痛苦,都源于环境与依赖没管好。

2.1 依赖地狱是怎么来的

设想这个场景:你打开终端,敲下

pip install requests==2.28

这条命令默认会把 requests 装到系统全局的 Python 里。下次你做 B 项目,又敲:

pip install requests==2.31

系统里的 requests 就被升级了,A 项目再一跑,行为变了,甚至直接报错——因为它依赖的是旧版本的某个 API。项目越多、依赖越乱,最后谁也不知道某个库到底装的是什么版本。这就是俗称的依赖地狱(Dependency Hell)

破局之道只有一个:给每个项目一个独立的 Python 环境,互不串味。

生活化类比:虚拟环境就像给每个项目分一间独立工作间。木工间放木工工具、电工间放电工工具,互不串味。你在 A 项目工作间装 requests==2.28,B 项目工作间装 requests==2.31,两不相干。

2.2 传统方案链路:venv + pip + pip-tools

长久以来,标准的做法是这样的三件套:

# ① 创建虚拟环境(在项目目录下生成一个 .venv 文件夹)
python -m venv .venv

# ② 激活环境(Windows / 类 Unix 命令不同)
# Windows:
.venv\Scripts\activate
# macOS / Linux:
source .venv/bin/activate

# ③ 在激活的环境里装包
pip install requests pandas

# ④ 导出依赖(生成 requirements.txt)
pip freeze > requirements.txt

# ⑤ 别人拉到项目后,复现你的环境
pip install -r requirements.txt

这套流程能跑通,但有几个固有缺陷,越往大项目走越痛:

缺陷

说明

后果

requirements.txt 不是锁文件

只记录"装了什么版本",不记录 hash

别人安装时可能拿到被篡改/不同的包

不区分直接依赖与间接依赖

pip freeze 把所有包一锅炖

升级困难,分不清哪些是你要的、哪些是连带装的

速度慢

pip 是纯 Python 实现,串行下载

大项目装一次几分钟,调试体验差

工具割裂

venv、pip、pip-tools、virtualenv 各管一摊

心智负担重,新手容易搞混

为了缓解第 1、2 个问题,社区搞出了 pip-toolspip-compile + pip-sync),能生成带 hash 的 .lock 风格文件。但工具链依然割裂、速度依然慢。直到 uv 出现,局面才被彻底改写。

2.3 现代方案:uv 一把梭

uv 是 Astral 公司(也就是做 Ruff 那家)用 Rust 重写的 Python 包管理器。它一出场就把 venv + pip + pip-tools(甚至还能替代 pipx、pyenv 的一部分职责)整合到了一个命令里。

它的"快"不是吹的:底层 Rust 实现、全局缓存(同一个包只下一次,不同项目复用)、并行下载。实测大项目装依赖,从 pip 的两三分钟降到 uv 的几秒。

更重要的是,uv 把虚拟环境和依赖锁文件都内化了,你不用再手动 activate、不用纠结要不要提交 requirements.txt。核心就一个命令:uv sync

# ① 新建项目(自动建好 pyproject.toml + .venv + 示例结构)
uv init my_project
cd my_project

# ② 加依赖(自动解析、自动锁定、自动写入 .venv)
uv add requests pandas

# ③ 加开发依赖(不进生产)
uv add --dev pytest ruff

# ④ 同步环境(别人拉到项目后只需这一条命令)
uv sync

它的工作流是这样的:

  • pyproject.toml 里只写你直接要的依赖requestspandas),不写版本号或只写宽松约束。
  • uv.lock 是 uv 自动生成的锁文件,精确记录每个包(包括间接依赖)的版本和 hash,应当提交到 Git
  • .venv/ 是实际环境,不提交(加进 .gitignore)。
  • 任何人拿到代码,敲 uv sync,uv 会读 uv.lock精确复现你当时的环境——hash 都对得上。

这一套解决了第 2.2 节列的全部缺陷:锁文件带 hash、自动区分直接/间接依赖、快、工具统一。

下面这张对照表建议收藏,迁移时照着查:

你想做的事

传统方案

uv 方案

创建虚拟环境

python -m venv .venv

uv venv(或 uv init 时自动建)

激活环境

source .venv/bin/activate

不用手动激活,uv 命令自动用项目 .venv

装一个包

pip install requests

uv add requests

装开发依赖

pip install pytest(手动区分)

uv add --dev pytest

锁定依赖

pip freeze > requirements.txt

自动生成 uv.lock

精确复现环境

pip install -r requirements.txt

uv sync

升级某个包

pip install -U requests

uv lock --upgrade-package requests

全局跑工具

pipx install ruff

uv tool install ruff

指定 Python 版本

pyenv install 3.13

uv python install 3.13

2.4 一次 uv sync 背后发生了什么

理解 uv 的流程,对你排查"为什么 sync 后还是报错"很有帮助。下面这张流程图把 uv sync 拆开了:

看懂这个流程,几个常见疑问就豁然开朗:

  • "我改了 pyproject.toml,为什么环境没变?" —— 因为还没 uv sync,uv 不会主动改 .venv
  • "lock 文件该不该提交?" —— 必须提交,它是团队复现环境的唯一依据。
  • "为什么 sync 比第一次快那么多?" —— 全局缓存命中,包不用重下。

2.5 常见踩坑

  • 忘了在项目根目录操作:uv 靠 pyproject.toml 识别项目,cd 错目录就找不到。
  • 手动 pip install 到全局:习惯没改过来,绕过了 uv,依赖又乱套。养成"装包只走 uv add"的习惯。
  • .venv 误提交:它不该进 Git,uv init 默认会生成 .gitignore 帮你挡掉,但自己要确认。
  • uv.lock 没提交:团队协作的致命错误,导致同事环境各装各的。

这章是全文 P0 中的 P0。建议你立刻新建一个空目录跑一遍 uv init → uv add → uv sync,比读十遍文档都有用。


三、pyproject.toml

上一章你已经反复看到 pyproject.toml 这个文件了,它就是 Python 现代工程的配置中枢。这一章专门讲它。

3.1 它解决了什么:碎片化配置的终结

pyproject.toml 之前,一个 Python 项目的配置散落在五六个文件里:

旧文件

管什么

痛点

setup.py

打包、元数据、依赖

可执行代码,易出错、难审计

requirements.txt

运行依赖

不锁间接依赖、无 hash

requirements-dev.txt

开发依赖

又一个文件要维护

setup.cfg

静态元数据、工具配置

和 setup.py 职责重叠

.flake8 / mypy.ini

代码检查配置

每个工具一个文件

MANIFEST.in

打包额外文件

容易漏

PEP 518 和 PEP 621 出来后,社区达成共识:所有配置收进一个 pyproject.toml。从此项目根目录清爽多了。

生活化类比pyproject.toml 就像项目的"身份证 + 说明书"。别人(或机器:构建工具、IDE、CI)拿到它,一眼就知道:这项目叫什么、谁做的、依赖什么、怎么构建、用什么工具检查。

3.2 三大核心区块

一个完整的 pyproject.toml 通常分三块,理解这三块就够了:

# ① [project] —— 项目元数据 + 依赖(PEP 621 定义)
[project]
name = "my_project"
version = "0.1.0"
description = "一个示例项目"
requires-python = ">=3.11"
dependencies = [
    "requests>=2.31",
    "pandas>=2.0",
]

# ② [build-system] —— 怎么打包这个项目(PEP 518 定义)
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

# ③ [tool.*] —— 各种工具的配置(ruff、pytest、uv 等都收这儿)
[tool.ruff]
line-length = 100
target-version = "py312"

[tool.pytest.ini_options]
testpaths = ["tests"]

[tool.uv]
dev-dependencies = ["pytest", "ruff"]

三块职责清晰:

  • [project]是什么(名字、版本、依赖)。这是最核心的一块,几乎所有工具都会读它。
  • [build-system]怎么打包。指定构建后端(hatchling、setuptools、flit 等)。如果你只是写个不可分发的脚本应用,可以加 [tool.uv] package = false 跳过打包。
  • [tool.*]怎么用工具。每个工具一个子表,集中管理。

3.3 从最小可用到生产可用

不要被"完整配置"吓到,实际开发是渐进的。下面看一个最小可用版本:

# 最小版本:能跑就行
[project]
name = "my_project"
version = "0.1.0"
requires-python = ">=3.11"
dependencies = ["requests"]

随着项目长大,逐步丰富成生产可用版:

[project]
name = "my_project"
version = "0.1.0"
description = "一个数据采集与处理的服务"
readme = "README.md"
requires-python = ">=3.11"
authors = [{ name = "your_name", email = "you@example.com" }]
license = { text = "MIT" }
keywords = ["data", "crawler"]

# 运行依赖:宽松约束,由 uv.lock 锁死精确版本
dependencies = [
    "requests>=2.31",
    "pandas>=2.0,<3.0",
    "pydantic>=2",
]

# 可选依赖组:装的时候按需选
# uv pip install -e ".[dev]" 或 uv sync --extra dev
[project.optional-dependencies]
dev = ["pytest>=8", "ruff>=0.5"]
docs = ["mkdocs", "mkdocs-material"]

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[tool.ruff]
line-length = 100
target-version = "py312"

[tool.uv]
dev-dependencies = ["pytest>=8", "ruff>=0.5"]

注意 dependencies[tool.uv].dev-dependencies 的区别:前者是生产也要装的运行依赖,后者是只在开发时装的工具链(pytest、ruff 之类,不该进生产镜像)。[project.optional-dependencies] 则是给"按需功能"分组用的。

3.4 高频字段速查表

字段 / 区块

作用

示例

name

包名(必填,发布到 PyPI 的名字)

name = "my_project"

version

版本号(必填)

version = "0.1.0"

requires-python

支持的 Python 版本下限

requires-python = ">=3.11"

dependencies

运行依赖列表

["requests>=2.31"]

[project.optional-dependencies]

可选依赖组(功能/分组)

dev = ["pytest"]

[tool.uv].dev-dependencies

开发依赖(不进生产)

["pytest", "ruff"]

[build-system].build-backend

构建后端

"hatchling.build"

[tool.ruff]

Ruff 代码检查配置

line-length = 100

[tool.pytest.ini_options]

pytest 配置

testpaths = ["tests"]

readme / description / license

元数据

"README.md" / "MIT"

这章的核心就一句话:Python 项目的所有配置,先想"能不能塞进 pyproject.toml"。能塞就别再开新文件。


四、包、模块与导入机制

写项目就绕不开"导入"。这一章把 Python 导入机制讲清楚,让你以后看到 ImportError 不再两眼一抹黑。

4.1 先厘清三个概念:模块、包、命名空间包

很多人混用"模块"和"包",其实定义很明确:

  • 模块(module):一个 .py 文件就是一个模块。文件名即模块名。
  • 包(package):一个包含 __init__.py 的目录,里面可以放多个模块。包可以嵌套(子包)。
  • 命名空间包(namespace package):Python 3.3+ 引入,允许一个包的逻辑内容跨多个目录甚至多个发行版,不需要 __init__.py。大型框架分拆时才用,新手用不到,知道有这么个东西即可。

举个具体例子:

my_project/
├── core/                 # 这是一个"包"(有 __init__.py)
│   ├── __init__.py
│   ├── models.py         # 这是一个"模块"
│   └── utils.py          # 这也是一个"模块"
└── api/
    ├── __init__.py
    └── routes.py

4.2 导入机制怎么工作

当你写下 import core.models 时,Python 不是凭空找到它的。它走的是这套流程:

第一,查 sys.path 这是一个路径列表,Python 依次在这些目录里找你导入的名字。你可以打印看看:

import sys
print(sys.path)
# ['', '/usr/lib/python313.zip', '/usr/lib/python3.13', ..., '/你的项目/.venv/lib/python3.13/site-packages']

第一个空字符串 '' 表示"当前目录"——这就是为什么你在项目根目录跑脚本时,能直接 import core。但如果你从别的目录跑,当前目录变了,可能就 import 不到了,这是新手最常见的坑之一。

生活化类比:导入机制像图书馆借书系统。sys.path 是"索书路线单"——系统按单子上的书架顺序一个个找,找到就停,找不到就报 ModuleNotFoundError。你想借的书(模块)如果没登记在路线单的任何书架上,系统就告诉你"查无此书"。

第二,遇到包目录,读 __init__.py 它有两个作用:

  • 标记"这是个包"(传统包的标志,虽然 3.3+ 没它也能成命名空间包,但显式写上更清晰)。
  • 包初始化代码:可以在这里提前导入子模块,让外部用 from core import models 而不必写 from core import models(实际等价,但可控制暴露什么)。

第三,绝对导入 vs 相对导入。

# 绝对导入:从顶层包写完整路径,清晰、推荐
from core.models import User
from core.utils import helper

# 相对导入:用 . 表示当前包,.. 表示上级包,只在包内部用
from .models import User      # 当前包的 models
from ..api.routes import api  # 上一级包的 api.routes

经验法则:包内部模块互相引用可以用相对导入(重构改包名时不用改导入);对外/顶层脚本用绝对导入更稳妥。新手优先全部用绝对导入,少踩坑。

4.3 包结构关系图

下面这张图展示一个典型多层级包的导入依赖关系(箭头表示"依赖于/导入了"):

看这张图能直观理解:main.py 是入口,依赖 coreapi 两个包;routes.py相对导入..core)回到上级引用 core.models__init__.py 控制对外暴露哪些模块。

4.4 两大经典报错拆解

报错一:ModuleNotFoundError / ImportError

绝大多数情况是这两类原因:

#  错误场景:从子目录直接运行脚本,sys.path 里没有项目根
# 你在 my_project/core/ 下执行 python services.py
from .models import User  # 报错:attempted relative import with no known parent package

根因:sys.path 不对,或运行方式不对。解法:

#  正确:从项目根目录用模块方式运行
python -m core.services          # 推荐
# 或安装自己的包后直接用
uv pip install -e .              # 以"可编辑"模式安装,全局可见

报错二:循环导入(circular import)

a.pyfrom b import xb.py 里又 from a import y——导入时互相等待,直接崩。

#  循环导入
# a.py
from b import do_b
def do_a(): return do_b()

# b.py
from a import do_a   #  a 还没初始化完,拿不到 do_a
def do_b(): return do_a()

三种解法,按推荐顺序:

# 解法一:推迟导入(把 import 放到函数内部)
def do_b():
    from a import do_a   # 调用时才导入,避开了模块加载期的循环
    return do_a()

# 解法二:重构,把共用逻辑抽到第三个模块 c.py
# a.py 和 b.py 都 from c import shared,a、b 之间不再互相依赖

# 解法三:只为类型注解时,用 TYPE_CHECKING
from typing import TYPE_CHECKING
if TYPE_CHECKING:
    from a import do_a   # 运行时不导入,只给类型检查器看

4.5 常见导入报错速查表

报错信息

根因

解法

ModuleNotFoundError: No module named 'x'

模块不在 sys.path 任何路径下

检查运行目录;或 uv pip install -e . 安装自己

ImportError: attempted relative import with no known parent package

直接运行了包内脚本,没有父包上下文

改用 python -m pkg.module 运行

ImportError: cannot import name 'x' from 'y'

循环导入导致名字还没定义

推迟导入 / 重构 / TYPE_CHECKING

导入成功但行为怪异

同名模块遮蔽(如把文件命名为 email.py

改名,避开标准库名

这章是编码排错的高发区。记住排查三连:看运行目录(sys.path 对不对)→ 看包结构(有没有 __init__.py)→ 看依赖方向(有没有环)


五、Python 项目目录结构

最后一章,把前面所有东西"装"进一个合理的目录结构里。好的结构让你导入顺手、测试好写、协作不乱;坏的结构会让你每加一个功能都举步维艰。

5.1 两种主流布局:flat vs src

历史上项目有两种布局:

扁平布局(flat layout):源码直接放根目录。

my_project/
├── core/              # 源码包就在根
├── api/
├── tests/
└── pyproject.toml

src 布局(src layout):源码统一放进 src/ 目录。

my_project/
├── src/
│   └── core/          # 源码包在 src 下
│   └── api/
├── tests/
└── pyproject.toml

生产推荐 src 布局。为什么?因为它有一个关键好处:强制你以"安装后"的方式测试

扁平布局的坑是:你在项目根目录跑测试时,Python 把当前目录加进 sys.path,于是 import core 直接命中了本地源码文件夹,而不是"安装版本"。这意味着:

  • 你可能"测试一直通过",但发布出去后别人装你的包却 import 不到(因为打包配置漏了文件);
  • 你改了源码,本地测试用的是改过的,跟发布的不一致。

src 布局把源码挪到 src/ 下,根目录不再自动命中源码,你必须先把包装好(uv pip install -e .)才能 import——这一步就逼着你把打包配置弄对。问题暴露在开发期,而不是发布后。

生活化类比:目录结构像房子的户型图。动线合理(导入路径短、清晰)、功能区划清(测试/文档/源码各归其位),住着才不乱。把所有 .py 堆根目录,就像把卧室厨房书房全塞一间屋——开始能住,越住越崩。

5.2 一个生产级目录结构(推荐模板)

下面这套结构是经过大量项目验证的通用模板,中小型项目可以直接套:

my_project/
├── src/                         # 源码区(src 布局)
│   └── my_project/              # 主包,名字 = pyproject.toml 里的 name
│       ├── __init__.py          # 包初始化,可暴露版本号等
│       ├── core/                # 核心业务逻辑
│       │   ├── __init__.py
│       │   ├── models.py        # 数据模型
│       │   └── services.py      # 业务服务
│       ├── api/                 # 对外接口(如 Web 路由)
│       │   ├── __init__.py
│       │   └── routes.py
│       ├── cli.py               # 命令行入口
│       └── __main__.py          # 支持 python -m my_project 运行
│
├── tests/                       # 测试区(与 src 平级)
│   ├── conftest.py              # pytest 公共 fixture
│   ├── unit/                    # 单元测试
│   └── integration/             # 集成测试
│
├── docs/                        # 文档区
│   ├── index.md
│   └── ...
│
├── scripts/                     # 运维/部署脚本(非源码)
│   └── deploy.sh
│
├── .github/                     # CI/CD 配置
│   └── workflows/
│
├── pyproject.toml               # 【配置中枢】元数据 + 依赖 + 工具配置
├── uv.lock                      # 【锁文件】精确锁定依赖,必须提交
├── README.md                    # 项目说明
├── .gitignore                   # 忽略 .venv/、__pycache__/、dist/ 等
└── .python-version              # (可选)声明项目用的 Python 版本

各部分职责一目了然,几个关键约定强调一下:

  • 主包名 = 项目名src/ 下那个目录名要和 pyproject.tomlname 一致,否则装完 import 不到。
  • tests/src/ 平级:测试不在源码包里,它只是"消费者"。tests/不需要 __init__.py(现代 pytest 推荐做法)。
  • 根目录只放配置文件pyproject.tomluv.lockREADME.md.gitignore 等,不要把业务 .py 丢根目录

5.3 项目目录组织图

用一张图把结构分层展示,方便整体记忆:

5.4 可选目录与常见坏结构

可选目录按需添加,不要一开始就堆:

可选目录

何时加

建议

data/

有测试用的小数据集

大数据用 DVC/对象存储,别进 Git

notebooks/

数据探索、Jupyter

探索用,产出迁回 src/

config/

多环境配置(dev/prod)

也可放 src/ 内做包数据

examples/

给用户的示例

库项目常用

常见坏结构(看到就改):

  • 所有 .py 平铺在根目录 → 改成 src/包名/ 结构。
  • 测试和源码混在一起(core/test_models.py)→ 测试挪到独立 tests/
  • 用中文/拼音目录名(业务逻辑/gongju/)→ 全英文,蛇形命名。
  • 没有 .gitignore.venv/__pycache__/ 全提交 → 用 uv init 自动生成的模板。

这章是"项目重构"的核心。如果你手头有个乱项目,照着 5.2 的模板慢慢挪,比推倒重写靠谱得多。


六、总结:一条主线,串起五件套

如果你要从零搭一个新项目,最短上手顺序就是这条主线本身:

  1. 环境检查:确认 Python 版本(3.12/3.13),别用系统自带;
  2. 实际建项目uv init,跑通 uv add → uv sync
  3. 配置实践:完善 pyproject.toml(元数据、依赖、工具配置);
  4. 编码排错:在 src/ 下组织包,遇到 ImportError 用第四章三连排查;
  5. 项目重构:对照第五章模板,把结构理顺。

工程化的本质,其实就是把"能跑"变成"能稳定地跑、能协作地跑、能长期地跑"。这套基本功看似琐碎,但一旦内化,你写 Python 的体感会从"每次都在救火"变成"一开始就不会着火"。这五件套,是这条路上绕不开的地基。共勉。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐