Python批量导出当前环境所有库的版本清单
介绍多种Python批量导出当前环境所有已安装库及版本号的方法,包括pip freeze、pip list、pkg_resources和importlib.metadata,附带格式化输出和常见问题处理。
在迁移项目、搭建新环境或排查依赖冲突时,经常需要把当前 Python 环境中所有已安装的第三方库连同版本号一起导出。这件事可以在命令行用 pip 一行完成,也可以在 Python 脚本中通过标准库拿到同样的信息。本文介绍四种常用方法,说明各自的输出格式、适用场景和需要注意的细节。
pip freeze —— 最常用的导出方式
pip freeze 是最直接的做法。它会列出当前环境中通过 pip 安装的所有包,每行一个,格式为 包名==版本号,和 requirements.txt 的写法完全一致。
导出到文件
在终端执行:
pip freeze > requirements.txt
执行后当前目录下会生成 requirements.txt,内容类似:
# 示例输出
certifi==2024.8.30
charset-normalizer==3.3.2
idna==3.10
numpy==1.26.4
pandas==2.2.3
python-dateutil==2.9.0.post0
requests==2.32.3
six==1.16.0
urllib3==2.2.3
▲ 示例内容,实际版本号取决于你的环境
这个文件可以直接用于另一台机器或新虚拟环境的批量安装:
pip install -r requirements.txt
需要注意的地方
pip freeze 默认不列出 pip、setuptools、wheel 这几个"基础设施"包。如果需要把它们也包含进去,加上 --all 参数:
pip freeze --all > requirements_all.txt
如果你在系统全局 Python 上运行,导出结果会包含系统自带的包和你手动安装的包混在一起。建议在虚拟环境中操作,这样导出的清单只包含项目实际依赖的库。
pip list —— 带格式的完整列表
pip list 同样列出所有已安装的包和版本号,但它的默认输出是表格样式,适合在终端直接阅读。和 pip freeze 的主要区别是:pip list 会把 pip、setuptools 等包也列出来,而且格式不是 == 连接的。
pip list
终端输出类似:
# 示例输出
Package Version
------------------ ----------
certifi 2024.8.30
numpy 1.26.4
pandas 2.2.3
pip 24.2
requests 2.32.3
setuptools 75.1.0
▲ 示例内容
导出为 JSON 或其他格式
如果想在脚本中解析这份清单,JSON 格式更方便:
pip list --format=json > packages.json
输出是一个 JSON 数组,每个元素包含 name 和 version 两个字段:
# 示例输出
[
{"name": "certifi", "version": "2024.8.30"},
{"name": "numpy", "version": "1.26.4"},
{"name": "pandas", "version": "2.2.3"}
]
▲ 示例,实际列表更长
如果只想看哪些包可以升级,--outdated 参数会列出有更新版本可用的包:
pip list --outdated
在 Python 脚本中获取版本清单
有时候不方便打开终端,或者需要在程序运行时自动收集环境信息,这时候可以直接在 Python 脚本里获取。常用的有两种方式。
方式一:pkg_resources(兼容较老版本)
pkg_resources 是 setuptools 附带的模块,在绝大多数 Python 环境中都能直接使用。它通过 working_set 属性返回当前环境中所有已安装的发行版(distribution)。
import pkg_resources
installed = sorted(
[(d.project_name, d.version) for d in pkg_resources.working_set],
key=lambda x: x[0].lower()
)
for name, ver in installed:
print(f"{name}=={ver}")
这段代码会在终端打印出和 pip freeze 类似的结果。如果要写入文件,把 print 换成文件写入即可:
with open("versions.txt", "w", encoding="utf-8") as f:
for name, ver in installed:
f.write(f"{name}=={ver}\n")
pkg_resources 在加载时会扫描所有包的元数据,当环境中的包特别多(几百个以上)时,导入速度可能偏慢。Python 3.8 之后推荐使用 importlib.metadata,性能更好。
方式二:importlib.metadata(Python 3.8+)
从 Python 3.8 开始,标准库提供了 importlib.metadata 模块,不需要安装任何第三方包就能查询已安装包的信息。
from importlib.metadata import distributions
installed = sorted(
[(d.metadata["Name"], d.version) for d in distributions()],
key=lambda x: x[0].lower()
)
for name, ver in installed:
print(f"{name}=={ver}")
这个方法和 pkg_resources 的结果基本一致,但不依赖 setuptools,启动更快。如果你的项目环境是 Python 3.8 或更新版本,优先用这种方式。
导出为 CSV 或 JSON
拿到列表后,可以按需输出成不同格式。下面是导出为 CSV 和 JSON 的完整示例:
import csv
import json
from importlib.metadata import distributions
installed = sorted(
[(d.metadata["Name"], d.version) for d in distributions()],
key=lambda x: x[0].lower()
)
# 导出 CSV
with open("packages.csv", "w", newline="", encoding="utf-8") as f:
writer = csv.writer(f)
writer.writerow(["包名", "版本"])
writer.writerows(installed)
# 导出 JSON
data = [{"name": name, "version": ver} for name, ver in installed]
with open("packages.json", "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=2)
虚拟环境与全局环境的区别
无论用哪种方法,导出结果取决于你在哪个 Python 环境中执行命令。这一点非常关键,弄混了会导致依赖清单不准确。
如果你使用了 venv 或 virtualenv 创建的虚拟环境,激活后再导出,清单只包含这个虚拟环境中安装的包。这通常是你想要的结果——干净、准确、和项目一一对应。
如果没有激活任何虚拟环境,pip freeze 和 Python 脚本都会读取系统全局 Python 的包列表。这个列表可能包含操作系统预装的包、其他项目安装过的包,混在一起很难分辨哪些是当前项目需要的。
确认当前 Python 指向哪个环境:
which python
# 或
python -c "import sys; print(sys.prefix)"
如果输出路径中包含你的虚拟环境目录名,说明当前处于虚拟环境中。
Conda 环境的导出方式
如果你使用的是 Conda 而不是 pip 管理包,pip freeze 仍然能用,但它只能看到通过 pip 安装的包,无法列出通过 conda install 安装的包。要导出完整的 Conda 环境,用 Conda 自己的命令:
conda list --export > conda_packages.txt
或者导出为可以精确复现环境的 YAML 文件:
conda env export > environment.yml
在新机器上,用这个文件可以一步还原整个环境:
conda env create -f environment.yml
两个环境之间的版本差异对比
把两个环境的版本清单导出后,可以用 diff 命令直接对比差异:
# 分别在两个环境中导出
pip freeze > env_a.txt # 环境 A
pip freeze > env_b.txt # 环境 B
# 对比
diff env_a.txt env_b.txt
如果想在 Python 中做对比,可以把两个清单读进字典,然后逐项检查:
def load_requirements(filepath):
pkgs = {}
with open(filepath, encoding="utf-8") as f:
for line in f:
line = line.strip()
if "==" in line:
name, ver = line.split("==", 1)
pkgs[name.lower()] = ver
return pkgs
a = load_requirements("env_a.txt")
b = load_requirements("env_b.txt")
all_keys = sorted(set(a) | set(b))
for key in all_keys:
va, vb = a.get(key, "未安装"), b.get(key, "未安装")
if va != vb:
print(f"{key}: A={va} B={vb}")
这在排查"在我的机器上能跑、在服务器上报错"这类问题时特别有用。
常见问题
pip freeze 输出中出现 @ file:/// 开头的行
如果某个包是通过本地路径安装的(比如 pip install -e . 或 pip install ./some_package),pip freeze 会输出类似 mypackage @ file:///home/user/project 的格式,而不是 mypackage==1.0.0。这种行在另一台机器上执行 pip install -r 时会因为路径不存在而失败。解决办法是手动把这些行改成正常的 包名==版本号 格式,或者从 PyPI 重新安装这些包。
导出的列表包含不需要的包
在一个用了很久的环境里,pip freeze 可能会列出很多已经不再使用的包。可以借助 pipreqs 这个工具,它通过扫描项目源码中的 import 语句来生成 requirements.txt,只包含项目实际用到的包:
pip install pipreqs
pipreqs /path/to/your/project
生成的 requirements.txt 会放在项目根目录下,只包含代码里确实导入过的包。不过它无法识别通过字符串动态导入的包,所以生成后建议检查一遍。
以上四种方法覆盖了从命令行快速导出到脚本内程序化获取的主要场景。日常使用中,pip freeze 加重定向是最快的做法;需要在代码里动态获取时,importlib.metadata 是 Python 3.8 以后的标准选择。无论用哪种方式,记得确认当前激活的是正确的环境,导出的清单才有意义。
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









