速读#
学 Python 第一件该养成的习惯不是写业务,而是给每个项目一份独立环境。venv 解决的是全局依赖互相顶版本这类烂账,后面的 Docker 只是把同一条隔离线放大到整个运行时。
共享环境是烂泥潭#
Python 全局环境有个天然缺陷:所有项目共用同一个包目录,而同一个目录里,一个包只能装一个版本。项目 A 把 lib 升到 2.0,项目 B 依赖 1.x 就跟着坏——不是可能坏,是结构上注定坏,因为 1.x 已经被物理覆盖了。更阴的是时间差:坏的那个项目当时不报错,过几天重跑才暴雷,而那时早就忘了中间装过什么。
根因不是「你装错了」,是环境没隔离。venv 给每个项目一份独立包目录,从结构上消灭「互相顶版本」。
两个项目打架之后才栽明白#
当时图省事,两个项目都装在全局:一个 Flask 博客、一个数据处理脚本,各自依赖同一个库的不同版本。脚本安装时把版本顶到 v2.0,博客过几天重跑,接口直接炸。
排查路径很折磨:先怀疑自己代码 → 再怀疑 Flask 版本 → 最后才发现依赖被另一个项目悄悄顶掉。从报错到定位,一个多小时;一开始用 venv,这事根本不会发生。
那次之后:新建项目第一条命令永远是 python -m venv .venv。
每项目一个 venv#
Python 3 自带,不用额外装:
python -m venv .venv
# Linux / macOSsource .venv/bin/activate
# Windows PowerShell.venv\Scripts\Activate.ps1激活后 pip install 只进这个目录,不污染全局。包括后来写 Django、Flask,这条习惯没变过。
venv 本身没有魔法:.venv 就是一个带独立 site-packages 的目录,「激活」也只是把它的可执行路径挂到 PATH 前面。所以有没有生效不用猜,which python 看指向就知道;脚本和定时任务里甚至不必激活,直接用 .venv/bin/python 跑,效果一样。拆穿到这一层,就不会在「激活了没有」上疑神疑鬼。
依赖清单边界#
pip freeze > requirements.txtpip install -r requirements.txt小工具规模下,pip freeze 够用。但要知道它在干什么:把当前环境里所有包连同间接依赖一起钉死版本。好处是别人装出来和我一模一样;代价是清单里分不清哪些是我真正要的、哪些只是被捎带进来的。
它也比不上 Maven、Poetry 那类带锁文件的依赖管理精确。还有个容易忽略的洞:清单钉死了包版本,却不记录 Python 解释器本身的版本——同一份 requirements.txt,3.8 和 3.12 装出来的行为未必一致。承认边界,不假装万能。 多人协作的大项目,这套机制往往不够。
两条配套习惯:
.venv/进.gitignore——环境不进库,进库的是清单requirements.txt和代码一起提交——环境随时可以删掉重建,清单才是事实源
环境问题占 bug 一大半#
一条规律贯穿后面所有事:环境问题占了我所有 bug 的一大半。
| 手段 | 治什么 |
|---|---|
| venv | 依赖打架 |
| Docker | 我机器能跑、你机器不行 |
守护环境,和守护进程是同一类问题:不主动隔离 / 收敛,迟早给你颜色看。
任何会在多台机器、多个项目间流动的东西,第一件事都是隔离。venv 是这条线最朴素的一站。
容器化是把 venv 的思路放大到整个运行环境:venv 隔离的是包目录,容器隔离的是从系统库到运行时的一整层——逻辑是通的。
