Day03 · 虚拟环境与依赖管理入门

Day03 · 虚拟环境与依赖管理入门

阶段:阶段一 · 核心基础 | 前置:Day02 | 技术基准:Python 3.15.0

学习目标

  • 说清楚为什么每个 Python 项目都要有自己独立的虚拟环境,它和"全局环境"到底差在哪
  • 熟练用标准库 venv 完成创建、激活、装包、退出的完整闭环
  • 掌握 pip 的 install / uninstall / list / show 四个最常用命令,会用 -i 临时换镜像、用 -U 升级
  • 会用 pip freeze 导出 requirements.txt,再用 pip install -r 在新环境一键复现
  • 认识现代项目配置文件 pyproject.toml 长什么样,知道它和 requirements.txt 的分工
  • 从今天起养成"每个项目一个 venv"的工作习惯

昨天(Day02)我们装好了 Python 3.15.0 解释器,也在最后走马观花地看了一眼 venv。今天就把这第一件"工程习惯"彻底讲透、亲手跑通。别小看它------很多新手学了半个月语法就卡住,原因不是语法难,而是装包装乱了、环境搞脏了,最后连自己写的脚本都跑不起来。今天这一天,是后面所有"装第三方库"的日子(Flask、NumPy、爬虫、机器学习......)的地基。

一、为什么需要虚拟环境

1.1、没有虚拟环境会出什么事

刚学 Python 的时候,我们最自然的动作就是打开命令行,直接敲 pip install requests。这条命令看着很省事,但它把包装到哪去了?答案是:装到全局解释器 的 Lib\site-packages\ 目录里------也就是你电脑上那唯一一个 Python 3.15.0 共享给所有项目用的"大仓库"。

只做一个小练习时,这完全没问题。可一旦你开始同时碰两个以上的项目,三件经典事故就会轮流找上门:

  1. 全局环境污染:每个新项目都往同一个大仓库里倒包。三个月后你根本记不清:到底装了哪些包?哪个版本是干嘛的?哪个包是为了哪个项目装的?大仓库变成了一个谁也不敢动的杂物间。
  2. 版本冲突:项目 A 要求用 Flask 2.x 写,项目 B 却依赖一个只兼容 Flask 3.x 的库。全局环境只能装一个 Flask,两个项目注定有一个跑不起来,你只能反复卸了装、装了卸。
  3. 不可复现 :你把代码发给同事,同事一 pip install,pip 给他装的是"今天最新版"。万一这个最新版改了接口(所谓 breaking change),"我电脑上明明是好的"这句话,就成了事故的开端。

虚拟环境(Virtual Environment)就是冲着这三件事来的:给每个项目单独开一个小仓库,各装各的,互不打扰。

1.2、全局环境 vs 虚拟环境:一次实测对比

空口无凭,小嘉在本机(Python 3.15.0)直接对比给你看。先看全局环境里装了多少东西:

text 复制代码
$ py -3 -m pip list
Package            Version
------------------ ------------
click              8.5.0
hatch              1.18.1
numpy              2.5.4
pandas             3.0.6
pytest             9.1.1
requests           2.34.2
rich               15.0.0
six                1.17.0
...(此处省略,全局共约 90 个包)

再看一个刚创建、什么都没装的全新虚拟环境:

text 复制代码
$ .\.venv\Scripts\python.exe -m pip list
Package Version
------- -------
pip     26.2.1

看出来了吗?全新的虚拟环境里只有 pip 自己一个包,全局环境里那九十多个包它一个都看不见。这就是隔离的直观效果。两者的区别可以列成一张表:

对比项 全局环境 虚拟环境
包装到哪 全局 Lib\site-packages\(所有项目共享) 项目目录里的 .venv\Lib\site-packages\(仅此项目用)
隔不隔离 不隔离,项目之间互相影响 隔离,项目 A 装的包项目 B 完全看不见
装错了怎么办 要手动一个个卸,容易留残渣 不用了整个 .venv 文件夹删掉,系统零残留
适合谁 跑系统自带的小程序 真实项目(本课程的主线做法)

1.3、venv 到底是什么

venv 是 Python 标准库 自带的模块(自 Python 3.3 起内置),不需要你 pip install 任何东西。它不是"再装一个 Python",而是在你项目目录里建一个文件夹 (约定叫 .venv),这个文件夹里放一份指向系统 Python 的"小启动器",外加一个属于你这个项目自己的 site-packages\ 目录。

一句话记住:venv = 一个文件夹 + 一份独立的装包小仓库。 后面 Day61 会深入讲它背后的三个机制(解释器重定向、独立 site-packages、激活改 PATH),今天我们先用起来。

