9

Python中的虚拟环境(Virtualenv)及其工作原理

 3 years ago
source link: https://note.qidong.name/2019/03/python-virtualenv/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Python中的虚拟环境(Virtualenv)及其工作原理

2019-03-30 22:44:05 +08  字数:2564  标签: Python

简介

也许Python由于2和3的分裂,Python发展出了虚拟环境(virtualenv)的技术。 还有一个原因——可能这才是主要原因——Python的包只能同时安装一个版本。 这种行为是学自系统包管理器。 对系统来说,一个包安装一个版本就够了。 但是对一个开发多个复杂项目的环境来说,只有像Java系的Maven库那样,多个版本同时保留,依赖检查延迟到打包时,才能确保并行开发。 否则,A项目需要S软件的1.0版本,B项目需要它的2.0,这就没法一起玩了。

virtualenv的关键词是隔离(Isolation)。 它能创造一个包含特定版本的Python环境,并且确保Python软件包非常干净。 它创造性地使用了一些Shell和Python原有的机制,实现了虚拟环境的功能。

因此,开发每个Python项目时,都推荐创建对应的virtualenv来隔离开发。 这样可以不受系统Python软件包的影响,安装任意包的任意版本,并且最终能通过pip freeze > requirements.txt获取依赖列表。 (当然,这个列表通常需要裁剪。)

安装virtualenv

使用aptyum等包管理器安装的版本老旧,推荐使用pip安装。

pip3 install --user virtualenv

准备virtualenv

每个项目,都需要独立创建一个(或多个)虚拟环境,隔离开发。

virtualenv -p python3 venv

-p是显式指定Python版本,避免使用默认的python。 虚拟环境的常用名,可选择envvenv.env.venvvenv是PyCharm的默认虚拟环境名称。

激活virtualenv

默认使用的是用户+系统环境,激活后才是虚拟环境。

source venv/bin/activate

激活虚拟环境后,可以看到只有三个Python包。 这个环境可以随意使用,所有安装都会在./venv/下,不会影响系统环境。 干净的环境,也能帮助开发人员确认依赖。

$ pip list
Package    Version
---------- -------
pip        19.0.3
setuptools 40.8.0
wheel      0.33.1

在这个虚拟环境中,python就是python3,而系统环境的python通常是python2。 在安装软件时,直接使用pip,即可安装到虚拟环境中。 而不像一般状态下,要么加sudo提权([brew]或Windows环境下不用),要么安装时需要加--user,安装到用户目录下。

以下是系统、用户、虚拟环境三种方式安装,以及可执行文件pylint被安装的位置。

$ sudo pip install pylint
$ ls /usr/local/bin/pylint
/usr/local/bin/pylint

$ pip install --user pylint
$ ls ~/.local/bin/pylint
/home/user/.local/bin/pylint

$ source venv/bin/activate
$ pip install pylint
$ ls venv/bin/pylint
venv/bin/pylint

退出virtualenv

deactivate

退出后,回到用户+系统环境。

虚拟环境的原理

上面的实操指南,只要照做,就能把虚拟环境用起来。 用多了,自然能够理解它。 virtualenv是如何创建一个隔离的Python虚拟环境?这个环境有什么特点?

这个环境的特点有二:

  • 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看不见什么软件包的原因,也是环境隔离的最大秘密。

标准库venv

从Python 3.3开始,标准库中就自带了一个venv模块,拥有virtualenv的部分功能。 因此,也可以通过以下命令来创建虚拟环境。

python3 -m venv venv

但还是推荐使用virtualenvvenv只能创建当前版本的虚拟环境,不能创建其它Python 3.x的版本,以及Python 2的环境。

结语

虚拟环境技术,早已成为当今Python生态的一个核心内容。 除了广泛而频繁的使用以外,它也成为了其它技术的基石。

  • autoenv是一个让你cd到某个目录下,就自动source进入虚拟环境的小工具,省去了手动source的操作。
  • pipenv基于Pipfile,结合了pipvirtualenv,让你可以有更好的依赖管理。
  • tox是一个标准化、自动化的测试技术。 基于virtualenv,它能同时在不同平台、类型与版本的Python下测试,确保软件包的兼容性。
  • conda是专为数据科学而设计的Anaconda的包管理器。 它不仅是pip的另一个替代,而且更进一步,自建了完整生态。 它充分利用了virtualenv的特点,把C/C++的包也纳入管理,改进了原先pip+apt才能实现的依赖管理。

当然,最近几年,由[Docker]兴起而导致的革命,改变了一切。 在生产环境,虚拟环境最终被容器所取代,仅在开发环境和部分测试环境据守。

参考


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK