python 的标准库引入了 setuptools 之类的以后,很快 pip 的出世解决了开发人员获得依赖包的问题。感觉现在 C++ 这么长时间了还没能够获得一个类似的标准化,确实有点落伍了。同时这也引入了一种开发模式:通常系统带的 python 包都比较陈旧(系统维护人员决定的更新速度),而开发人员可能会有一些不同的要求,系统包与开发需求不匹配,因此一个比较常见的问题就是怎么替换掉系统包。
venv
这也是 python 标准库提供的一个基本的解决方案,通常我们只需要
$ python3 -m venv venv
$ source venv/bin/activate
$ pip3 install --upgrade pip
就能创建一个 venv 目录然后进入到该环境中,然后通过一组 pip 命令安装需要的包,通过 pip freeze 获得一个 requirements.txt,这样方便重新建立,比如在 docker 的部署说明文件 Dockerfile 里面可以用同样的 pip install -r requirements.txt 获得与开发环境一致的环境。
virtualenvwrapper
一个比较烦人的事情是通过 source venv/bin/activate 进入这个环境有点不方便,比如我们想设置一些环境变量之类的,就需要在在 activate 的前后做一些事情,也就是说我们需要一些 shell script 的整合,virtualenvwrapper 大概就将这一些常见的 shell magic 整理出来一套,我们通过
- workon 进入某个环境
- mkvirtualenv 创建一个环境
这里通常设置
- export WORKON_HOME=$HOME/.virtualenvs 指定这些环境的位置
- 然后需要将 virtualenvwrapper 提供的 completion script /usr/share/bash-completion/completions/virtualenvwrapper 连接到 /etc/bash_completion.d/ 目录里
conda / mamba
Conda 基本上将 virtual env 的想法发挥到极致,从某种程度上它甚至可以提供系统库,比如通常 pip 安装某些包会提示安装一些 -dev 的系统包,这是因为某些库安装需要编译连接到系统提供的某些库。conda 更进一步也提供了一些系统库、应用程序,更像是一个 dpkg + virtualenvwrapper 的合体。当然这个实现性能不一定很好,这导致后面有一些诸如 mamba 的实现,提升了一些性能,简化了 conda 的安装等等。
某些情况下特别是如果想选择不同的 python 版本,可能 conda 比起上面的两种加 pyenv 还是要方便很多的。
如何选择
官方的版本其实最容易获得,因此使用的代价偏低,但是一旦项目变得复杂可能不得不迁移到后面的选项。如果不介意在系统上安装 conda / mamba 的话可能这反而是一个一劳永逸的解决方案。
Conda 的问题是引入到虚拟环境的东西太多会导致很多原有环境程序坏掉,尤其是像 Qt 这样对插件 ABI 版本有严格要求的。Conda 支持默认激活虚拟环境更加剧了这个问题,在 Arch Linux CN 的群组里经常因为这个遇到应用无法正常运行的问题。
你说的默认激活虚拟环境是提供的那个 base environment 吗?如果在里面装了某些库和系统冲突就会导致问题?
如果是这样,是不是不应该在 base 里面安装这些东西呢?
毕竟 conda 不是 container,仅仅靠一些环境变量改变链接库的位置,肯定是有一定的局限性的。
base 的存在只是加剧了这个问题,即使没有 base,只要你在虚拟环境里安装了这些库(比如 openssl qt),在虚拟环境激活状态调用使用系统库的程序都有可能出现问题。
可以参考该问题在 Arch Linux CN 出现的频率: https://luoxu.archlinuxcn.org/#g=1031857103&q=conda
明白,靠环境变量做这种定制化是存在这种风险的,一般还是在一个比较干净的 container 里面做部署会干扰少很多的。系统包跟虚拟环境并没有完全的隔离,一旦有个变量设置的不对或者混淆了,折腾错误 debug 都太花时间,不值得。
感觉是比较熟悉 Linux 特别是 C/C++ 编译、链接关系的人会考虑的临时方案。但是一般不怎么熟悉、不想花时间调试问题的人,不建议这种混合搞法。