正文
代码明明一样,为什么结果不同?
因为程序的运行还依赖解释器、安装的包及其他环境条件。Git 能保存源代码,却不会自动让另一台机器具备完全相同的运行条件。
先把两个容易混淆的角色分开。
Python 解释器负责执行 Python 程序;包则提供可以被程序使用的代码与相关资源。 某些模块随 Python 标准库提供,例如前面用过的 json;另一些由项目另外安装,例如访问特定服务的客户端。
依赖管理,就是明确程序需要哪些组件、接受什么版本,并安排取得、安装和更新。环境隔离则帮助不同项目保有各自的依赖集合,减少一个项目的调整影响另一个项目。
假设会议程序使用了某个日历客户端,它又依赖其他包来处理 HTTP 请求。前者是项目直接选用的依赖,后者属于通过这些包间接引入的依赖。只记住自己在代码中直接使用的名称,未必足以还原整个环境。
另一个细节是,安装时的发行包名称与 Python 中 import 使用的名称,不一定相同。遇到找不到模块,应先核对解释器和安装位置,而不是凭记忆反复执行一个名字相似的安装命令。
在 Python 项目中,常见的隔离方式是虚拟环境,标准库中的 venv 就能创建这类环境。
假设我们位于专门的练习项目目录,并且已有合适的 Python 安装,可以用如下命令说明创建与查看过程:
python3 -m venv .venv
.venv/bin/python -m pip --version
第一行让当前的 python3 运行 venv 模块,在 .venv 目录建立环境。第二行明确使用这个环境里的 Python,运行它所对应的 pip,并显示 pip 的版本与位置。
这些是 macOS 或类似 Unix 环境中的路径示例,没有在本文写作时实际创建环境或安装软件。Windows 下的路径通常不同,不能直接照搬 .venv/bin/python。
venv 主要为项目安排独立的 Python 包安装位置,并关联创建它所用的基础解释器。 它不会因为名字中有“虚拟”,就自动下载另一个 Python 大版本,也不是一台新的虚拟电脑。Python 虚拟环境的机制与使用
使用明确的解释器路径有一个好处:减少“安装时用的是一个 Python,运行时却是另一个”的混乱。很多命令中的 python -m pip,就是要求由这个 Python 来执行对应的 pip。
常见的“激活环境”,主要是在当前终端里调整查找命令的路径等信息,让直接输入 python 时更容易找到环境中的解释器。不激活也可以使用完整路径运行;关闭激活状态,则不等于删除环境或卸载已装的包。
虚拟环境解决了一部分隔离问题。会议程序与另一个数据分析项目,可以各自安装适合自己的包版本,不必争用同一个全局安装位置。但它们仍然运行在本机,能够做什么文件和网络操作,还取决于程序、账户与系统权限。
因此,venv 不是用来阻止代码访问磁盘或联网的安全沙箱。它也不会隔离所有系统库、外部服务和操作系统差异。
接下来,还需要把依赖写清楚。
项目可以使用 pyproject.toml 等配置描述依赖,也可能提供 requirements 文件,用于向安装工具说明要安装哪些项目。不同文件承担什么职责,要看所用工具和项目约定,不能只通过扩展名就判断它是否锁定了全部环境。
假设练习项目已经提供并核对了 requirements.txt,下面的命令表示按其中条目安装:
.venv/bin/python -m pip install -r requirements.txt
这里 -r 表示从文件读取安装要求。命令仍然只是教学示例,本文没有执行安装。真正执行前,解释器是否合适、文件内容来自哪里、条目是否符合项目要求,都需要明确。
版本描述也有不同精度。一个范围可以允许工具在兼容候选中选择;指定某个确切版本,可以减少这一项依赖在不同时间安装时的变化。但只固定一个直接依赖,并不代表它的所有间接依赖也都固定了。
新版本也不一定自动更适合当前项目。更新可能修复问题,也可能改变接口。维护时需要结合更新说明和测试,判断通知查询、参数处理等行为是否仍符合约定。
为了查看当前装了什么,还会遇到 pip freeze。它能按安装要求的形式列出环境中的已安装包。例如,可以把结果写进一个新的快照文件:
.venv/bin/python -m pip freeze > requirements-snapshot.txt
右侧的重定向符号表示把输出写入该文件;如果文件已存在,这种写法会覆盖它。这里用的是示例文件名,并没有实际运行。pip freeze 记录当前安装情况,不会重新求解一份理想依赖集合,也不是专门生成锁文件的工具。pip freeze 的输出与边界
不同版本和选项,也可能影响部分安装工具是否出现在 freeze 输出中。因此,这份文件不能当作解释器、系统组件与全部环境信息的完整清单。
环境里如果还装着别的实验工具,这份快照也可能包含与会议程序无关的项目。因此,记录快照以前,最好知道它来自什么环境,而不是把所有输出都当成程序最小必需项。
专门的锁文件则通常由相应工具在解析依赖后生成,记录选定的依赖结果。不同工具和规范对版本、平台条件、包文件及校验信息的记录范围不同,使用时应当按对应说明恢复。
锁定依赖能够减少部分变化,却不能保证任何机器都原样运行。Python 版本、操作系统、CPU 架构、系统组件,以及远程日历接口的变化,仍可能影响结果。可复现运行需要把这些条件与代码版本一起考虑。
认识 venv 以后,还可以区分“容器”。
容器通常组织更广的进程运行环境,包括文件系统视图及应用所需的相关组件。它与只围绕 Python 解释器和包位置工作的 venv,不是同一个层次;两者也可以组合。容器不等于为每个应用创建一台完整虚拟机,也不自动消除全部平台与权限问题。容器所组织的运行环境
小型会议程序可能只需要一个清楚的虚拟环境与依赖说明,不必为了学习 Agent 就马上引入容器。是否需要更完整的部署封装,要由实际运行和协作条件决定。
依赖还涉及一个独立问题:许可证。
一个包能够被下载安装,不表示它在任何使用方式下都没有要求。采用依赖时,需要查看相关许可证信息与原文,并结合实际用途理解相应条件。软件包、数据和模型也可能分别采用不同条款,不能因为都用于同一个 Agent,就把它们当成同一份许可。Python 包的许可证信息与场景
迁移项目时,也不宜只把 .venv 整个复制给别人,就认为环境已经可靠移植。虚拟环境中可能包含与原解释器路径和平台有关的内容,通常应当根据明确记录的条件重新创建。
程序源码、依赖说明、必要的非敏感配置和使用方法,应该与可重建的环境目录分清。下一位维护者既需要知道装什么,也需要知道怎样确认程序确实工作。
这就接到了下一篇:依赖安装完成、程序没有立刻报错,还不足以说明会议通知功能正确。我们需要用单元测试与集成测试,把预期行为变成能够反复检查的证据。
当前学习编号:02.11 依赖管理与环境隔离。
下一学习编号:02.12 单元测试与集成测试。后续还有其他学习编号。
关联分支:运行条件对应 24.12 可复现运行与环境快照、25.01 服务化、容器与运行环境;版本变化对应 26.09 模型、数据、提示词和工具版本协同、26.13 供应商迁移、维护成本与退出方案。
许可管理对应 26.11 数据、软件与模型许可证管理、12.10 模型许可证与使用条款、12.11 开放权重、开源与可商用的区别。这些支系会在各自编号中单独展开。