和其他大多数现代编程语言一样,Python对包和模块的下载、存储以及管理有其自己的一套方法。Python的包一般存在几个地方。比如,大部分系统包会存在sys.prefix指定的路径下。在Mac OS X下这个路径为:
>>> import sys>>> sys.prefix'/System/Library/Frameworks/Python.framework/Versions/3.5'
通常情况下我们更关心第三方包的安装位置,比如easy_install或pip会将包存放在 site.getsitepackages所指定的路径下:
>>> import site>>> site.getsitepackages()['/System/Library/Frameworks/Python.framework/Versions/3.5/Extras/lib/python','/Library/Python/3.5/site-packages']
这里就带来了一个问题,当我们同时开发多个工程时,不同的工程会将第三方的包存放在相同的路径下。这就意味着,如果有两个工程依赖同一个包,但是所需要的版本却不一样,比如工程A依赖v1.0.0,而工程B依赖v2.0.0。由于Python无法根据版本来区分包的安装路径,所以这里就会发生版本冲突。这也就是本文所要介绍的虚拟环境(virtualenv/venv)所要解决的问题。
什么是虚拟环境?
Python虚拟环境的主要目的是为了给不同的工程创建互相独立的运行环境。在虚拟环境下,每一个工程都有自己的依赖包,而与其它的工程无关。不同的虚拟环境中同一个包可以有不同的版本。并且,虚拟环境的数量没有限制,我们可以轻松地用virtualenv或者pyenv等工具来创建多个虚拟环境。
虚拟环境的使用
安装虚拟环境工具的方法非常简单,
$ pip install virtualenv
然后,我们创建一个名字叫“env”的虚拟环境:
$ virtualenv env
这条命令会自动创建一个叫“env”的目录,并在其中生成如下的目录结构:
├── bin│ ├── activate│ ├── activate.csh│ ├── activate.fish│ ├── easy_install│ ├── easy_install-3.5│ ├── pip│ ├── pip3│ ├── pip3.5│ ├── python -> python3.5│ ├── python3 -> python3.5│ └── python3.5 -> /Library/Frameworks/Python.framework/Versions/3.5/bin/python3.5├── include├── lib│ └── python3.5│ └── site-packages└── pyvenv.cfg
这些目录包括:
bin: 用于管理虚拟环境的文件include: 编译Python包时所需要的C头文件lib: Python自带及第三方的库这其中还包含有一些Python的工具和可执行文件等副本。这些文件用来保证Python代码可以独立于系统环境而运行。
bin目录下有一个重要的脚本文件activate,这个脚本就是用来将其所在的虚拟环境设置为当前Python的运行环境:
$ source env/bin/activate(env) $
可以看到,在运行完这行命令后,shell的提示符前会出现虚拟环境的名字,表示我们已经进入了这个环境中。
为了验证这一点,我们以bcrypt模块为例。首先我们要在全局系统环境中安装这个模块。在测试之前,我们需要先通过deactivate命令退出当前的虚拟环境:
(env) $ deactivate$
现在我们的shell提示符回归到了正常状态,同时Python的环境也切换到了全局的系统环境。
现在我们来安装bcrypt并用它来生成一个密码的hash值:
$ pip -q install bcrypt$ python -c "import bcrypt; print(bcrypt.hashpw('password'.encode('utf-8'), bcrypt.gensalt()))"$2b$12$vWa/VSvxxyQ9d.WGgVTdrell515Ctux36LCga8nM5QTW0.4w8TXXi
然后我们切换到虚拟环境并试图运行同样的命令:
$ source env/bin/activate(env) $ python -c "import bcrypt; print(bcrypt.hashpw('password'.encode('utf-8'), bcrypt.gensalt()))"Traceback (most recent call last):File "", line 1, in ImportError: No module named 'bcrypt'
可以看到,在虚拟环境中,bcrypt并没有被安装,说明虚拟环境的隔离作用是生效的。
虚拟环境是如何工作的?
了解虚拟环境背后的工作原理,对于一个Python开发者来说非常重要,它能够帮助我们理解Python的运行环境,对依赖的解析等技术。为了解释虚拟环境的原理,我们先来看一下在不同的环境中,Python可执行文件的位置有什么不同。在默认的系统环境中查看python的路径:
$ which python/usr/bin/python
然后我们进入虚拟环境
$ source env/bin/activate(env) $ which python/Users/michaelherman/python-virtual-environments/env/bin/python
可以看到,在激活虚拟环境后,python可执行文件的路径变成了当前环境目录下的路径,并且$PATH也发生了变化:
$ echo $PATH/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:$ source env/bin/activate(env) $ echo $PATH/Users/michaelherman/python-virtual-environments/env/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:
虚拟环境下的bin目录排在了$PATH的最前面。这意味着,用户在命令行中执行python的时候,将率先使用虚拟环境下的可执行文件,而不是系统默认的。
那么虚拟环境下的python可执行文件与系统默认的有什么不同呢?Python又是如何找到虚拟环境下的第三方库的呢?
事实上,上面提到的python可执行文件之间并没有什么不同,但是它们所在的位置至关重要。在Python启动的时候,它会获取自身所在的路径。然后这一路径(bin的上一级)被设置到sys.prefix和sys.exec_prefix之中。在搜索第三方的site-packages时,搜索目录是sys.prefix所指向的路径下的lib/pythonX.X/site-packages/,其中X.X是Python的版本。
在前面的例子中,python文件所在路径为/Users/michaelherman/python-virtual-environments/env/bin,因此sys.prefix会被设为/Users/michaelherman/python-virtual-environments/env,从而site-packages的路径就变成了/Users/michaelherman/python-virtual-environments/env/lib/pythonX.X/site-packages。最后,这一路径被存储在sys.path数组中,其中包含着所有包的引用来源。
使用virtualenvwrapper管理虚拟环境
虚拟环境的引入解决了我们关于环境冲突的问题,但是它同时也带来了一个问题,就是虚拟环境过多所带来的管理问题。virtualenvwrapper就是专门用来解决虚拟环境管理问题的一个工具。我们可以很方便地用它来实现对虚拟环境的创建,删除,拷贝,并且可以轻松地在不同环境间进行切换。
virtualenvwrapper的安装非常简单:
$ pip install virtualenvwrapper
安装后,我们需要将virtualenvwrapper.sh脚本添加到shell启动文件中:
$ which virtualenvwrapper.sh/usr/local/bin/virtualenvwrapper.sh在shell启动文件~/.bashrc中添加:export WORKON_HOME=$HOME/.virtualenvs # Optionalexport PROJECT_HOME=$HOME/projects # Optionalsource /usr/local/bin/virtualenvwrapper.sh
并运行
$ source ~/.bashrc
然后就会在$WORKON_HOME所在的路径下创建集中存放虚拟环境的目录。
接着,我们就可以用下面的一系列命令虚拟环境进行管理:
workondeactivatemkvirtualenvcdvirtualenvrmvirtualenv
比如,我们要创建一个新的工程:
$ mkvirtualenv my-new-project(my-new-project) $
此时会在$WORKON_HOME下创建一个新的虚拟环境。
退出一个虚拟环境的方法和之前一样:
(my-new-project) $ deactivate$
如果你有多个虚拟环境,可以对它们进行查看:
$ workonmy-new-projectmy-django-projectweb-scraper
并选择其中一个进行激活:
$ workon web-scraper(web-scraper) $
这样,你就不再需要记住每一个环境存放的具体位置,可以轻松的对他们进行删除、拷贝、切换,是不是非常方便?
好了,关于虚拟环境的介绍就到这里,获取更多精彩内容敬请关注。