Python项目结构和依赖项管理

1.概述

    如果你写过Go项目,一定会喜欢其整洁的项目结构项目依赖。而反观Python的项目结构和项目依赖,就真的一言难尽了。

    Python最开始面向的并不是大型工程项目,而是定位于脚本开发和教学使用,所以对项目结构、项目依赖等,并无太多规范和建议,因此大家都是根据个人习惯或理解来自定义项目结构。

    随着Python在WEB开发、数据分析、AI等领域的逐步火爆,大型项目也越来越多,大家会经常为Python的项目结构和项目依赖感到很苦恼。因此Python的项目结构和项目依赖管理也越来越迫切。为此Python社区也在逐步持续推出一套项目管理规范,从pipvenvpyproject.tomluv等,这些工具也逐步将Python项目转向更加清晰和规范的项目结构。

python 项目规范官方指导:https://packaging.python.org/en/latest/

2.项目结构规范

    与Go这种自带强制规范属性不同,Python的项目结构管理一直是处于百花齐放阶段,因此大家都是自行组织项目结构。因此,一些早期的项目结构组织形式会出现如下所示的场景:

  • 项目根目录中堆满了各种py文件
  • 配置文件杂乱分布在各个路径中
  • 各种插件有各自的配置文件
  • 项目没有明确的依赖
  • 测试代码分散

    以上这些场景会导致在代码复用、测试、部署都变得非常困难和痛苦。针对这种情况,官方也在通过不断完善PEP(Python Enhancement Proposals)规范,逐步引导大家形成一个统一的规范。目前一个比较规范的Python项目结构,通常包含以下各项:

project_name              # 项目名称
├── src                   # 项目功能和业务代码
├── test                  # 项目测试代码
├── docs                  # 项目文档
├── pyproject.toml        # 项目配置和依赖管理中心
├── README.md             # 项目说明文档
└── .venv                 # 项目虚拟环境

在一些项目中,还会看到requirements.txt,主要用于存放项目依赖项,但随着官方推荐使用pyproject.toml以来requirements.txt也还慢慢逐步退出历史舞台

    一个风格良好的项目,通常具备清晰整洁的项目结构。在完成项目结构规范之后,后而就需要处理项目依赖问题。

3. 项目依赖

    Python的项目依赖管理,早期一直是使用官方的pip工具,到后面逐步发展各种不同的工具,例如pdmuv等。下面就来分别梳理一下。

3.1 pip 阶段

    pip是官方推出的Python项目依赖管理工具,也是大家使用最多的项目依赖管理工具,其使用和安装也非常简单,示例如下所示:

# 安装依赖包
$ pip install requests

    以上安装方式,使用起来非常简单,但同时也存在严重的兼容问题,因为使用pip安装的依赖项默认都是安装在系统全局环境中,示例如下所示:

C:\Users\Surpass>pip show requests
Name: requests
Version: 2.32.4
Summary: Python HTTP for Humans.
Home-page: https://requests.readthedocs.io
Author: Kenneth Reitz
Author-email: me@kennethreitz.org
License: Apache-2.0
Location: D:\Program Files\Python\Lib\site-packages
Requires: certifi, charset_normalizer, idna, urllib3
Required-by: jupyterlab_server, wqb

    由于都是安装在全局环境中,若存在以下场景,则会产生严重的兼容问题:

  • 项目-A的依赖项要求版本是 2.0
  • 项目-B的依赖项要求版本为 3.0

    如果上述两个版本互相兼容,则通常不会有太大问题,如果不兼容,则会产生严重的兼容性问题,导致两个项目无法共用同一个依赖项。并且同一个依赖随着版本的更新迭代,间接依赖项也会存在不同的情况。

3.2 venv 阶段

    既然使用pip安装的依赖项默认都是在系统全局环境中,有没有一种办法,把各自的依赖项安装在不同的目录,不就解决这个问题了吗?为解决这个问题,Python引入了venv(虚拟环境)机制,以Windows为例,使用虚拟环境的操作命令如下所示:

