Ubuntu使用pyenv管理多版本python

使用pyenv的初衷是为了解决系统内多个python版本管理混乱的问题。实际开发中,切换版本时由于系统内装了多个python版本,不记得每个版本的别名,甚至不记得都有哪些版本,而pyenv正好帮我解决了这个痛点。

pyenv是什么?

pyenv是一个forked自ruby社区的简单、低调、遵循UNIX哲学的Python环境管理工具, 它可以轻松切换全局解释器版本, 同时结合vitualenv插件(virtualenvwrapper自然也可以)可以方便的管理对应的包源。

从官方给出的介绍来看没感觉出来其相对于其他版本管理的工具有什么优势,下面就实际走一遍流程,感受下pyenv的便捷之处。

安装pyenv

下载pyenv

我这里直接以家目录为例了,很多软件的配置文件等都是以隐藏文件的形式放在家目录下或者根目录下,我们的安装路径设置为~/.pyenv

1
git clone https://github.com/pyenv/pyenv.git ~/.pyenv

以上操作会将pyenv安装到当前用户的~/.pyenv

定义环境变量

将以下内容放到~/.bashrc文件最后

1
2
3
export PATH=~/.pyenv/bin:$PATH
export PYENV_ROOT=~/.pyenv
eval "$(pyenv init -)"

然后激活pyenv

1
source ~/.bashrc

或者直接终端使用echo命令

1
2
3
echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bashrc
echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bashrc
echo 'eval "$(pyenv init -)"' >> ~/.bashrc

注意:此时需要重启终端,才能使路径更改生效

pyenv使用

查看可安装的python版本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
root@UYYNOT:~# pyenv install --list
Available versions:
2.1.3
2.2.3
2.3.7
2.4.0
2.4.1
2.4.2
2.4.3
2.4.4
2.4.5
2.4.6
2.5.0
2.5.1
2.5.2
2.5.3
2.5.4
2.5.5
2.5.6
2.6.0
2.6.1
2.6.2
2.6.3
……

查看已安装的python版本

1
2
3
4
5
root@UYYNOT:~# pyenv versions
system
3.8.5
3.10.5
* 3.11.1 (set by /root/.pyenv/version)

此处的system是系统自带的Python版本,Ubuntu中系统自带2.7和3.5,*代表当前正在使用的python版本

查看当前使用的python版本

1
2
root@UYYNOT:~# pyenv version
3.11.1 (set by /root/.pyenv/version)

安装python版本

安装python依赖包

在安装python之前,要先安装python的一些依赖包,否则会安装失败

1
2
sudo apt-get install libc6-dev gcc
sudo apt-get install -y make build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm

安装python指定版本

1
2
3
root@UYYNOT:~# pyenv install 3.6.15
Downloading Python-3.6.15.tar.xz...
-> https://www.python.org/ftp/python/3.6.15/Python-3.6.15.tar.xz

默认从官方下载会比较慢,你可以考虑更换其他源进行下载,但是如果追求最新版本可能国内源就不在你考虑范围了(淘宝源python最新版本为3.9)。

自行下载安装python版本包
1
2
3
4
cd ~/.pyenv
sudo mkdir cache
wget -c https://npm.taobao.org/mirrors/python/3.6.15/Python-3.6.15.tar.xz -P ~/.pyenv/cache/
pyenv install 3.6.15

此处不确定该淘宝源可用,请自行验证。

更新数据库

1
root@UYYNOT:~# pyenv rehash

切换python版本

xxx表示要切换的版本

有三种切换方式 global local shell

  • **global**: 全局环境。在未再次使用 global切换环境之前,一直使用此环境。
  • **local**: 本次登录环境。重启后,则环境失效,并返回当前global的环境。
  • **shell**: 局部(临时)环境。关闭命令行窗口,则环境失效,并返回当前global的环境。
1
root@UYYNOT:~# pyenv global 3.10.5

验证是否切换成功,经验证已经成功切换至3.10.5

1
2
3
4
5
6
7
root@UYYNOT:~# pyenv version
3.10.5 (set by /root/.pyenv/version)
root@UYYNOT:~# pyenv versions
system
3.8.5
* 3.10.5 (set by /root/.pyenv/version)
3.11.1

卸载指定python版本

1
root@UYYNOT:~# pyenv uninstall 3.6.5

注意事项:

  • 系统自带的脚本会以/usr/bin/python的方式直接调用老版本的python,因而不会对系统脚本产生影响。
  • 使用pip安装第三方模块时会安装到~/.pyenv/versions/3.6.5下,不会和系统模块发生冲突。
  • 使用pip安装模块后,可能需要执行pyenv rehash更新数据库。

pyenv-virtualenv使用

virtualenv

python虚拟环境。将一个目录建立为一个虚拟的python环境, 这样的话, 用户可以建立多个虚拟环境, 每个环境里面的python版本可以是不同的, 也可以是相同的, 而且环境之间相互独立。

首先我们可以用pyenv 安装多个python 版本, 比如安装了2.5, 2.6, 3.3 三个版本。 用户可以随意切换当前默认的python版本。 但这时候, 每个版本的环境仍是唯一的, 如果我们想在环境中安装一些库的话, 还是会导致这个版本的环境被修改。 这个时候, 如果我们用virtualenv去建立虚拟环境, 就可以完全保证系统路径的干净。无论你在虚拟环境中安装了什么程序, 都不会影响已安装版本的系统环境

安装pyenv-virtualenv

pyenv已经帮我们以plugin的形式安装好了。如果未安装,则需要我们手动安装一下。我们的pyenv的安装目录是~/.pyenv,所以我们需要把pyenv-virtualenv克隆到~/.pyenv/plugins下,执行下面的命令

1
git clone https://github.com/pyenv/pyenv-virtualenv.git ~/.pyenv/plugins/pyenv-virtualenv

配置环境变量

1
2
3
4
5
echo 'eval "$(pyenv virtualenv-init -)"' >> ~/.bashrc
source ~/.bashrc
# 或
# 重新初始化 shell 环境,如果不执行该命令需要重新启动终端窗口
# exec $SHELL

创建虚拟环境

1
2
3
4
5
6
7
8
9
root@UYYNOT:~# pyenv virtualenv 3.8.5 py_test_3_8_5
created virtual environment CPython3.8.5.final.0-64 in 341ms
creator CPython3Posix(dest=/root/.pyenv/versions/3.8.5/envs/py_test_3_8_5, clear=False, no_vcs_ignore=False, global=False)
seeder FromAppData(download=False, pip=bundle, setuptools=bundle, wheel=bundle, via=copy, app_data_dir=/root/.local/share/virtualenv)
added seed packages: pip==23.0, setuptools==67.1.0, wheel==0.38.4
activators BashActivator,CShellActivator,FishActivator,NushellActivator,PowerShellActivator,PythonActivator
Looking in links: /tmp/tmpsevlagwp
Requirement already satisfied: setuptools in /root/.pyenv/versions/3.8.5/envs/py_test_3_8_5/lib/python3.8/site-packages (67.1.0)
Requirement already satisfied: pip in /root/.pyenv/versions/3.8.5/envs/py_test_3_8_5/lib/python3.8/site-packages (23.0)

验证是否创建环境成功

1
2
3
4
5
6
7
8
9
10
root@UYYNOT:~# ls .pyenv/versions
3.10.5 3.11.1 3.8.5 py_test_3_8_5
root@UYYNOT:~# ll .pyenv/versions
total 20
drwxr-xr-x 5 root root 4096 Feb 7 18:41 ./
drwxr-xr-x 15 root root 4096 Feb 7 14:28 ../
drwxr-xr-x 6 root root 4096 Feb 7 14:19 3.10.5/
drwxr-xr-x 7 root root 4096 Feb 7 14:44 3.11.1/
drwxr-xr-x 7 root root 4096 Feb 7 18:41 3.8.5/
lrwxrwxrwx 1 root root 46 Feb 7 18:41 py_test_3_8_5 -> /root/.pyenv/versions/3.8.5/envs/py_test_3_8_5/

使用虚拟环境

1
2
3
root@UYYNOT:~# pyenv activate py_test_3_8_5
(py_test_3_8_5) root@UYYNOT:~# python -V
Python 3.8.5

退出虚拟环境

1
2
(py_test_3_8_5) root@UYYNOT:~# pyenv deactivate
root@UYYNOT:~#

删除虚拟环境

1
root@UYYNOT:~# rm -rf ~/.pyenv/versions/py_test_3_8_5

注意事项

当我们使用pyenv virtualenv创建虚拟环境的时候,验证环境是否创建成功的时候,不知道大家有没有注意到最后一行。

1
2
3
4
5
6
7
8
root@UYYNOT:~# ll .pyenv/versions
total 20
drwxr-xr-x 5 root root 4096 Feb 7 18:41 ./
drwxr-xr-x 15 root root 4096 Feb 7 14:28 ../
drwxr-xr-x 6 root root 4096 Feb 7 14:19 3.10.5/
drwxr-xr-x 7 root root 4096 Feb 7 14:44 3.11.1/
drwxr-xr-x 7 root root 4096 Feb 7 18:41 3.8.5/
lrwxrwxrwx 1 root root 46 Feb 7 18:41 py_test_3_8_5 -> /root/.pyenv/versions/3.8.5/envs/py_test_3_8_5/

可以看见虚拟环境并不是直接创建在了~/.pyenv/versions/,而只是创建了一个软连接,实际指向的位置在要创建的python版本下的envs文件夹内,所以当你使用rm -rf ~/.pyenv/versions/py_test_3_8_5去删除虚拟环境时,其实删除的只是一个软连接。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 查看当前已安装的python版本
root@UYYNOT:~# pyenv versions
system
3.8.5
3.8.5/envs/py_test_3_8_5
* 3.10.5 (set by /root/.pyenv/version)
3.11.1
# 查看versions中版本文件夹
root@UYYNOT:~# ll .pyenv/versions
total 20
drwxr-xr-x 5 root root 4096 Feb 8 09:12 ./
drwxr-xr-x 15 root root 4096 Feb 7 14:28 ../
drwxr-xr-x 6 root root 4096 Feb 7 14:19 3.10.5/
drwxr-xr-x 7 root root 4096 Feb 7 14:44 3.11.1/
drwxr-xr-x 7 root root 4096 Feb 7 18:41 3.8.5/
# 激活虚拟环境时使用tab进行自动回填提示
root@UYYNOT:~# pyenv activate
3.8.5/envs/py_test_3_8_5 --help --unset
# 激活虚拟环境
root@UYYNOT:~# pyenv activate 3.8.5/envs/py_test_3_8_5
# 查看python版本
(3.8.5/envs/py_test_3_8_5) root@UYYNOT:~# python -V
Python 3.8.5

通过以上操作可以看出,虚拟环境并没有被我们删除,我们还需要去python版本对应的envs下删除实际创建的虚拟环境

1
2
3
4
5
6
7
8
9
root@UYYNOT:~# rm -rf .pyenv/versions/3.8.5/envs/py_test_3_8_5/
root@UYYNOT:~# pyenv versions
system
3.8.5
* 3.10.5 (set by /root/.pyenv/version)
3.11.1
# 使用tab自动回填提示也没有了刚才的虚拟环境
root@UYYNOT:~# pyenv activate
--help --unset

pyenv-virtualenvwrapper

没有使用pyenv的时候,对于virtualenv环境的混乱,我们通常会使用virtualenvwrapper来管理,主要是每次开启虚拟环境之前要去虚拟环境所在目录下的 bin 目录下 source 一下 activate,这就需要我们记住每个虚拟环境所在的目录。

并且还有可能你忘记了虚拟环境放在哪。。。

  • 一种可行的解决方案是,将所有的虚拟环境目录全都集中起来,例如/opt/all_venv/,并且针对不同的目录做不同的事。
  • 使用virtualenvwrapper管理你的虚拟环境(virtualenv),其实他就是统一管理虚拟环境的目录,并且省去了source的步骤。

