Python 虚拟环境原理

34 min read

虚拟环境环境的特点

  • Python版本固定。即使系统的Python升级了,虚拟环境中的仍然不受影响,保留开发状态。
  • 所有Python软件包,都只在这个环境生效。一旦退出,则回到用户+系统的默认环境中。

这两个特点,由两个小手段实现。

  • 改变当前Shell的PATH
  • 改变Python运行时的sys.path

以下为python:alpine镜像中,以root用户演示的例子。

改变PATH

首先看一下它的目录结构:

# ls venv
bin      include  lib
# ls /usr/local
bin      include  lib      share

环境内所有的新内容,都在这个新生成目录下。 bin是可执行文件的位置,include是C/C++的头文件位置,lib是库文件位置。 它和/usr/local内的主要目录几乎相同,也和~/.local下类似。

魔法都在两个PATH中。

# echo $PATH
/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
# . venv/bin/activate
(venv) # echo $PATH
/root/venv/bin:/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

显然,这个activate,为当前PATH增加了venv/bin这个位置在最前方,因此虚拟环境中的可执行文件拥有最高优先级。 而libinclude,仅仅是bin下面的可执行文件做相对路径运算来寻找的位置。 所以,改变了PATH,就改变了很多事。

# ls -hl venv/bin/
total 88
-rw-r--r--    1 root     root        2.0K Mar 31 08:06 activate
-rw-r--r--    1 root     root        1.1K Mar 31 08:06 activate.csh
-rw-r--r--    1 root     root        3.0K Mar 31 08:06 activate.fish
-rw-r--r--    1 root     root        1.5K Mar 31 08:06 activate.ps1
-rw-r--r--    1 root     root         986 Mar 31 08:06 activate.xsh
-rw-r--r--    1 root     root        1.5K Mar 31 08:06 activate_this.py
-rwxr-xr-x    1 root     root         238 Mar 31 08:06 easy_install
-rwxr-xr-x    1 root     root         238 Mar 31 08:06 easy_install-3.7
-rwxr-xr-x    1 root     root         220 Mar 31 08:06 pip
-rwxr-xr-x    1 root     root         220 Mar 31 08:06 pip3
-rwxr-xr-x    1 root     root         220 Mar 31 08:06 pip3.7
-rwxr-xr-x    1 root     root       35.8K Mar 31 08:06 python
-rwxr-xr-x    1 root     root        2.3K Mar 31 08:06 python-config
lrwxrwxrwx    1 root     root           6 Mar 31 08:06 python3 -> python
lrwxrwxrwx    1 root     root           6 Mar 31 08:06 python3.7 -> python
-rwxr-xr-x    1 root     root         216 Mar 31 08:06 wheel

由于优先级最高,所以环境里的pythonpip等,包括后来用pip安装的可执行文件,都使用的是venv下的。

改变sys.path

(venv) # python -m site
sys.path = [
    '/root',
    '/root/venv/lib/python37.zip',
    '/root/venv/lib/python3.7',
    '/root/venv/lib/python3.7/lib-dynload',
    '/usr/local/lib/python3.7',
    '/root/venv/lib/python3.7/site-packages',
]
USER_BASE: '/root/.local' (doesn't exist)
USER_SITE: '/root/.local/lib/python3.7/site-packages' (doesn't exist)
ENABLE_USER_SITE: False
(venv) # deactivate
# python -m site
sys.path = [
    '/root',
    '/usr/local/lib/python37.zip',
    '/usr/local/lib/python3.7',
    '/usr/local/lib/python3.7/lib-dynload',
    '/usr/local/lib/python3.7/site-packages',
]
USER_BASE: '/root/.local' (doesn't exist)
USER_SITE: '/root/.local/lib/python3.7/site-packages' (doesn't exist)
ENABLE_USER_SITE: True

可见,sys.path发生了翻天覆地的变化。 除了当前路径/root和标准库/usr/local/lib/python3.7被保留以外,其它位置都换成了venv下的。 这就是为什么pip list看不见什么软件包的原因,也是环境隔离的最大秘密。