# 创建虚拟环境,其中.venv 为创建虚拟环境的名称
C:\Users\Surpass> python -m venv .venv
# 激活并使用当前虚拟环境
C:\Users\Surpass>.venv\Scripts\activate.bat
# 在虚拟环境中安装依赖项,依赖项会被安装到Lib/site-packages目录中
(.venv) C:\Users\Surpass> pip install requests
# 退出当前虚拟环境
C:\Users\Surpass>.venv\Scripts\deactivate.bat

官方venv介绍文档:https://docs.python.org/zh-cn/3.13/library/venv.html

    虚拟环境创建完成后的目录结构如下所示:

$ tree -L 2 .venv/
.venv/
|-- Include
|-- Lib
|   └── site-packages
|-- Scripts
|   |-- Activate.ps1
|   |-- activate
|   |-- activate.bat
|   |-- deactivate.bat
|   |-- pip.exe
|   |-- pip3.12.exe
|   |-- pip3.exe
|   |-- python.exe
|   └── pythonw.exe
└── pyvenv.cfg

    .venv中文件pyvenv.cfg是虚拟环境的配置文件,用于存储虚拟环境相关配置信息。在Python解释器启动时,会读取这个文件来加载虚拟环境相关配置。pyvenv.cfg文件内容如下所示:

home = D:\Program Files\Python
include-system-site-packages = false
version = 3.12.8
executable = D:\Program Files\Python\python.exe
command = D:\Program Files\Python\python.exe -m venv C:\Users\Surpass\Documents\PyCharmProjects\TestNote\code\.venv
  1. home:创建虚拟环境时使用的全局Python解释器路径
  • Linux: 可能是 /usr/local/bin
  • Windows: 可能是 D:\Program Files\Python

2.include-system-site-packages:是否包含Python全局解释器库(site-packages)

  • false: 默认值,虚拟环境隔离,只使用自己的库,不包含全局解释器的库
  • true:会加载全局解释器的库

3.version: 当前Python解释器版本
4.executable: 全局Python解释器可执行路径
5.command:创建虚拟环境时使用的命令

    在激活虚拟环境之后,再来安装依赖项,就会被安装自己独立的依赖库路径.venv\Lib\site-packages,从而实现了项目的隔离,避免产生冲突和兼容性问题。site-packages 目录如下所示:

$ tree -L 1 .venv/Lib/site-packages/
.venv/Lib/site-packages/
|-- certifi
|-- certifi-2025.8.3.dist-info
|-- charset_normalizer
|-- charset_normalizer-3.4.3.dist-info
|-- idna
|-- idna-3.10.dist-info
|-- pip
|-- pip-24.3.1.dist-info
|-- requests
|-- requests-2.32.5.dist-info
|-- urllib3
└── urllib3-2.5.0.dist-info

    从原理上来讲,虚拟环境的实现并不复杂,其主要是修改了Python的sys.path搜索路径。在启用venv的项目中,sys.path的值如下所示:

>>> import sys,pprint
>>> pprint.pp(sys.path)
['',
 'D:\\Program Files\\Python\\python312.zip',
 'D:\\Program Files\\Python\\DLLs',
 'D:\\Program Files\\Python\\Lib',
 'D:\\Program Files\\Python',
 #  将虚拟环境的site-packages 添加到sys.path中
 'C:\\Users\\Surpass\\Documents\\PyCharmProjects\\TestNote\\code\\.venv',
 'C:\\Users\\Surpass\\Documents\\PyCharmProjects\\TestNote\\code\\.venv\\Lib\\site-packages']

    在Python代码中执行import xxx时,Python就会按照sys.path的列表顺序逐个搜索,直到找到我们要导入的包,在激活虚拟环境后,虚拟环境中的 site-packages 添加到sys.path中,而由于我们将依赖项安装到了虚拟环境中,因此可以正常导入依赖项。

    虚拟环境成功解决了项目依赖冲突问题,但如果需要将项目分享到github或其他人时,那各个依赖项该怎么安装呢?

