Conda的前世今生

在介绍pixi之前,有必要先聊聊Conda。实际上Conda最早诞生于2012年,晚于pip和venv,为什么在已经存在pip和venv的情况下,还要推出一个看似重复的包管理工具呢?

将时间倒回2012年,python的包管理面临着一个巨大的问题。彼时,很多科学计算库(如 NumPy, SciPy, Pandas)底层都是用 C、C++ 或 Fortran 编写的,而 pip 主要安装的是源码包(sdist)。这意味着当你执行pip install numpy时,你的电脑必须装有对应的编译器、动态链接库和复杂的数学库(如 MKL 或 OpenBLAS)。如果环境不匹配,安装就会报错。这是一个让人头疼的事情(尤其是那些编程基础本就不扎实的科学家们)。

当时 Anaconda 的创始人 Travis Oliphant 意识到,科学计算的用户(物理学家、生物学家、金融分析师)不应该把时间花在解决 gcc 编译器报错上。因此,Conda出现了,它直接分发预编译好的二进制文件。这就意味着用户不需要在本地编译,直接下载解压就能用。由于不需要处理那些烦人的依赖,这对很多那些科学家来说简直是救命稻草。而将所有这些库以及非python编写的依赖库打包在一起的巨大的库,就是 Anaconda Distribution。如今,它是目前全球最流行的科学计算平台,本质上是将 Python、Conda 包管理器以及一大堆常用的科学计算库打包在一起的软件发行版。

不过在最初,Anaconda Distribution只是一个将所有软件包放在一起的大杂烩,在后续的更新中,人们意识到需要一个类似于 pip 的命令来管理这些依赖库,比如添加、更新和删除依赖,于是,conda命令行诞生了。他们维护的那个软件包仓库,在当时叫做default

在一段时间以来,他们维护着官方的 defaults ,这是一个经过严格测试的软件包仓库,兼容性极好。但问题也随之而来:科学计算的世界太大了。除了 NumPyPandas 这些通用的,还有成千上万个小众领域的库(比如天文学的 Astropy、地理信息的 GDAL、生物信息学的Biopython)。这对于一家商业公司(当时甚至只是个创业公司)来说,根本没有人力去打包、编译、测试全世界所有的科学软件。而且,Conda 包是二进制文件,需要针对 Windows、Linux、macOS 分别编译,工作量是 PyPI 的数倍。

为了解决这个问题,Anaconda开放了 Channel 机制。 他们推出了 Anaconda.org(最初叫 Binstar.org),允许用户创建自己的 Channel。如果你需要的包官方不提供,那么你可以自己打包上传到你的个人频道,别人只要添加你的 URL 就能下载。

将权限下放给用户,解决了官方维护成本的问题,但是随着个人 Channel 的开放,又带来了过于混乱的问题。很快,大家就发现,如果要装 tensorflow,在搜索时可能会发现:

  • google/tensorflow
  • zhang3/tensorflow
  • alice/tensorflow

如此多的tensorflow,你敢用吗?这些包是基于什么环境编译的?有没有植入恶意代码?彼此兼容吗?一群核心开发者意识到,不能让成千上万个个人频道各自为战。于是他们建立了一个社区驱动的超级频道 —— conda-forge。它引入了自动化的 CI/CD 流水线。任何人都可以在 GitHub 上提交 Recipe,由CI自动构建,并由社区审核。它解决了信任和标准化的问题,成为了现如今的事实标准。

而对于那些生物信息学的软件。很多底层工具是用 Perl、C++ 甚至 Fortran 写的老古董,而且依赖关系极其复杂(不仅仅依赖 Python,还依赖系统级的库)。这些软件对于做金融或做 Web 开发的人来说完全没有用,拖慢搜索速度。而且,生物软件的版本更新逻辑和通用软件完全不同。因此,Bioconda Channel 诞生了。 这是一群生物学家建立的。他们把所有生物相关的软件(超过 7000 个)都放在这个频道里。通过 Channel,不同领域的人可以在自己的圈子里维护自己的生态,互不干扰。