二、venv 的创建、激活与退出

2.1、创建:py -3 -m venv .venv

先 cd 进入你的项目根目录,然后执行:

bash 复制代码
# 用 Python 3.15 解释器,在当前目录创建一个名为 .venv 的虚拟环境
py -3 -m venv .venv

小嘉提醒:为什么写 py -3 -m venv 而不是直接 python -m venv?和 Day02 讲的一样------你的电脑上可能装了好几个 Python 版本,py -3 明确锁定"用 3 系列里的那个解释器"。本机实测它就是 Python 3.15.0,避免"这个环境是被别的版本顺手建的"这种经典坑。

目录名约定俗成用 .venv(前面那个点让它在 Linux/macOS 上变成隐藏目录,主流 IDE 也都默认认识这个名字,会自动把它当虚拟环境)。

2.2、激活:让"python / pip"指向这个小环境

创建完 .venv 之后,它还"没生效"------你直接敲 python,用的还是全局那个。必须先激活 它,系统才会把命令行里的 python、pip 都指向 .venv 里的那一份。不同平台命令不一样:

powershell 复制代码
# Windows PowerShell
. .\.venv\Scripts\Activate.ps1
bash 复制代码
# Windows 命令提示符(CMD)
.venv\Scripts\activate.bat

# Linux / macOS
source .venv/bin/activate

激活成功的标志是:命令行提示符前面多出一个 (.venv) 标记,长这样:

text 复制代码
(.venv) C:\myproject>

小嘉提醒:Windows 的 PowerShell 默认可能禁止运行 .ps1 脚本,激活时报"无法加载文件,因为在此系统上禁止运行脚本"。临时解决办法是在当前 PowerShell 窗口里先执行一句 Set-ExecutionPolicy -Scope Process Bypass -Force(只放开当前这一个窗口,不改系统设置),或者干脆改用上面 CMD 的 activate.bat。

不想激活也可以 :在非交互脚本里,直接用 .venv 里的解释器全路径去跑 pip,效果完全等价(本课程所有实测就是这么跑的):

powershell 复制代码
# 不激活,直接点名 venv 里的 python 来调用 pip
.\.venv\Scripts\python.exe -m pip install six

2.3、验证隔离:看一眼"现在用的到底是哪个 Python"

激活(或直接用 venv 解释器)之后,最直观的验证方法是看 sys.executable------它告诉你当前运行的是哪个 python.exe。本机实测:

python 复制代码
import sys

print("当前解释器:", sys.executable)
print("环境前缀:", sys.prefix)
print("基础解释器:", sys.base_prefix)
text 复制代码
当前解释器: 项目目录\.venv\Scripts\python.exe
环境前缀:   项目目录\.venv
基础解释器: D:\devtools\Python

看这三行就能确认隔离成立:

  • 当前解释器 指向了 .venv\Scripts\python.exe------命令行里的 python 已经是这个小环境的了;
  • 环境前缀 是 .venv 自己------它有独立的"地盘";
  • 基础解释器 还是全局的 D:\devtools\Python------venv 并没有真的复制一份解释器,而是"借"全局的来跑,只是装包、查包都只认自己的小仓库。

再对比一下装包前后。全新 venv 里 pip 只认识自己:

text 复制代码
$ .\.venv\Scripts\python.exe -m pip list
Package Version
------- -------
pip     26.2.1

2.4、退出:deactivate

今天用完了,想回到全局环境,在命令行敲:

bash 复制代码
deactivate

提示符前面那个 (.venv) 标记就消失了。退出后,你在 venv 里装的包并没有被删除,它们还好好躺在 .venv\Lib\site-packages\ 里;只是当前命令行"看不见"它们了------下次激活同一个 .venv,它们立刻又能用。这正是"每个项目一个小仓库"的好处。

2.5、.venv 目录约定与 .gitignore

.venv 整个文件夹不要 提交到 git 仓库、不要拷给别人------它体积不小,而且可以在任何一台机器上用 requirements.txt 一键重建 (第四节讲)。所以项目根目录的 .gitignore 里加一行:

text 复制代码
# .gitignore
.venv/

2.6、pyvenv.cfg 初识

打开 .venv 文件夹根目录,里面有个很小的 pyvenv.cfg 文件,记录了这个环境是"从哪个 Python 建出来的"。本机实测内容:

text 复制代码
home = D:\devtools\Python
include-system-site-packages = false
version = 3.15.0

