pip install requests
项目一启动,啪一下:
ModuleNotFoundError: No module named 'requests'
这种问题我见到第一反应不是再装一遍,而是先敲两条命令:
python -c "import sys; print(sys.executable)"
python -m pip -V
十有八九,pip 装包的 Python,和你运行项目的 Python 根本不是一个。
Python 的包管理乱,很多时候不是哪个工具不好用,而是把 pip、venv、conda、Poetry、uv 全当成一类东西了。其实它们管的东西并不完全一样。
pip 干的事情很直接:安装 Python 包。官方对它的定位就是 Python package installer,可以从 PyPI 或其他索引安装包。
比如一个临时的数据清洗脚本,我通常不会搞得很重:
python -m pip install openpyxl httpx
代码可能就这么点:
from pathlib import Path
import openpyxl
book = openpyxl.load_workbook(Path("待处理.xlsx"))
sheet = book.active
for row in sheet.iter_rows(min_row=2):
if row[2].value:
row[2].value = str(row[2].value).strip()
book.save("清洗后.xlsx")
一次性脚本,装两个包跑完就结束,这时候为了“工程化”再塞个 Poetry,我觉得纯属给自己加活。
但正式项目不能直接往系统 Python 里装。
这时候 venv 就该上了。
venv 本身不是拿来下载依赖的,它负责给项目切一个独立 Python 环境,每个环境有自己的一套包。Python 官方也把它定位成轻量级虚拟环境工具。
我自己写普通后端、小工具,最朴素的一套其实一直能打:
python -m venv .venv
# Linux / macOS
source .venv/bin/activate
python -m pip install fastapi uvicorn
python -m pip freeze > requirements.txt
这套东西土不土?
有点。
但服务器上跑几个内部 Python 服务,我反而挺喜欢这种土办法。谁接手都认识,CI 也不用多装一层工具。
麻烦一般出在项目变大之后。
比如开发环境装:
pytest
ruff
mypy
生产环境只需要:
fastapi
uvicorn
sqlalchemy
再加上依赖版本锁定、打包发布,requirements.txt 慢慢就容易管得别扭。这个阶段我才会考虑 Poetry。
Poetry 管的不只是“装一个包”,它把依赖声明、依赖解析、虚拟环境和 Python 项目打包这几件事收到了一起,项目依赖可以统一放进 pyproject.toml。
例如:
poetry add httpx
poetry add --group dev pytest
poetry install
团队项目里我比较看重它的 lock 文件。
A 同事今天装出来一套版本,B 同事下周拉代码,尽量别给我重新解析出另一套依赖。Python 项目最烦的不是“装不上”,而是你机器能跑,我机器也能跑,一上测试环境突然不认账。
至于 conda,我不会因为“它也能建虚拟环境”就拿它替代所有方案。
conda 能同时管理环境、Python 版本以及软件包,而且它并不局限于 Python 包。
所以一旦碰到科学计算、机器学习、底层二进制依赖,我对 conda 的接受度会高很多。
比如项目除了 Python 包,还折腾 NumPy、CUDA 相关环境或者一些系统库。这种时候你继续拿 pip + venv 硬顶,有时候最后查的已经不是 Python 问题了,是编译器、动态库和系统环境。
普通 Web API 项目,我一般不会优先 conda。
工具太重倒不是最大问题,最大的问题是团队里一半人 conda install,另一半人 pip install,最后环境来源混在一起。这个场面我比较嫌弃,出问题的时候连该查哪个包源都得先确认。
这两年我新开 Python 项目,反而越来越愿意先试 uv。
uv 现在覆盖的范围已经很大:包安装、项目依赖、虚拟环境、Python 版本、lockfile 都能管,而且提供了兼容 pip 使用习惯的接口。官方文档甚至直接把它定位成 Python package and project manager。
我比较喜欢的是项目目录能收得很干净:
uv init order-checker
cd order-checker
uv add httpx pydantic
uv add --dev pytest
uv run python main.py
项目环境默认可以放在 .venv,uv run 执行时还能检查项目环境是不是最新状态。
以前搭一个项目,我脑子里的步骤是:
装 Python
→ 建 venv
→ 激活
→ pip install
→ 管 requirements
→ 再考虑锁版本
用 uv 后,这几个动作明显往一个入口收了。
所以现在让我选,我基本不会纠结五个工具谁“最好”。
写个临时脚本,pip 足够。
普通小项目,我宁愿 venv + pip,简单,坏了也好查。
数据科学、机器学习,尤其掺杂比较多非 Python 依赖,我会先看 conda。
已经采用 Poetry 的成熟团队项目,我不会为了追新工具专门迁移。Poetry 的依赖管理和打包能力本身没什么问题。
新开的普通 Python 工程,如果团队没有历史包袱,我现在会优先试 uv。
还有一个习惯我建议保留,不管最后用哪个工具,出环境问题先把这三个东西对上:
python -c "import sys; print(sys.executable)"
python -m pip -V
python -c "import site; print(site.getsitepackages())"
Python 包装不上不可怕。
可怕的是折腾半小时以后才发现:你一直在给另一个 Python 装包。