pixi:新时代的包管理方案

既然 Conda 生态已经如此成熟,为什么还需要 pixi?

简单来说,pixi 是一个基于 Rust 编写(底层使用 rattler 库)、强调完全可重现性和极高性能的现代化包管理器。它不仅完全兼容 Conda 庞大的 conda-forge 生态,还通过内置的 uv 库实现了对 PyPI 的“一等公民”级支持。

相比于传统的 Conda 客户端,pixi 的核心优势在于:

  1. 快到极致:依赖解析速度比传统 conda 快几个数量级。
  2. 声明式配置:不再依赖一系列手动输入的命令行指令,所有配置都记录在一个 pixi.toml 中。
  3. 强制锁文件:生成 pixi.lock 确保跨平台、跨机器的安装结果 100% 一致。
  4. 无需激活:直接运行 pixi run 即可,不再需要繁琐的 conda activate

快速上手

对于 Python 项目,pixi 推荐直接使用 pyproject.toml 作为配置文件。这不仅符合 Python 社区的标准,还能让你在同一个文件中管理构建系统、项目元数据以及 pixi 专有的环境配置。

1. 安装 pixi

在 macOS 或 Linux 上,使用官方脚本进行一键安装:

1
curl -fsSL https://pixi.sh/install.sh | sh

安装脚本会将 ~/.pixi/bin 添加到 PATH。安装完成后,重启终端以使配置生效。

2. 初始化项目(pyproject.toml 模式)

进入你的项目目录,使用 --format pyproject 参数初始化:

1
2
pixi init my-project --format pyproject
cd my-project

此时生成的 pyproject.toml 会包含 [tool.pixi.workspace] 部分。pixi 会自动将 requires-python 映射为 Python 依赖,并将当前项目以可编辑模式(editable)添加到环境中。

3. 添加依赖(Conda 与 PyPI 混合)

pixi 遵循 “Conda 优先” 策略:先解析 Conda 依赖,再处理 PyPI 依赖。

1
2
3
4
5
# 添加 Conda 依赖(存放在 [tool.pixi.dependencies])
pixi add python numpy

# 添加 PyPI 依赖(通常存放在 [project.dependencies])
pixi add --pypi rich

执行后,pixi 会更新 pyproject.toml 并同步生成 pixi.lock 文件。该锁文件记录了跨平台一致的精确依赖版本。

4. 运行代码

在 pixi 中,你不需要手动激活环境。通过 pixi run 执行命令时,pixi 会自动确保环境处于最新状态。

直接运行 Python 命令:

1
pixi run python -c "import rich; rich.print('[bold magenta]Hello Pixi![/bold magenta]')"

如果需要交互式 Shell,可以使用:

1
2
3
4
pixi shell
# 此时已进入激活的隔离环境
python --version
exit

5. 查看环境状态

1
2
3
4
5
# 列出当前环境的所有包及其来源(Conda 或 PyPI)
pixi list

# 查看项目配置和虚拟包(Virtual Packages)信息
pixi info

进阶配置

1. 任务管理 (Tasks)

pixi 允许你在 pyproject.toml 中定义跨平台的任务命令,类似于 npm scripts

[tool.pixi.tasks] 区域添加定义:

1
2
3
4
5
6
7
8
[tool.pixi.tasks]
# 简单的字符串任务
start = "python main.py"
# 带依赖的任务:运行 test 之前会先运行 lint
lint = "ruff check ."
test = { cmd = "pytest", depends-on = ["lint"] }
# 指定工作目录
build = { cmd = "python setup.py build", cwd = "scripts" }

使用方式:

1
2
pixi run start
pixi run test

2. 多环境管理 (Features & Environments)

这是 pixi 最强大的特性之一。你可以为测试、生产或文档生成定义不同的环境,而它们共享同一个锁文件。