现在只需认识其中一行:include-system-site-packages = false。它就是隔离开关 ------为 false 表示这个 venv 不去看全局大仓库里的任何第三方包。这就是"为什么全局有 90 个包、venv 却一个都看不见"的答案。这几行背后的完整原理,Day61 会再带你精读一遍。

三、pip 基础命令

pip 是 Python 的包管理器,负责从 PyPI(Python Package Index,全球第三方仓库)下载、安装、升级、卸载第三方库。venv 里已经自带 pip,不需要额外装。记住一个稳妥写法:python -m pip 子命令------它保证用的是"当前这个环境"的 pip,不会误用全局的。

3.1、安装:pip install

我们往刚建的 venv 里装两个很小的纯 Python 包 six 和 pycodestyle,本机实测完整过程:

bash 复制代码
python -m pip install six pycodestyle
text 复制代码
Looking in indexes: https://mirrors.huaweicloud.com/repository/pypi/simple/
Collecting six
  Downloading six-1.17.0-py2.py3-none-any.whl (11 kB)
Collecting pycodestyle
  Downloading pycodestyle-2.15.0-py2.py3-none-any.whl (31 kB)
Installing collected packages: six, pycodestyle
Successfully installed pycodestyle-2.15.0 six-1.17.0

注意最后一行 Successfully installed ...------这就是装成功的标志。哪怕全局环境里本来就有 six,venv 里还是会重新下载一份装进自己的小仓库,两边互不影响。

3.2、查看已装:pip list 与 pip show

pip list 列出当前环境里所有已装的包和版本号。装完后再看一眼:

bash 复制代码
python -m pip list
text 复制代码
Package     Version
----------- -------
pip         26.2.1
pycodestyle 2.15.0
six         1.17.0

想查某个包的详细信息(作者、主页、装在哪、还被谁依赖),用 pip show 包名:

bash 复制代码
python -m pip show six
text 复制代码
Name: six
Version: 1.17.0
Summary: Python 2 and 3 compatibility utilities
Home-page: https://github.com/benjaminp/six
Author: Benjamin Peterson
License: MIT
Location: 项目目录\.venv\Lib\site-packages
Requires:
Required-by:

重点看 Location 这一行:它指向的是项目目录里的 .venv\Lib\site-packages,而不是全局目录。这就用 pip 自己的话,证明了"包装进了小环境、没污染全局"。

3.3、卸载:pip uninstall

不想要了就卸掉。加 -y 表示"不再问我确认,直接卸"。本机实测把 pycodestyle 卸掉:

bash 复制代码
python -m pip uninstall pycodestyle -y
text 复制代码
Found existing installation: pycodestyle 2.15.0
Uninstalling pycodestyle-2.15.0:
  Successfully uninstalled pycodestyle-2.15.0

卸完再 pip list,pycodestyle 就从列表里消失了,只剩 pip 和 six。

3.4、常用选项:-i 镜像源 与 -U 升级

国内直连 PyPI 官方源有时很慢 ,可以用 -i(等价 --index-url)临时指定一个国内镜像站。本机实测用清华源再装一次 six(因为已经装过了,pip 会提示 already satisfied,我们重点看它真的去问了清华源):

bash 复制代码
python -m pip install six -i https://pypi.tuna.tsinghua.edu.cn/simple
text 复制代码
Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple
Requirement already satisfied: six in .\.venv\Lib\site-packages (1.17.0)

常用镜像源记两三个就够:清华 https://pypi.tuna.tsinghua.edu.cn/simple、阿里云 https://mirrors.aliyun.com/pypi/simple/、中科大 https://pypi.mirrors.ustc.edu.cn/simple/。

升级某个包 用 -U(等价 --upgrade):

bash 复制代码
# 把 six 升级到最新版
python -m pip install -U six

小嘉提醒:装包、卸包、升级之前,养成先看一眼命令行提示符前有没有 (.venv) 的习惯。没有它,你辛苦建的隔离就白费了,包装到全局去了。

四、requirements.txt 基础用法

4.1、它解决什么问题

虚拟环境解决了"项目之间不打架",但还有个问题没解决:怎么把这个项目的环境原样搬到另一台机器? 总不能把整个 .venv 文件夹拷过去(里面是和你电脑路径绑死的东西,拷过去也用不了)。

标准答案是一个纯文本文件 requirements.txt:把"这个环境里装了哪些包、各自什么版本"记下来。别人拿到项目后,新建一个空 venv,再照着这份清单一条命令把包装回来,环境就和你一模一样。这就是"可复现"。

4.2、导出:pip freeze

在已经激活(或正在用)这个项目 venv 的状态下执行 pip freeze,它会把当前环境所有包连同精确版本号打印出来:

bash 复制代码
python -m pip freeze
text 复制代码
pycodestyle==2.15.0
six==1.17.0

把这份输出重定向到文件,就得到了 requirements.txt:

bash 复制代码
# 把冻结清单写入项目根目录的 requirements.txt
python -m pip freeze > requirements.txt

小嘉提醒:一定要在你自己项目的 venv 里 freeze!如果忘了激活、在全局环境里 freeze,这份清单会把全局那九十多个无关包全列进去,换机器复现时必出问题。

4.3、复现:pip install -r requirements.txt

现在做个实验:小嘉新建了第二个完全干净的 venv(初始只有 pip),然后执行:

bash 复制代码
# 在第二个干净 venv 里,照着 requirements.txt 把包装回来
python -m pip install -r requirements.txt
text 复制代码
Looking in indexes: https://mirrors.huaweicloud.com/repository/pypi/simple/
Collecting pycodestyle==2.15.0 (from -r requirements.txt (line 1))
  Downloading pycodestyle-2.15.0-py2.py3-none-any.whl (31 kB)
Collecting six==1.17.0 (from -r requirements.txt (line 2))
  Downloading six-1.17.0-py2.py3-none-any.whl (11 kB)
Installing collected packages: six, pycodestyle
Successfully installed pycodestyle-2.15.0 six-1.17.0

装完再 pip list,第二个干净环境里就出现了和第一个环境完全相同的两个包、完全相同的版本号:

text 复制代码
Package     Version
----------- -------
pip         26.2.1
pycodestyle 2.15.0
six         1.17.0

这就是 requirements.txt 的全部魔力:一份文本清单 + 一条命令 = 换台机器也能复现同一个环境。

4.4、最小 requirements.txt 长什么样

它本质上就是一个"每行一个包 + 版本号"的纯文本文件,本机实测内容:

text 复制代码
# requirements.txt
pycodestyle==2.15.0
six==1.17.0

每行格式是 包名==版本号,双等号表示"精确锁定这个版本"。今天先会用 pip freeze 自动生成、会用 pip install -r 安装就够了;至于 >=、~= 这些版本运算符、base/dev/prod 多文件怎么拆分、和 poetry/uv 的 lockfile 有什么区别------这些进阶内容统一放在 Day61,今天不展开。

五、pyproject.toml 初识

5.1、它是什么

pyproject.toml 是现代 Python 项目的"统一配置中心",遵循 PEP 621 规范。从 Python 3.12 起,旧的打包模块 distutils 已经被移除,现在不管是发布自己写的库,还是用现代工具(pip-tools、poetry、uv 等)管理项目,都围着这个文件转。它用一种叫 TOML 的简单格式写配置(Python 3.11+ 标准库的 tomllib 模块就能直接读它)。

5.2、一个最小示例

一个最简单的项目,根目录放一个 pyproject.toml,长这样:

toml 复制代码
# 文件位置:项目根目录\pyproject.toml
[project]
name = "xiaojia-demo"
version = "0.1.0"
description = "跟小嘉学 Python 的第一个示例项目"
requires-python = ">=3.15"
dependencies = [
    "six>=1.16",
    "pycodestyle>=2.11",
]

现在你不需要背它的语法,只要认得:[project] 这一段是在"声明这个项目叫什么、需要哪些第三方库"。

5.3、它和 requirements.txt 的分工(一句话)

今天先建立一个直觉:pyproject.toml 是"声明"------这个包是什么、大概需要哪些库;requirements.txt 是"快照"------这个环境今天精确装了哪几个版本。 前者给打包和别人用你的库看,后者给部署和复现环境用。两者不冲突,而是分工。更细的用法(怎么用 pyproject 统一管理依赖、和 lockfile 怎么取舍)留到 Day61 与 Day62 再深入。

六、从今天起的工作习惯

学完今天,小嘉希望你以后每开一个新项目,都按下面这个顺序来------把它当成肌肉记忆:

  1. 先建项目文件夹,进去第一件事就是建 venv :py -3 -m venv .venv。
  2. 装包前先激活 (或直接用 .\.venv\Scripts\python.exe -m pip ...),确认提示符前有 (.venv)。
  3. .venv/ 加进 .gitignore,这个文件夹不进版本库。
  4. 项目跑通、装好了依赖,就 pip freeze > requirements.txt,把这份清单提交进 git,跟着代码一起走。
  5. 换机器或重装时 :建空 venv → pip install -r requirements.txt,环境立刻复原。

这样坚持下来,你就再也不会经历"项目 A 把项目 B 的包搞坏了"或者"我电脑上明明是好的"这类噩梦了。

