2172 字
11 分钟
Python 项目管理
2026-03-04

Python 项目管理#

安装 uv

管理 Python#

下载 Python,并查看本机已安装的 Python:

Terminal window
PS C:\Users\Shy_Vector\AAA> uv python install pypy
PS C:\Users\Shy_Vector\AAA> uv python list
Terminal window
cpython-3.15.0a6-windows-x86_64-none <download available>
cpython-3.15.0a6+freethreaded-windows-x86_64-none <download available>
cpython-3.14.3-windows-x86_64-none <download available>
cpython-3.14.3+freethreaded-windows-x86_64-none <download available>
cpython-3.13.12-windows-x86_64-none <download available>
cpython-3.13.12+freethreaded-windows-x86_64-none <download available>
cpython-3.12.13-windows-x86_64-none <download available>
cpython-3.12.10-windows-x86_64-none C:\Program Files\Python\python.exe
cpython-3.11.15-windows-x86_64-none <download available>
cpython-3.10.20-windows-x86_64-none <download available>
cpython-3.9.25-windows-x86_64-none <download available>
cpython-3.8.20-windows-x86_64-none <download available>
pypy-3.11.13-windows-x86_64-none C:\Users\Shy_Vector\AppData\Roaming\uv\python\pypy-3.11.13-windows-x86_64-none\pypy3.11.exe
pypy-3.10.16-windows-x86_64-none <download available>
pypy-3.9.19-windows-x86_64-none <download available>
pypy-3.8.16-windows-x86_64-none <download available>
graalpy-3.12.0-windows-x86_64-none <download available>
graalpy-3.11.0-windows-x86_64-none <download available>
graalpy-3.10.0-windows-x86_64-none <download available>

临时运行特定版本的 Python / 脚本:

Terminal window
PS C:\Users\Shy_Vector\AAA> uv run -p 3.12 python
Python 3.12.10 (tags/v3.12.10:0cc8128, Apr 8 2025, 12:21:36) [MSC v.1943 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
Ctrl click to launch VS Code Native REPL
>>> exit()
PS C:\Users\Shy_Vector\AAA> uv run -p pypy python
Python 3.11.13 (413c9b7f57f5, Jul 03 2025, 18:04:37)
[PyPy 7.3.20 with MSC v.1941 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
Ctrl click to launch VS Code Native REPL
>>>> exit()

删除 Python:

Terminal window
PS C:\Users\Shy_Vector\AAA> uv python uninstall pypy
Terminal window
Searching for Python versions matching: PyPy
Uninstalled Python 3.11.13 in 777ms
- pypy-3.11.13-windows-x86_64-none

管理 venv#

初始化项目:

Terminal window
PS C:\Users\Shy_Vector\AAA> uv init -p 3.12
Initialized project `AAA`
PS C:\Users\Shy_Vector\AAA> ls
目录: C:\Users\Shy_Vector\AAA
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 2026/3/4 10:53 109 .gitignore
-a---- 2026/3/4 10:53 5 .python-version
-a---- 2026/3/4 10:46 167 main.py
-a---- 2026/3/4 10:53 154 pyproject.toml
-a---- 2026/3/4 10:53 0 README.md

可在 .python-version 中修改当前项目的 Python 版本:

.python-version
3.12

pyproject.toml 包含当前项目的配置信息,第三方工具零散的配置文件得到统一:

.pyproject.toml
[project]
name = "AAA"
version = "0.1.0"
description = "AAAAAA"
readme = "README.md"
requires-python = ">=3.12"
dependencies = []

初始化之前已有 main.py

main.py
from flask import Flask
app = Flask(__name__)
@app.route('/')
def foo():
return 'Hello World'
if __name__ == '__main__':
app.run(debug=True)

里面依赖了 flask 第三方库,接下来我们安装它:

Terminal window
PS C:\Users\Shy_Vector\AAA> uv add flask
Terminal window
Using CPython 3.12.10 interpreter at: C:\Program Files\Python\python.exe
Creating virtual environment at: .venv
Resolved 9 packages in 1.41s
Prepared 8 packages in 533ms
Installed 8 packages in 26ms
+ blinker==1.9.0
+ click==8.3.1
+ colorama==0.4.6
+ flask==3.1.3
+ itsdangerous==2.2.0
+ jinja2==3.1.6
+ markupsafe==3.0.3
+ werkzeug==3.1.6
--dev

对于 uv add 的参数,可以使用 --dev 参数将工具加入开发依赖组,组内的依赖仅在开发阶段被使用,不会被打包出去.

Terminal window
uv add ruff --dev
.pyproject.toml
[project]
name = "AAA"
version = "0.1.0"
description = "AAAAAA"
readme = "README.md"
requires-python = ">=3.12"
dependencies = [
"flask>=3.1.3",
]
[dependency-groups]
dev = [
"ruff>=0.15.4",
]

当然,ruff 是工具而不是依赖,与工程代码无关,不应该作为依赖引入.

Terminal window
uv remove ruff --dev
uv tool

对于工具,可以使用 uv tool install 来安装:

Terminal window
PS C:\Users\Shy_Vector\AAA> uv tool install ruff
Resolved 1 package in 265ms
Installed 1 package in 15ms
+ ruff==0.15.4
Installed 1 executable: ruff
PS C:\Users\Shy_Vector\AAA> which ruff
C:\Users\Shy_Vector\.local\bin\ruff.EXE
PS C:\Users\Shy_Vector\AAA> ruff check
All checks passed!

查看所有工具:

Terminal window
PS C:\Users\Shy_Vector\AAA> uv tool list
ruff v0.15.4
- ruff

我们可以看到 uv 已经为当前项目自动创建虚拟环境 .venv

Terminal window
PS C:\Users\Shy_Vector\AAA> tree
Terminal window
文件夹 PATH 列表
卷序列号为 EA1E-9D15
C:.
├─.ruff_cache
│ └─0.15.4
└─.venv
├─Lib
│ └─site-packages
│ ├─blinker
│ │ └─__pycache__
│ ├─blinker-1.9.0.dist-info
│ ├─click
│ │ └─__pycache__
│ ├─click-8.3.1.dist-info
│ │ └─licenses
│ ├─colorama
│ │ ├─tests
│ │ └─__pycache__
│ ├─colorama-0.4.6.dist-info
│ │ └─licenses
│ ├─flask
│ │ ├─json
│ │ │ └─__pycache__
│ │ ├─sansio
│ │ │ └─__pycache__
│ │ └─__pycache__
│ ├─flask-3.1.3.dist-info
│ │ └─licenses
│ ├─itsdangerous
│ │ └─__pycache__
│ ├─itsdangerous-2.2.0.dist-info
│ ├─jinja2
│ │ └─__pycache__
│ ├─jinja2-3.1.6.dist-info
│ │ └─licenses
│ ├─markupsafe
│ │ └─__pycache__
│ ├─markupsafe-3.0.3.dist-info
│ │ └─licenses
│ ├─werkzeug
│ │ ├─datastructures
│ │ │ └─__pycache__
│ │ ├─debug
│ │ │ ├─shared
│ │ │ └─__pycache__
│ │ ├─middleware
│ │ ├─routing
│ │ │ └─__pycache__
│ │ ├─sansio
│ │ │ └─__pycache__
│ │ ├─wrappers
│ │ │ └─__pycache__
│ │ └─__pycache__
│ ├─werkzeug-3.1.6.dist-info
│ │ └─licenses
│ └─__pycache__
└─Scripts
虚拟环境何以实现?

当虚拟环境被激活后,.venv\\Lib\\site-packages 目录将被追加进 Python 中 sys.path 列表:

>>> import sys, pprint
>>> pprint.pp(sys.path)
['',
'C:\\Program Files\\Python\\python312.zip',
'C:\\Program Files\\Python\\DLLs',
'C:\\Program Files\\Python\\Lib',
'C:\\Program Files\\Python',
'C:\\Users\\Shy_Vector\\AAA\\.venv',
'C:\\Users\\Shy_Vector\\AAA\\.venv\\Lib\\site-packages']

Python 在导入模块时,将逐个检查 sys.path 列表里的每个路径,直到找出该模块.

查看所有第三方库的依赖关系

Terminal window
PS C:\Users\Shy_Vector\AAA> uv tree
Terminal window
Resolved 9 packages in 1ms
AAA v0.1.0
└── flask v3.1.3
├── blinker v1.9.0
├── click v8.3.1
│ └── colorama v0.4.6
├── itsdangerous v2.2.0
├── jinja2 v3.1.6
│ └── markupsafe v3.0.3
├── markupsafe v3.0.3
└── werkzeug v3.1.6
└── markupsafe v3.0.3

现在运行 main.py

Terminal window
PS C:\Users\Shy_Vector\AAA> uv run main.py
* Serving Flask app 'main'
* Debug mode: on
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://127.0.0.1:5000
Press CTRL+C to quit
* Restarting with stat
* Debugger is active!
* Debugger PIN: 329-036-685
127.0.0.1 - - [04/Mar/2026 10:59:36] "GET / HTTP/1.1" 200 -
我拿到的是别人的项目,怎么配环境?

难不成只能看 pyproject.toml,一个个手动 uv add?当然不需要!只需

Terminal window
PS C:\Users\Shy_Vector\AAA> uv sync

uv 就会读取 pyproject.toml,自动搭建虚拟环境,并安装好所有依赖.

Python 往事

如果只用 Python 官方的工具管理项目,那么流程是:手动创建虚拟环境 .venv 并激活,编辑并读取 pyproject.toml,往 .venv 安装依赖.

Terminal window
python -m venv .venv
source .venv/bin/activate (.venv\\Scripts\\activate)
edit pyproject.toml
pip install -e .

.venv 可以取别的名字,但 .venv 这个名字已经被各界认同,也便于 IDE 识别.

pip install -e .自安装

  1. pip 先读取 pyproject.toml把当前项目打包成标准的 Python 软件包;

  2. pip像安装任何第三包那样,安装刚刚打包好的软件包 (含依赖).

  1. 当前项目的源代码也会被打包进去 (比如 main.py 也被放进了 site-packages 目录下);
  2. 这样做的好处是:我们的源代码在导入本项目别的代码时,可以不用相对导入 (如 from . import foo),而用绝对导入 (如 import AAA.foo),这样写会与项目使用者写法保持一致,语义更清晰;
  3. 但这样就存在两份一样的源代码,会引入修改同步问题.此时使用 -e 参数 (编辑模式),site-packages 目录下的源代码将变成链接文件,指向项目源代码.

uv run 已经自动在运行脚本之前帮我们自安装好啦,不用操心.

Python 传说

在 Python 官方没有标准规定 pyproject.toml 文件之前,若想让别人复现这个环境,需使用

Terminal window
pip freeze > requirements.txt

把所有包导出到 requirements.txt

requirements.txt
flask==3.1.3
blinker==1.9.0
click==8.3.1
itsdangerous==2.2.0
jinja2==3.1.6
markupsafe==3.0.3
werkzeug==3.1.6

requirements.txt 混合了所有的直接或间接依赖,很难维护 (比如删除 flask 之后,剩余的间接依赖仍被保留,因为 requirements.txt 并不包含依赖关系的信息).

项目结构#

flat layout

Terminal window
AAA
├─AAA
│ ├─app.py
│ ├─bar.py
│ └─foo.py
├─...
├─docs
├─...
├─tests
├─...
├─pyproject.toml
├─README.md
└─...

src layout

Terminal window
AAA
├─docs
├─...
├─src
│ └─AAA
│ ├─app.py
│ ├─bar.py
│ └─foo.py
├─tests
├─...
├─pyproject.toml
├─README.md
└─...
为什么要套娃

如果没有套一层,项目打包并解压到 site-packages 后,根目录的源代码会裸露在 site-packages 里,此时会有导入污染的问题:

import app, bar, foo # 万一同时有 AAA.app 和 BBB.app 怎么办?

如果套了一层,就不会有这个污染问题:

import AAA.app
from AAA import app, bar, foo

打包#

认识 .whl

当我们使用 pipuvpoetry 这些工具安装软件包时,都会在 PyPI 下载相应的 .whl 文件 (如 flask-3.1.3-py3-none-any.whl),其本质就是压缩包

所谓的 pip install,就是将 .whl 压缩包解压到 site-packages 目录里

Terminal window
└─.venv
├─Lib
│ └─site-packages
│ ├─__pycache__
│ ├─...
│ ├─AAA
│ │ ├─__pycache__
│ │ ├─module
│ │ │ ├─cat.py
│ │ │ ├─dog.py
│ │ │ └─...
│ │ ├─app.py
│ │ ├─bar.py
│ │ ├─foo.py
│ │ └─...
│ ├─AAA-0.1.0.dist-info
│ │ └─...
│ ├─...
│ ├─flask
│ │ ├─__pycache__
│ │ └─...
│ ├─flask-3.1.3.dist-info
│ │ └─...
│ └─...
└─Scripts
  1. flask 目录存放着该库的所有源代码,此时可以使用 import flask.app 来导入 app.py 文件.
  2. flask-3.1.3.dist-info 目录里的 METADATA 文件由 pyproject.toml 生成,记录了所需依赖.
Python 构建系统

.whl 文件如何生成?

Python 的构建系统分为前端 (命令行工具,官方推荐工具为 build) 和后端 (把项目代码打包成 .whl 文件,默认使用 setuptools 作为后端).由于 PEP 517 规范 把前后端之间的交互接口定义得十分清晰,社区涌现了许多第三方实现,如 flithatchlinguvpoetryPDM 等,它们都可以作为前端和后端,可以自由组合.

下面我们使用 build 作为前端,hatchling 作为后端.

安装 build

Terminal window
pip install build

根据 hatchling要求,配置 pyproject.toml

pyproject.toml
[project]
name = "AAA"
version = "0.1.0"
description = "AAAAAA"
readme = "README.md"
requires-python = ">=3.12"
dependencies = [
"flask>=3.1.3",
]
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.hatch.build.targets.wheel]
packages = ["src/AAA"]

现在可以打包了:

Terminal window
PS C:\Users\Shy_Vector\AAA> python -m build
* Creating isolated environment: venv+pip...
* Installing packages in isolated environment:
- hatchling
* Getting build dependencies for sdist...
* Building sdist...
* Building wheel from sdist
* Creating isolated environment: venv+pip...
* Installing packages in isolated environment:
- hatchling
* Getting build dependencies for wheel...
* Building wheel...
Successfully built AAA-0.1.0.tar.gz and AAA-0.1.0-py3-none-any.whl

也可以使用 uv 作为前端:uv build,比 build 快.

打包出来的 .whl.tar.gz 存放在项目的 dist 目录里:

Terminal window
PS C:\Users\Shy_Vector\AAA> unzip -l ./dist/AAA-0.1.0-py3-none-any.whl
Archive: ./dist/AAA-0.1.0-py3-none-any.whl
Length Date Time Name
--------- ---------- ----- ----
162 2020/02/02 00:00 AAA/main.py
128 2020/02/02 00:00 AAA-0.1.0.dist-info/METADATA
87 2020/02/02 00:00 AAA-0.1.0.dist-info/WHEEL
280 2020/02/02 00:00 AAA-0.1.0.dist-info/RECORD
--------- -------
657 4 files
__init__.py

hatchling 默认支持 src layout.

只需要添加空文件 __init__.pyhatchling 认为其所在的目录 AAA 可打包.

pyproject.toml
[project]
name = "AAA"
version = "0.1.0"
description = "AAAAAA"
readme = "README.md"
requires-python = ">=3.12"
dependencies = [
"flask>=3.1.3",
]
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.hatch.build.targets.wheel]
packages = ["src/AAA"]
Terminal window
PS C:\Users\Shy_Vector\AAA> python -m build
PS C:\Users\Shy_Vector\AAA> unzip -l ./dist/AAA-0.1.0-py3-none-any.whl
Archive: ./dist/AAA-0.1.0-py3-none-any.whl
Length Date Time Name
--------- ---------- ----- ----
0 2020/02/02 00:00 AAA/__init__.py
162 2020/02/02 00:00 AAA/main.py
128 2020/02/02 00:00 AAA-0.1.0.dist-info/METADATA
87 2020/02/02 00:00 AAA-0.1.0.dist-info/WHEEL
354 2020/02/02 00:00 AAA-0.1.0.dist-info/RECORD
--------- -------
731 5 files

可以 pip install 或者 uv add.whl 文件解压至 .venv\\Lib\\site-packages,实现软件包的安装.

Python 项目管理
https://fuwari.vercel.app/posts/lang/py/proj/
作者
Shy_Vector
发布于
2026-03-04
许可协议
CC BY-NC-SA 4.0