但是我们现在使用了pyenv,将virtualenv作为其一个插件来使用,我们可以使用pyenv activate tab自动回填方式确定已有环境,而且各个环境包也都统一管理在一个目录里,但是,这并不意味着我们使用pyenv-virtualenv没有缺点

  • 删除虚拟环境麻烦,需要手动删除软连接和虚拟环境实际目录(上面已经提到过)

  • 使用pyenv-virtualenv创建虚拟环境对于pyenv来说相当于创建了一个python版本,会与pyenv安装的python版本混在一起,并且会重复显示创建的虚拟环境和软连接,如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    root@UYYNOT:~/.pyenv/versions/3.8.5/envs# pyenv virtualenv 3.10.5 pyenv_31005
    created virtual environment CPython3.10.5.final.0-64 in 351ms
    creator CPython3Posix(dest=/root/.pyenv/versions/3.10.5/envs/pyenv_31005, clear=False, no_vcs_ignore=False, global=False)
    seeder FromAppData(download=False, pip=bundle, setuptools=bundle, wheel=bundle, via=copy, app_data_dir=/root/.local/share/virtualenv)
    added seed packages: pip==23.0, setuptools==67.1.0, wheel==0.38.4
    activators BashActivator,CShellActivator,FishActivator,NushellActivator,PowerShellActivator,PythonActivator
    root@UYYNOT:~/.pyenv/versions/3.8.5/envs# pyenv versions
    system
    3.8.5
    3.10.5
    3.10.5/envs/pyenv_31005
    * 3.11.1 (set by PYENV_VERSION environment variable)
    pyenv_31005

    可以看到pyenv_310053.10.5/envs/pyenv_31005本来是一个,重复跟pyenv安装的python版本混在一起,后续进行python版本切换的时候也会让你一脸懵。

  • 我们使用pyenv切换版本至虚拟环境时退不出来,只能通过切换到其他版本才能退出来(也就是你只能在pyenv activate时才能用pyenv deactivate退出)

    1
    2
    3
    4
    5
    6
    root@UYYNOT:~# pyenv shell pyenv_31005
    (pyenv_31005) root@UYYNOT:~# pyenv deactivate
    (pyenv_31005) root@UYYNOT:~# python -V
    Python 3.10.5
    (pyenv_31005) root@UYYNOT:~# pyenv shell 3.11.1
    root@UYYNOT:~#

以上就是我用pyenv-virtualenv的感受,虽然缺点说了这么多,但也有一个很显眼的优点就是创建虚拟环境时可以指定python版本,而pyenv-virtualenvwrapper只能切换到指定python版本创建对应的虚拟环境。

安装 pyenv-virtualenvwrapper 作为 pyenv 插件

pyenv-virtualenvwrapper 安装为 pyenv 插件将使您能够访问 pyenv virtualenvwrapperandpyenv virtualenvwrapper_lazy命令。

1
$ git clone https://github.com/pyenv/pyenv-virtualenvwrapper.git ~/.pyenv/plugins/pyenv-virtualenvwrapper

这会将最新开发版本的 pyenv-virtualenvwrapper 安装到$(您的pyenv目录)/plugins/pyenv-virtualenvwrapper目录中。从该目录中,您可以签出特定的发布标签。要更新 pyenv-virtualenvwrapper,请运行git pull以下载最新的更改。

激活

安装完成后使用下列命令激活pyenv-virtualenvwrapper

1
$ pyenv virtualenvwrapper

或者,如果你喜欢virtualenvwrapper_lazy.sh

1
$ pyenv virtualenvwrapper_lazy

配置文件

要让 virtualenvwrapper 使用pyvenv而不是创建虚拟环境virtualenv,请设置 PYENV_VIRTUALENVWRAPPER_PREFER_PYVENV环境变量和环境存放目录。例如,在您的.bashrc配置中设置以下内容:

1
2
3
export PYENV_VIRTUALENVWRAPPER_PREFER_PYVENV="true"
export WORKON_HOME=$HOME/.virtualenvs
pyenv virtualenvwrapper_lazy

最后一行pyenv virtualenvwrapper_lazy是为了让你每次打开shell,自动激活pyenv-virtualenvwrapper,可以直接使用virtualenvwrapper命令。

使配置生效

1
2
3
source .bashrc
或者
exec "$SHELL"

pip安装virtualenvwrapper

第一次使用新的 Python 环境需要安装此包,否则创建的虚拟环境 Python 版本仍为系统默认,要在pyenv里安装的每个python版本中安装virtualenvwrapper包才可使用

其实使用一下命令即可为当前python版本安装virtualenvwrapper,并且如果第一个版本装过后,切换版本的话会利用缓存进行安装,速度上快很多。

我们已经将该条语句写入了.bashrc文件中,当你source .bashrc时,会自动为你安装

1
pyenv virtualenvwrapper_lazy

或者使用以下命令,相对较慢

1
pip install virtualenvwrapper