小嘉预告:今天讲的是入门够用版 的环境与依赖管理。等你学到后面真实项目,会遇到"想自动锁依赖树""想区分开发用库和生产用库"这类需求------那就是 Day61「虚拟环境与 pip 依赖管理」 的进阶内容(pip-tools、lockfile 对比、多环境拆分);而 Day62「现代包管理器」 会带你认识 uv、poetry 这些比 pip 更快、更省心的新工具。今天先把 venv + pip + requirements.txt 这一套练熟就好。

练习题

  1. 动手题:在任意一个新文件夹里执行 py -3 -m venv .venv,激活后敲 python -c "import sys; print(sys.executable)",看看打印出来的路径是不是指向了你刚建的 .venv\Scripts\python.exe;然后 deactivate 再看一次,解释两次结果为什么不同。
  2. 动手题:激活 venv 后执行 python -m pip install six,再 pip list;接着 deactivate 退出,在全局环境再 pip list,对比两个列表里 six 的"可见性",说说为什么退出后 venv 里的包就"不见了"。
  3. 动手题:激活 venv,pip install six pycodestyle,然后 pip freeze > requirements.txt,打开这个文件看看里面写了哪几行;再新建第二个 venv,执行 pip install -r requirements.txt,验证两边版本号完全一致。
  4. 思考题:为什么 .venv 文件夹要加进 .gitignore?如果直接把整个 .venv 拷给另一台电脑,大概率会出什么问题?(提示:想想 pyvenv.cfg 里写死的 home 路径。)
  5. 思考题:pip show six 输出里的 Location 这一行,在激活 venv 和不激活两种情况下,分别会指向哪里?它能帮你判断什么?
  6. 对比题:用自己的话说一说 pyproject.toml(声明)和 requirements.txt(快照)分工有什么不同?为什么一个项目常常两个文件都要有?

小结

  • 为什么需要虚拟环境:全局环境会污染、版本冲突、不可复现;venv 给每个项目一个独立小仓库,本机实测全新 venv 只有 pip、全局却有九十个包,对比鲜明。
  • venv 是标准库自带模块(Python 3.3+),创建 py -3 -m venv .venv,激活后提示符出现 (.venv),deactivate 退出;.venv 整个目录加进 .gitignore。
  • 隔离开关是 pyvenv.cfg 里的 include-system-site-packages = false;sys.executable 和 pip show 的 Location 是验证隔离是否成立的两大利器。
  • pip 四大基础命令:install(装)、uninstall(卸)、list(列)、show(查详情);-i 临时换国内镜像、-U 升级。
  • requirements.txt:pip freeze 导出当前环境快照,pip install -r 在新环境一键复现;今天用 six + pycodestyle 实测了"导出→在第二个干净 venv 复现"全流程,版本号完全一致。
  • pyproject.toml(PEP 621)是现代项目配置中心,与 requirements.txt 分工:一个声明、一个快照;进阶的 pip-tools / lockfile / 多环境拆分留给 Day61,现代工具留给 Day62。

扩展阅读

相关推荐
MasonYyp1 小时前
‌WSL简单使用
1024程序员节
todoitbo1 小时前
Q1 MuJoCo 模型里的 49 项传感器定义与 60 维数据:一次关节和 IMU 输出核验
人工智能·ai·机器人·1024程序员节·启元机器人
墨抒颖 msy.plus1 小时前
矩阵、注意力与 GPU:拆解 DeepGEMM 与 FlashMLA
人工智能·llm·gpu·源码解析·1024程序员节·deepseek
小冷coding1 小时前
Codex 下载与本地部署实战:从环境准备到跑通第一个任务
1024程序员节
郝学胜-神的一滴1 小时前
游戏设计:原型与开发 11:用概率与电子表格搞定骰子系统
程序人生·游戏·游戏策划·1024程序员节·游戏设计
lusklusklusk1 小时前
Python 流程控制与异常处理笔记:break、continue、pass、return 与 try-except-finally
1024程序员节
paopaokaka_luck2 小时前
心理咨询管理系统(WebSocket实时沟通、基于用户的协同过滤、 ECharts多维分析、 预约业务流程、预约校验与定时处理)
java·vue.js·spring boot·mysql·spring·echarts·1024程序员节
信誓旦旦的程序猿2 小时前
【Python量化实战宝典 #22】涨跌停、连板情绪手册:情绪指标构建(Python 可跑)
python·1024程序员节·股票数据api·股票数据api接口·股票api数据接口·股票量化数据api·股票量化数据接口