3.3 requirements.txt 阶段

    如果要分享项目或项目协作开发时,对于项目依赖的处理,通常的做法是导出项目依赖,然后对方再导入同样的依赖项,导出依赖项的命令如下所示:

# 重定向于文件
C:\Users\Surpass> pip freeze > requirements.txt
# 仅打印已经安装的包
C:\Users\Surpass>  pip freeze
  certifi==2025.8.3
  charset-normalizer==3.4.3
  idna==3.10
  requests==2.32.5
  urllib3==2.5.0

    pip freeze 作用是打印出当前环境所有已经安装的依赖项和使用的版本信息。这样在其他人获得项目后,就不需要关心需要安装哪些具体的项目依赖了,只需要在自己的环境中执行以下安装命令:

pip install -r requirements.txt

    执行以上命令后,pip会将requirements.txt里面的依赖包全部安装。这种方法非常直观,现在依然被大量采用。但这种方法依然存在以下问题:

  • pip freeze 无法分清哪些是直接依赖,哪些是间接依赖
  • 执行 pip uninstall 时,仅仅会删除指定的包,而因这个产生的间接依赖无法删除,进而产生孤儿依赖项
  • 版本管理困难: 使用pip freeze导出的依赖项版本是基于当前已经安装的版本,如果版本发生变更,则需要手动修改版本,无法适应灵活的版本需求

    基于存在以上问题,requirements.txt并不适合维护长期项目,因此官方便推出了pyproject.toml来管理项目依赖和配置。

3.4 pyproject.toml 阶段

    基于requirements.txt存在的问题,Python官方推出了以pyproject.toml文件做为项目统一配置中心。其优势如下所示:

  • 支持主流工具统一编写配置
  • 原生支持项目打包和依赖管理
  • 清晰区分直接依赖和间接依赖

    pyproject.toml文件示例如下所示:

[project]
name = "mcp-server-demo"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.12"
dependencies = [
    "mcp[cli]>=1.14.1",
]

更多详细pyproject.toml可参考:https://packaging.python.org/en/latest/guides/writing-pyproject-toml/

    相对于requirements.txt,我们只需要在pyproject.tomldependencies声明直接依赖项即可。在其他人获得项目时,执行以下命令进行安装依赖即可:

$ pip install .

    在使用pyproject.toml的项目中使用pip install 时,其实存在两个步骤,如下所示:

  • 打包项目:根据pyproject.toml,把当前项目打包标准格式的Python软件包
  • 安装项目:执行安装,并自动将所有声明依赖荐一并安装

    安装过程如下所示:

  ...
  Building wheels for collected packages: mcp-server-demo
  # 构建Python标准安装包
  Building wheel for mcp-server-demo (pyproject.toml) ... done
  Created wheel for mcp-server-demo: filename=mcp_server_demo-0.1.0-py3-none-any.whl size=1362 sha256=ce503c4e1b59ee6db3c3ca1e7cc1c16801867f3b9a49d2b7b4d402595a2d0a36
  Stored in directory: c:\users\surpass\appdata\local\pip\cache\wheels\af\7e\a6\dd6f9e6a227ccacf517ad9fd0e94d0021254c10f876807aee1
Successfully built mcp-server-demo
# 安装包
Installing collected packages: mcp-server-demo
  Attempting uninstall: mcp-server-demo
    Found existing installation: mcp-server-demo 0.1.0
    Uninstalling mcp-server-demo-0.1.0:
      Successfully uninstalled mcp-server-demo-0.1.0
Successfully installed mcp-server-demo-0.1.0

    因此在使用pip install后,项目中会多出一些文件,如下所示:

$ tree -L 3
.
|-- README.md
|-- build
|   |-- bdist.win-amd64
|   └── lib
|       └──  sample.py
|-- mcp_server_demo.egg-info
|   |-- PKG-INFO
|   |-- SOURCES.txt
|   |-- dependency_links.txt
|   |-- requires.txt
|   └── top_level.txt
|-- pyproject.toml
└── sample.py

    通过上面的目录可以看出,我们刚才写的文件sample.py在目录build\lib也有一样同样的代码,这样一个项目中就会两份一样的代码,从而带来一个新问题开发阶段如何同步代码。因为在日常开发过程,我们修改代码,build\lib并不会自动同步,为此在安装时,需要添加参数-e,这样就不会再源码复制到build\lib目录中了,如下所示:

$ pip install -e .

3.5 增强型工具阶段

    虽然pip+venv+pyproject.toml解决了前面出现的各类问题,但步骤还是太多,有没有一键解决这个问题的办法呢?因此社区便出现一些增强版的工具,这些工具可以理解为对pip+venv+pyproject.toml三个步骤的高级封装,以下使用uv来演示:

1.安装uv工具

pip install -U uv

2.初始化工程

$ uv init uv-demo
$ cd uv-demo

3.添加依赖项

uv add requests

    在操作添加依赖项时,uv会自动完成以下操作

  • 自动创建虚拟环境
  • 下载和安装直接和间接依赖项
  • 更新pyproject.toml文件

5.运行项目

$ uv run main.py

无需手动激活虚拟环境,uv自动识别当前目录中的.venv并自动切换环境后,再执行运行文件

6.团队协作

    如果是从git上拉取的uv管理的项目,还可以使用uv来同步项目,操作命令

$ uv sync
Resolved 6 packages in 1ms
Audited 5 packages in 0.41ms

4. uv

    uv是一个使用Rust开发的一款Python项目管理和依赖项管理工具,其官方文档:https://docs.astral.sh/uv/

4.1 uv安装

    uv本身并依赖Python,所以也可以不使用pip安装,可以使用以下方式安装:

  • MacOS
curl -LsSf https://astral.sh/uv/install.sh | sh
  • Windows
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"

4.2 uv 使用

4.2.1 使用帮助

    查看uv使用帮助,可以使用命令uv help,示例如下所示:

C:\Users\Surpass>uv help
An extremely fast Python package manager.

Usage: uv [OPTIONS] <COMMAND>

Commands:
  auth                       Manage authentication
  run                        Run a command or script
  init                       Create a new project
  add                        Add dependencies to the project
  remove                     Remove dependencies from the project
  version                    Read or update the project's version
  sync                       Update the project's environment
  lock                       Update the project's lockfile
  export                     Export the project's lockfile to an alternate format
  tree                       Display the project's dependency tree
  format                     Format Python code in the project
  tool                       Run and install commands provided by Python packages
  python                     Manage Python versions and installations
  pip                        Manage Python packages with a pip-compatible interface
  venv                       Create a virtual environment
  build                      Build Python packages into source distributions and wheels
  publish                    Upload distributions to an index
  cache                      Manage uv's cache
  self                       Manage the uv executable
  generate-shell-completion  Generate shell completion
  help                       Display documentation for a command

4.2.2 常用命令

4.2.2.1 管理多版本Python

    使用uv python list,可以显示可安装或已安装的Python版本

C:\Users\Surpass>uv python list
cpython-3.14.0rc3-windows-x86_64-none                 <download available>
cpython-3.14.0rc3+freethreaded-windows-x86_64-none    <download available>
cpython-3.13.7-windows-x86_64-none                    <download available>
cpython-3.13.7+freethreaded-windows-x86_64-none       <download available>
cpython-3.12.11-windows-x86_64-none                   <download available>
cpython-3.12.8-windows-x86_64-none                    D:\Program Files\Python\python.exe
cpython-3.11.13-windows-x86_64-none                   <download available>
cpython-3.10.18-windows-x86_64-none                   <download available>
cpython-3.9.23-windows-x86_64-none                    <download available>
cpython-3.8.20-windows-x86_64-none                    <download available>
pypy-3.11.13-windows-x86_64-none                      <download available>
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>

    使用uv python install可安装最新版本的Python或指定版本的Python