查看是否安装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
root@UYYNOT:~# pyenv version
3.8.5 (set by PYENV_VERSION environment variable)
root@UYYNOT:~# python -V
Python 3.8.5
root@UYYNOT:~# pip list
Package Version
----------------- -------
distlib 0.3.6
filelock 3.9.0
pbr 5.11.1
pip 23.0
platformdirs 2.6.2
setuptools 67.2.0
stevedore 4.1.1
virtualenv 20.18.0
virtualenv-clone 0.5.7
virtualenvwrapper 4.8.4
root@UYYNOT:~# pyenv shell 3.10.5
root@UYYNOT:~# pyenv version
3.10.5 (set by PYENV_VERSION environment variable)
root@UYYNOT:~# python -V
Python 3.10.5
root@UYYNOT:~# pip list
Package Version
----------------- -------
distlib 0.3.6
filelock 3.9.0
pbr 5.11.1
pip 23.0
platformdirs 2.6.2
setuptools 67.2.0
stevedore 4.1.1
virtualenv 20.18.0
virtualenv-clone 0.5.7
virtualenvwrapper 4.8.4
root@UYYNOT:~# pyenv shell 3.11.1
root@UYYNOT:~# pyenv version
3.11.1 (set by PYENV_VERSION environment variable)
root@UYYNOT:~# python -V
Python 3.11.1
root@UYYNOT:~# pip list
Package Version
----------------- -------
distlib 0.3.6
filelock 3.9.0
pbr 5.11.1
pip 22.3.1
platformdirs 2.6.2
setuptools 65.5.0
stevedore 4.1.1
virtualenv 20.18.0
virtualenv-clone 0.5.7
virtualenvwrapper 4.8.4

以上可以看出我们每个python版本都安装好了virtualenvwrapper

使用virtualenvwrapper

创建虚拟环境

依次切换我们的python版本,创建测试虚拟环境

1
2
3
root@UYYNOT:~# python -V
Python 3.11.1
root@UYYNOT:~# mkvirtualenv 3111_ccc

查看已创建的虚拟环境

使用workon直接回车

1
2
3
4
root@UYYNOT:~# workon
3085_bbb
3105_aaa
3111_ccc

或者lsvirtualenv

1
2
3
4
5
6
7
8
9
10
11
12
root@UYYNOT:~# lsvirtualenv
3085_bbb
========



3105_aaa
========


3111_ccc
========

使用指定虚拟环境

1
2
3
root@UYYNOT:~# workon 3085_bbb
(3085_bbb) root@UYYNOT:~# python -V
Python 3.8.5

进入当前使用的虚拟环境目录

1
2
3
(3105_aaa) root@UYYNOT:~# cdvirtualenv
(3105_aaa) root@UYYNOT:~/.virtualenvs/3105_aaa# pwd
/root/.virtualenvs/3105_aaa

进入当前使用的虚拟环境site-packages目录

1
2
3
(3105_aaa) root@UYYNOT:~# cdsitepackages
(3105_aaa) root@UYYNOT:~/.virtualenvs/3105_aaa/lib/python3.10/site-packages# pwd
/root/.virtualenvs/3105_aaa/lib/python3.10/site-packages

显示 当前虚拟环境site-packages 目录中的内容。

1
2
3
4
5
(3105_aaa) root@UYYNOT:~# lssitepackages
_distutils_hack pip-23.0.virtualenv setuptools-67.1.0.dist-info wheel
distutils-precedence.pth pkg_resources setuptools-67.1.0.virtualenv wheel-0.38.4.dist-info
pip __pycache__ _virtualenv.pth wheel-0.38.4.virtualenv
pip-23.0.dist-info setuptools _virtualenv.py

退出虚拟环境

1
2
(3085_bbb) root@UYYNOT:~# deactivate
root@UYYNOT:~#

删除指定虚拟环境

1
2
3
4
5
root@UYYNOT:~# rmvirtualenv 3085_bbb
Removing 3085_bbb...
root@UYYNOT:~# workon
3105_aaa
3111_ccc

查看虚拟环境目录

1
2
3
4
5
6
root@UYYNOT:~/.virtualenvs# ll
total 64
drwxr-xr-x 4 root root 4096 Feb 8 10:02 ./
drwx------ 11 root root 4096 Feb 8 09:33 ../
drwxr-xr-x 4 root root 4096 Feb 8 09:34 3105_aaa/
drwxr-xr-x 4 root root 4096 Feb 8 09:37 3111_ccc/

刚才删除3085_bbb虚拟环境这里也同步删除了