pixi 会自动将 [project.optional-dependencies][dependency-groups] 识别为“特性 (Features)”:

1
2
3
4
5
6
7
8
9
10
11
[dependency-groups]
test = ["pytest", "pytest-cov"]
docs = ["mkdocs-material"]

[tool.pixi.environments]
# 默认环境
default = { features = [], solve-group = "default" }
# 测试环境:包含基础依赖 + test 特性
test = { features = ["test"], solve-group = "default" }
# 开发全家桶环境
dev = { features = ["test", "docs"], solve-group = "default" }
  • solve-group: 将多个环境放入同一个 solve-group,可以确保它们安装的共同依赖版本完全一致,避免环境切换时的包冲突。

3. 系统需求 (System Requirements)

如果你的项目依赖特定的硬件环境(如 GPU)或系统库,可以通过 system-requirements 告知 pixi,它会在解析依赖时进行校验。

1
2
3
4
[tool.pixi.system-requirements]
linux = "5.10"
cuda = "11.0" # 确保 GPU 驱动支持
libc = "2.31"

深度解析:Conda Channels 与 PyPI 集成

Pixi 的核心灵魂在于它如何协调 Conda 庞大的多语言二进制生态与 PyPI 极速更新的 Python 生态。

1. Conda Channels

在 Pixi 中,Channel 是获取二进制包的路径。默认情况下,Pixi 仅使用 conda-forge,这保证了环境的纯净和高度兼容。

  • 添加自定义频道:如果需要特定领域的包(如高性能计算或生物信息学),可以在 [tool.pixi.workspace] 中配置:
    1
    2
    [tool.pixi.workspace]
    channels = ["pytorch", "nvidia", "conda-forge"]
  • 优先级规则:Pixi 遵循严格的顺序优先级。在上面的配置中,如果 pytorchconda-forge 都有同名的包,Pixi 会优先从 pytorch 下载。
  • 镜像源配置:为了加速国内访问,可以在项目根目录或全局配置(~/.pixi/config.toml)中设置 mirrors,但这通常是针对特定域名的重定向。

2. PyPI 集成

虽然 Pixi 诞生于 Conda 社区,但它通过内置 uv 引擎,将 PyPI 支持做到了极致。

  • 配置位置:
    • [project.dependencies]:标准的 Python 项目依赖,会被 uv 解析。
    • [tool.pixi.pypi-dependencies]:如果你不使用 pyproject.toml 格式,或者想在特定的 Pixi 环境中添加 PyPI 包。
  • 高级依赖类型:Pixi 支持所有现代 pip 支持的安装方式:
    1
    2
    3
    4
    5
    6
    7
    [tool.pixi.pypi-dependencies]
    # 1. 直接从 Git 仓库安装
    requests = { git = "https://github.com/psf/requests.git", branch = "main" }
    # 2. 从特定 URL 安装
    torch = { url = "https://download.pytorch.org/whl/cpu/torch-2.0.1%2Bcpu-cp310-cp310-linux_x86_64.whl" }
    # 3. 本地路径(常用于单体仓库中的模块)
    my-common-lib = { path = "../libs/common" }

3. 最佳实践

Pixi 的解析逻辑是:先 Conda,后 PyPI。

  1. 解决冲突:如果一个包(如 numpy)同时出现在 Conda 依赖和 PyPI 依赖中,Pixi 会强制选择 Conda 版本,并告诉 uv 忽略 PyPI 上的对应包。这是为了利用 Conda 优化的二进制加速(如 MKL)。
  2. 何时用 PyPI?:
    • conda-forge 还没有收录某个新包时。
    • 当某个包的 Conda 版本更新极其缓慢时。
    • 纯 Python 的小型库。
  3. 环境稳定性:建议尽可能将核心科学计算库(NumPy, SciPy, PyTorch)放在 Conda 中,而将 Web 框架、辅助工具放在 PyPI 中。