跳到主要内容
约 6 分钟5 张卡

Python 包管理与虚拟环境

Python 的模块导入决定代码从哪里加载,包管理工具负责安装分发的依赖,虚拟环境则为项目提供独立的解释器入口和依赖目录。三个问题相互关联,但职责不同:安装成功不保证运行时使用了同一个环境,虚拟环境也不会自动修复错误的模块路径。

面试或项目演示中,需要能够说明依赖装在哪里、程序由哪个解释器执行,以及别人怎样重建相同的运行条件。

1. 面试核心​

先记住这几条,再读正文
  • 模块提供自己的名称空间,包组织模块。安装分发名称与代码里的导入名称不保证相同,不能只凭 import 名字猜安装命令。卡 · 模块与包
  • Python 会结合 sys.path 搜索模块,并复用 sys.modules 中已加载的模块。模块首次导入会执行顶层代码,导入不只是读取函数声明。卡 · 导入过程与路径
  • venv 默认隔离项目的第三方依赖,它不自动复制操作系统、数据库或所有环境变量,也不是安全沙箱。卡 · 虚拟环境
  • 用目标解释器执行 python -m pip,再用同一解释器运行程序,可以减少依赖安装到另一个 Python 的问题。卡 · 导入过程与路径
  • 可重复环境需要记录解释器、依赖版本、依赖源与必要配置。pip freeze 是已安装环境的快照,不自动保证跨系统和跨 Python 版本完全一致。卡 · 依赖安装与环境重建

2. 模块与包​

模块是具有独立名称空间的代码单元,普通 .py 文件可以作为模块。包让模块按层级组织,常见的普通包目录包含 __init__.py。Python 也支持没有该文件的命名空间包,不能说所有包都必须有 __init__.py。

例如下面的项目结构:

project/
└── checks/
├── __init__.py
├── rules.py
└── runner.py

checks/rules.py:

def accepted(status):
return status == 200

checks/runner.py:

from .rules import accepted

def main():
print(accepted(200))

if __name__ == "__main__":
main()

在 project 目录执行:

python -m checks.runner

输出为 True。-m 按模块名查找并执行,runner 保留包上下文,所以 .rules 表示同一个包内的模块。直接执行 python checks/runner.py 时,这个文件通常没有可供相对导入使用的父包上下文,会因相对导入失败而报错。

if __name__ == "__main__" 让脚本入口只在作为主程序执行时运行。其他模块导入 checks.runner 时仍会执行函数定义和导入语句,但不会自动调用这里的 main()。

3. 导入过程与路径​

导入系统先检查模块是否已经加载;普通导入通常复用 sys.modules 中缓存的模块对象。需要加载时,再通过相应查找机制定位代码并执行。

sys.path 是普通模块查找的重要路径集合,包含脚本或启动方式决定的路径、标准库路径和环境中的安装目录。工作目录、启动命令或解释器变化,都可能影响最终找到哪个模块。

import sys

print(sys.executable)
print(sys.path[0])

这里的具体输出由环境和启动方式决定。排查“明明安装了却无法导入”时,先核对 sys.executable,再检查导入路径;不要先往 sys.path 随意追加多个目录掩盖项目结构问题。

还要避免把自己的文件命名为 json.py、pytest.py 等正在使用的依赖模块名。当前项目文件可能先被找到,导致导入的对象与预期不同。可以检查模块的 __file__ 属性确认实际来源,但并非所有模块都一定有文件路径。

4. 虚拟环境​

虚拟环境为一个项目提供独立的 Python 环境入口和包安装目录,避免两个项目必须共享同一套第三方依赖版本。

在 macOS 或 Linux 的项目目录中,可以执行:

python3 -m venv .venv
.venv/bin/python -m pip --version
.venv/bin/python -c "import sys; print(sys.prefix != sys.base_prefix)"

最后一条通常输出 True,表示运行在虚拟环境中。Windows 对应解释器路径通常为 .venv\Scripts\python.exe。

激活脚本主要是调整当前终端环境,让 python 等命令指向虚拟环境入口。激活不是使用虚拟环境的必要条件,持续集成和脚本也可以直接调用其中的解释器路径。

虚拟环境默认不使用全局第三方安装目录,但仍依赖基础 Python 和操作系统。它不隔离网络、文件权限和外部数据库,也不会自动装好项目依赖。虚拟环境目录通常不提交到版本库,换机器后应由声明的依赖重新创建。

5. 依赖安装与环境重建​

分发包是安装工具下载和安装的单位,它的名称不一定就是 import 使用的名称。例如 Pillow 对应 PIL 导入包;安装前应查看项目官方说明,而不是反复尝试猜测名称。

使用目标解释器调用 pip,可以明确安装落到哪个环境:

.venv/bin/python -m pip install -r requirements.txt
.venv/bin/python -m pip check

requirements.txt 保存项目选择的依赖约束。写 package==版本 可以固定直接列出的版本,但还要考虑传递依赖、平台条件和依赖源。pip check 检查已安装分发声明的依赖是否兼容,不证明应用测试已经通过。

已有环境可以用 python -m pip freeze 导出安装快照。它可能包含开发者临时安装的工具,也不记录解释器实现、系统库和所有运行配置。构建可重建环境时,应保留:

  • 目标 Python 版本,以及操作系统或平台限制。
  • 项目直接依赖与能够重建的完整版本约束,必要时使用项目选定的锁定文件。
  • 可信依赖源、必要的校验信息和安装命令。
  • 测试入口、配置键与外部服务版本;敏感值通过环境或秘密管理注入,不写入示例配置。

项目通过 pyproject.toml 声明构建与项目元数据时,仍应按所选工具的锁定方式执行。这里的目标是解释环境如何重建,不要求同时熟练使用多套包管理工具。

6 · 面试题卡片​

本页题目与参考回答

6.1 Python 怎样组织和导入包,安装包与导入模块有什么区别?​

考察重点:模块名称空间、包层级、安装单位与导入单位。

相关内容:模块与包、依赖安装与环境重建。

参考回答

模块是代码和名称空间单元,包组织模块层级;pip 安装的是分发包,代码导入的是模块或包。 二者名称不保证相同。

包内部可以按明确的包层级使用绝对或相对导入。包含相对导入的入口适合通过 python -m package.module 执行,以保留包上下文。第三方依赖先按官方说明安装到目标解释器环境,再由相同环境加载。

6.2 为什么导入一个模块可能触发操作,第二次导入却不重复执行?​

考察重点:顶层代码执行与模块缓存。

相关内容:导入过程与路径、模块与包。

参考回答

首次加载模块时会执行顶层代码,之后普通导入通常复用 sys.modules 中的模块对象。 因此顶层网络请求、启动线程或改写全局状态可能在导入时发生。

把主动执行入口放到函数中,并通过主程序判断调用,可以避免被其他模块导入时无意启动任务。重新加载等特殊行为要另行考虑,不能把普通重复导入理解成每次重新执行文件。

6.3 虚拟环境隔离了什么,激活是必要步骤吗?​

考察重点:解释器入口、安装目录与环境边界。

相关内容:虚拟环境。

参考回答

虚拟环境主要隔离项目的第三方依赖安装目录,并提供对应解释器入口。 它仍依赖系统与基础 Python,不隔离文件权限、网络或外部服务。

激活只是方便终端选择命令,可以直接调用 .venv/bin/python 或 Windows 对应路径,无须先激活才能使用。

6.4 pip 显示安装成功,但程序仍报 ModuleNotFoundError,应先查什么?​

考察重点:安装环境、运行解释器与模块搜索路径的一致性。

相关内容:导入过程与路径、依赖安装与环境重建。

参考回答

先确认安装与执行是否用了同一个解释器。 在程序里查看 sys.executable,用该解释器执行 -m pip --version 和相应安装命令。

然后检查分发名称与导入名称是否一致、启动目录和包结构是否正确,以及是否存在同名本地文件遮蔽依赖。不要把所有导入失败都当成“再安装一次”就能解决的问题。

6.5 只提交 pip freeze 输出,能否保证别人完全重建环境?​

考察重点:安装快照、依赖约束与运行平台条件。

相关内容:依赖安装与环境重建。

参考回答

不能单靠它保证。 pip freeze 记录当前安装的分发版本,但不会完整表达 Python 版本、操作系统库、依赖源、配置与外部服务,也可能混入临时工具。

应记录项目所需依赖和可重建的版本约束,再在干净环境执行安装、依赖检查和实际测试。跨平台有条件依赖或缺少可用二进制包时,还需验证目标平台。

7. 参考资料​

来源:Coding 101 原文 · 免费阅读,引用请保留出处。内容使用说明