C:\Users\Surpass>uv python install cpython-3.12.11 

    使用uv python dir查看使用uv安装的Python路径

C:\Users\Surpass>uv python dir
C:\Users\Surpass\AppData\Roaming\uv\python

    移除安装的Python版本,使用uv python uninstall cpython-3.12.11

4.2.2.2 初始化项目

    使用uv初始化项目,使用uv init projectName,示例如下所示:

C:\Users\Surpass> uv init uv-demo

4.2.2.3 添加依赖项

    在初始化项目后,切换项目目录,使用uv add 添加依赖项,示例如下所示:

C:\Users\Surpass> uv add requests

4.2.2.4 删除依赖项

    如果需要删除依赖项,可以使用uv remove ,示例如下所示:

C:\Users\Surpass> uv remove requests
Resolved 1 package in 12ms
Uninstalled 5 packages in 273ms
 - certifi==2025.8.3
 - charset-normalizer==3.4.3
 - idna==3.10
 - requests==2.32.5
 - urllib3==2.5.0

    在使用uv remove命令后,除删除指定的依赖项,也会同步删除其依赖项,并同步更新pyproject.toml文件

4.2.2.5 查看依赖树

    如果想查看当前项目的依赖树,可以使用uv tree命令,示例如下所示:

C:\Users\Surpass> uv tree
Resolved 6 packages in 1ms
python-project-demo v0.1.0
└── requests v2.32.5
    ├── certifi v2025.8.3
    ├── charset-normalizer v3.4.3
    ├── idna v3.10
    └── urllib3 v2.5.0

4.2.2.6 同步项目依赖

    在多人协作的项目中,在进入项目目录后,可以使用uv sync来同步项目,示例如下所示:

C:\Users\Surpass> uv sync

4.2.2.7 运行代码或命令

    可以使用uv来运行代码,示例如下所示:

C:\Users\Surpass> uv run main.py
Hello from uv-demo!

4.2.2.8 安装系统级tool

    如果想要安装一个工具,但又不仅限于当前项目,其他项目也可以使用,可以使用命令uv tool install,示例如下所示:

C:\Users\Surpass> uv tool install ruff

4.2.2.9 打包

    在我们编写好代码,可以将代码打包并发布到pypi,这样就可以让其他能够直接安装发布的包了,在发布包之前,需要在pyproject.toml添加以下内容:

[project.scripts]
# 格式:python脚本:函数名
surpass="sample:main"

    执行以下的打包命令,即可将文件打包为whl文件,然后发布到Pypi中供其他人使用:

C:\Users\Surpass> uv build

...
creating 'C:\Users\Surpass\Documents\PyCharmProjects\TestNote\test-note\python-project-demo\dist\.tmp-yeygi16z\python_project_demo-0.1.0-py3-none-any.whl' and adding 'build\bdist.win-amd64\wheel' to it
adding 'sample.py'
adding 'python_project_demo-0.1.0.dist-info/METADATA'
adding 'python_project_demo-0.1.0.dist-info/WHEEL'
adding 'python_project_demo-0.1.0.dist-info/entry_points.txt'
adding 'python_project_demo-0.1.0.dist-info/top_level.txt'
adding 'python_project_demo-0.1.0.dist-info/RECORD'
removing build\bdist.win-amd64\wheel
Successfully built dist\python_project_demo-0.1.0.tar.gz
Successfully built dist\python_project_demo-0.1.0-py3-none-any.whl

    uv是一款非常优秀并且速度很快的Python项目依赖项管理工具,有很多的使用方法,大家可以查看官网(https://docs.astral.sh/uv/)。

本文同步在微信订阅号上发布,如各位小伙伴们喜欢我的文章,也可以关注我的微信订阅号:woaitest,或扫描下面的二维码添加关注:

posted @ 2025-10-02 21:05  Surpassme  阅读(28)  评论(0)    收藏  举报