从接触Python以来,一直都是采用 virtualenv 和 virtualenvwrapper 来管理不同项目的依赖环境,通过 workon 、
mkvirtualenv 等命令进行虚拟环境切换,很是愉快。
然而,最近想让项目能兼容更多的Python版本,例如至少同时兼容 Python2.7 和 Python3.3+ ,就发现采用之前的方式行不通了。
最大的问题在于,在本地计算机同时安装 Python2.7 和 Python3 后,即使分别针对两个Python版本安装了 virtualenv 和
virtualenvwrapper ,也无法让两个Python版本的 workon 、 mkvirtualenv
命令同时生效。另外一方面,要想在本地计算机安装多个Python版本,会发现安装的成本都比较高,实现方式也不够优雅。
幸运地是,针对该痛点,已经存在一个比较成熟的方案,那就是 pyenv 。
如下是官方的介绍。
pyenv lets you easily switch between multiple versions of Python. It's simple, unobtrusive, and follows the UNIX tradition of single-purpose tools that do one thing well.
This project was forked from rbenv and ruby-build , and modified for Python.
本文就针对 pyenv 最核心的功能进行介绍。
基本原理
如果要讲解 pyenv 的工作原理,基本上采用一句话就可以概括,那就是:修改系统环境变量 PATH 。
对于系统环境变量 PATH ,相信大家都不陌生,里面包含了一串由冒号分隔的路径,例如 /usr/local/bin:/usr/bin:/bin
。每当在系统中执行一个命令时,例如 python 或 pip ,操作系统就会在 PATH
的所有路径中从左至右依次寻找对应的命令。因为是依次寻找,因此排在左边的路径具有更高的优先级。
而 pyenv 做的,就是在 PATH 最前面插入一个 $(pyenv root)/shims 目录。这样, pyenv 就可以通过控制 shims
目录中的Python版本号,来灵活地切换至我们所需的Python版本。
如果还想了解更多细节,可以查看 pyenv 的文档介绍及其源码实现。
环境初始化
pyenv 的安装方式包括多种,重点推荐采用 pyenv-installer 的方式,原因主要有两点:
通过 pyenv-installer 可一键安装 pyenv 全家桶,后续也可以很方便地实现一键升级;
pyenv-installer 的安装方式基于 GitHub ,可保证总是使用到最新版本的 pyenv ,并且 Python 版本库也是最新最全的。
install && config
通过如下命令安装 pyenv 全家桶。
$ curl -L https://raw.githubusercontent.com/pyenv/pyenv- installer/master/bin/pyenv-installer | bash
内容除了包含 pyenv 以外,还包含如下插件:
- pyenv-doctor
- pyenv-installer
- pyenv-update
- pyenv-virtualenv
- pyenv-which-ext
安装完成后, pyenv 命令还没有加进系统的环境变量,需要将如下内容加到 ~/.zshrc 中,然后执行 source ~/.zshrc 。
export PATH=$HOME/.pyenv/bin:$PATH
eval "$(pyenv init -)"
eval "$(pyenv virtualenv-init -)"
完成以上操作后, pyenv 就安装完成了。
$ pyenv -v
pyenv 1.0.8
如果不确定 pyenv 的环境是否安装正常,可以通过 pyenv doctor 命令对环境进行检测。
$ pyenv doctor
Cloning /Users/Leo/.pyenv/plugins/pyenv-doctor/bin/.....
Installing python-pyenv-doctor...
BUILD FAILED (OS X 10.12.3 using python-build 20160602)
Last 10 log lines:
checking for memory.h... yes
checking for strings.h... yes
checking for inttypes.h... yes
checking for stdint.h... yes
checking for unistd.h... yes
checking openssl/ssl.h usability... no
checking openssl/ssl.h presence... no
check