使用pyenv进行Python版本与虚拟环境的管理

Page content

使用Python,尤其是运行机器学习相关的程序时,有两个经常需要面临的问题:Python的版本、Python依赖包的版本。pyenv可以方便地进行Python版本的管理;再结合pyenv-virtualenv插件,通过对不同项目创建不同的虚拟环境来应对依赖包的版本问题。

这篇文章是基于Ubuntu 18.04 LTS进行的介绍,其它类Unix系统的安装使用可以去项目的主页自行查看。

pyenv项目主页:https://github.com/pyenv/pyenv

pyenv-virtualenv项目主页:https://github.com/pyenv/pyenv-virtualenv

pyenv的原理

当我们在命令行中输入python时,系统会读取一个称为PATH的环境变量,其中包含了许多用冒号分隔的路径,从左向右对这些路径对应的文件夹进行搜索,当遇到一个名称为python的文件时,便对其进行执行。下面的代码展现了环境变量PATH的内容。

root@cc9fe53700ea:~# echo $PATH
/root/.pyenv/shims:/root/.pyenv/bin:/root/.pyenv/shims:/root/.pyenv/bin:/root/.pyenv/shims:/root/.pyenv/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

基于此,pyenv将自己的目录放在PATH变量的最前面,并且该目录包含诸如python、pip的文件(它们被称为shim,意为垫片)。这样,pyenv就拦截了执行python的命令。这些shim将命令传递给pyenv,根据设置决定运行哪个版本的python。该决策根据以下优先级进行:

  1. 读取环境变量PYENV_VERSION进行决定,该变量可以根据pyenv shell命令设置。
  2. 读取当前目录下的.python-version文件进行决定,该文件由pyenv local命令设置。
  3. 递归搜索父目录,由找到的第一个.python-version文件决定。
  4. 读取$(pyenv root)/version文件进行决定,该文件由pyenv global命令设置。$(pyenv root)代表安装pyenv的根目录。
  5. 如果上面的步骤都失败,交由PATH中其他路径处理,效果与不使用pyenv相同。

安装pyenv(Ubuntu下适用)

下载项目到~/.pyenv目录。

 git clone https://github.com/pyenv/pyenv.git ~/.pyenv
 
 # 下面一行失败了也没关系,成功后可以加速pyenv的运行。
 cd ~/.pyenv && src/configure && make -C src

将pyenv添加到PATH中。

# 如果项目不在~/.pyenv,修改PYENV_ROOT的值。
echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bashrc
echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bashrc

修改.bashrc,使能shim与命令自动补全,并且设定pyenv与pyenv-virtualenv插件(后面会安装)的初始化。

echo -e 'if command -v pyenv 1>/dev/null 2>&1; then\n  eval "$(pyenv init --path)"\n  eval "$(pyenv init -)"\n  eval "$(pyenv virtualenv-init -)"\nfi' >> ~/.bashrc

上面一行其实将如下内容写入了~/.bashrc:

if command -v pyenv 1>/dev/null 2>&1; then  # 判断pyenv命令是不是存在
  eval "$(pyenv init --path)"           	# 使能shim与命令自动补全
  eval "$(pyenv init -)"                	# pyenv初始化
  eval "$(pyenv virtualenv-init -)"     	# pyenv-virtualenv初始化
fi

重启shell使得更改生效。

exec "$SHELL"

安装必要的软件包支持pyenv的运行。

sudo apt-get update; sudo apt-get install --no-install-recommends make build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm libncurses5-dev xz-utils tk-dev libxml2-dev libxmlsec1-dev libffi-dev liblzma-dev

使用举例

# 下载版本为3.7.8的Python
pyenv install 3.7.8

# 查看当前系统中存在的Python版本
pyenv versions

# 将全局、当前文件夹下、当前会话Python版本切换为3.7.8
pyenv global 3.7.8
pyenv local 3.7.8
pyenv shell 3.7.8

# 取消local设置
pyenv local --unset

# 卸载Python 3.7.8
pyenv uninstall 3.7.8

当使用pyenv global进行设置时,可能由于全局版本的切换影响其它软件。为了尽可能减少影响,推荐使用pyenv local对工作目录进行版本设置

使用pyenv-virtualenv插件管理虚拟环境

通过pyenv-virtualenv插件,可以为不同的项目创建不同的虚拟环境,进而安装不同的python依赖包。pyenv-virtualenv的安装很简单。

# 下载项目
git clone https://github.com/pyenv/pyenv-virtualenv.git $(pyenv root)/plugins/pyenv-virtualenv
# 重启shell
exec "$SHELL"

使用举例

# 使用Python 3.7.8创建一个名为virenv_378的虚拟环境
pyenv virtualenv 3.7.8 virenv_378
# 激活virenv_378,这样使用pip安装的包将位于virenv_378环境下,activate沿袭了virtualenv的使用方式
pyenv activate virenv_378
# 失活virenv_378
pyenv deactivate virenv_378
# virenv_378也可以像3.7.8一样直接使用global等进行设置
pyenv global virenv_378
pyenv local virenv_378
pyenv shell virenv_378
# 删除virenv_378
pyenv uninstall virenv_378

真实案例

github上的一个项目的环境要求如图中Requirements所示。

为了创建相符的环境,执行如下命令。

# 下载Python 3.7.6
pyenv install 3.7.6
# 创建虚拟环境 analysis-tool
pyenv virtualenv 3.7.6 analysis-tool
# 从当前工作目录下启用该虚拟环境
pyenv local analysis-tool
# 安装相关依赖包,requirements.txt中包含了指定版本的包名
pip install -r requirements.txt