虚拟环境库如何工作

您是否考虑过虚拟环境库如何在Python中工作? 在本文中,我建议熟悉所有环境库都使用的主要概念,例如virtualenv,virtualenvwrapper,conda,pipenv。

最初,在Python中没有内置的创建环境的功能,并且此功能是作为hack实现的。 事实证明,所有库都基于python解释器的一个非常简单的功能。

当Python启动解释器时,它将开始查找包含模块(站点程序包)的目录。 搜索从有关解释程序可执行文件(python.exe)物理位置的父目录开始。 如果未找到模块文件夹,则Python会上升一个级别,直到到达根目录为止。 为了理解这是一个包含模块的目录,Python查找os模块,该模块必须在os.py文件中,并且是python工作所需的模块。

假设我们的解释器位于/usr/dev/lang/bin/python 。 然后,搜索路径将如下所示:

 /usr/dev/lang/lib/python3.7/os.py /usr/dev/lib/python3.7/os.py /usr/lib/python3.7/os.py /lib/python3.7/os.py 

如您所见,Python向我们的路径添加了一个特殊的前缀( lib/python$VERSION/os.py )。 解释器找到第一个匹配项(存在os.py文件)后, sys.prefixsys.exec_prefix更改为此路径(已删除前缀)。 如果由于某种原因未找到匹配项,则使用标准路径,该路径将编译到解释器中。

现在,让我们看看最古老,最著名的库virtualenv是如何做到的。

 user@arb:/usr/home/test# virtualenv ENV Running virtualenv with interpreter /usr/bin/python3 New python executable in /usr/home/test/ENV/bin/python3 Also creating executable in /usr/home/test/ENV/bin/python Installing setuptools, pkg_resources, pip, wheel...done. 

执行后,它将创建其他目录:

 user@arb:/usr/home/test/ENV# tree -L 3 . ├── bin │ ├── activate │ ├── activate.csh │ ├── activate.fish │ ├── activate_this.py │ ├── easy_install │ ├── easy_install-3.7 │ ├── pip │ ├── pip3 │ ├── pip3.7 │ ├── python │ ├── python-config │ ├── python3 -> python │ ├── python3.7 -> python │ └── wheel ├── include │ └── python3.7m -> /usr/include/python3.7m ├── lib │ └── python3.7 │ ├── __future__.py -> /usr/lib/python3.7/__future__.py │ ├── __pycache__ │ ├── _bootlocale.py -> /usr/lib/python3.7/_bootlocale.py │ ├── _collections_abc.py -> /usr/lib/python3.7/_collections_abc.py │ ├── _dummy_thread.py -> /usr/lib/python3.7/_dummy_thread.py │ ├── _weakrefset.py -> /usr/lib/python3.7/_weakrefset.py │ ├── abc.py -> /usr/lib/python3.7/abc.py │ ├── base64.py -> /usr/lib/python3.7/base64.py │ ├── bisect.py -> /usr/lib/python3.7/bisect.py │ ├── codecs.py -> /usr/lib/python3.7/codecs.py │ ├── collections -> /usr/lib/python3.7/collections │ ├── config-3.7m-darwin -> /usr/lib/python3.7/config-3.7m-darwin │ ├── copy.py -> /usr/lib/python3.7/copy.py │ ├── copyreg.py -> /usr/lib/python3.7/copyreg.py │ ├── distutils │ ├── encodings -> /usr/lib/python3.7/encodings │ ├── enum.py -> /usr/lib/python3.7/enum.py │ ├── fnmatch.py -> /usr/lib/python3.7/fnmatch.py │ ├── functools.py -> /usr/lib/python3.7/functools.py │ ├── genericpath.py -> /usr/lib/python3.7/genericpath.py │ ├── hashlib.py -> /usr/lib/python3.7/hashlib.py │ ├── heapq.py -> /usr/lib/python3.7/heapq.py │ ├── hmac.py -> /usr/lib/python3.7/hmac.py │ ├── imp.py -> /usr/lib/python3.7/imp.py │ ├── importlib -> /usr/lib/python3.7/importlib │ ├── io.py -> /usr/lib/python3.7/io.py │ ├── keyword.py -> /usr/lib/python3.7/keyword.py │ ├── lib-dynload -> /usr/lib/python3.7/lib-dynload │ ├── linecache.py -> /usr/lib/python3.7/linecache.py │ ├── locale.py -> /usr/lib/python3.7/locale.py │ ├── no-global-site-packages.txt │ ├── ntpath.py -> /usr/lib/python3.7/ntpath.py │ ├── operator.py -> /usr/lib/python3.7/operator.py │ ├── orig-prefix.txt │ ├── os.py -> /usr/lib/python3.7/os.py │ ├── posixpath.py -> /usr/lib/python3.7/posixpath.py │ ├── random.py -> /usr/lib/python3.7/random.py │ ├── re.py -> /usr/lib/python3.7/re.py │ ├── readline.so -> /usr/lib/python3.7/lib-dynload/readline.cpython-37m-darwin.so │ ├── reprlib.py -> /usr/lib/python3.7/reprlib.py │ ├── rlcompleter.py -> /usr/lib/python3.7/rlcompleter.py │ ├── shutil.py -> /usr/lib/python3.7/shutil.py │ ├── site-packages │ ├── site.py │ ├── sre_compile.py -> /usr/lib/python3.7/sre_compile.py │ ├── sre_constants.py -> /usr/lib/python3.7/sre_constants.py │ ├── sre_parse.py -> /usr/lib/python3.7/sre_parse.py │ ├── stat.py -> /usr/lib/python3.7/stat.py │ ├── struct.py -> /usr/lib/python3.7/struct.py │ ├── tarfile.py -> /usr/lib/python3.7/tarfile.py │ ├── tempfile.py -> /usr/lib/python3.7/tempfile.py │ ├── token.py -> /usr/lib/python3.7/token.py │ ├── tokenize.py -> /usr/lib/python3.7/tokenize.py │ ├── types.py -> /usr/lib/python3.7/types.py │ ├── warnings.py -> /usr/lib/python3.7/warnings.py │ └── weakref.py -> /usr/lib/python3.7/weakref.py └── pip-selfcheck.json 

如您所见,虚拟环境是通过将Python二进制文件复制到本地文件夹(ENV / bin / python)创建的。 我们还可以注意到,父文件夹包含指向python标准库文件的符号链接 。 我们无法创建指向可执行文件的符号链接,因为 解释器仍会将其重命名为实际路径。

现在让我们激活环境:

 user@arb:/usr/home/test# source ENV/bin/activate 

此命令更改$ PATH环境变量,以便python命令指向我们的本地python版本。 这是通过在$ PATH行的开头替换bin文件夹的本地路径来实现的,从而使本地路径优先于右侧的所有路径。

 export "/usr/home/test/ENV/bin:$PATH" echo $PATH 

如果在此环境中运行脚本,则将使用/usr/home/test/ENV/bin/python的二进制文件执行脚本。 解释器将使用此路径作为查找模块的起点。 在我们的例子中,标准库的模块位于/usr/home/test/ENV/lib/python3.7/路径中。

这是主要的技巧,所有用于虚拟环境的库都可以使用。

Python 3的改进


从Python 3.3版本开始,出现了一个新标准,称为PEP 405 ,它引入了用于轻量级环境的新机制。

该PEP在搜索过程中增加了一个额外的步骤。 如果创建pyenv.cfg配置pyenv.cfg ,则无需复制Python二进制文件及其所有模块,而只需在此配置中指示它们的位置即可。

标准venv模块(在Python 3中出现)积极使用了此功能。

 user@arb:/usr/home/test2# python3 -m venv ENV user@arb:/usr/home/test2# tree -L 3 . └── ENV ├── bin │ ├── activate │ ├── activate.csh │ ├── activate.fish │ ├── easy_install │ ├── easy_install-3.7 │ ├── pip │ ├── pip3 │ ├── pip3.5 │ ├── python -> python3 │ └── python3 -> /usr/bin/python3 ├── include ├── lib │ └── python3.7 ├── lib64 -> lib ├── pyvenv.cfg └── share └── python-wheels 

 user@arb:/usr/home/test2# cat ENV/pyvenv.cfg home = /usr/bin include-system-site-packages = false version = 3.7.0 user@arb:/usr/home/test2# readlink ENV/bin/python3 /usr/bin/python3 

归功于此配置,venv无需复制二进制文件,只需创建指向它的链接即可。 如果将参数include-system-site-packages更改为true ,则可以从虚拟环境中自动访问标准库的所有模块。

尽管有这些更改,但大多数用于虚拟环境的第三方库仍使用旧方法。

PS:我是本文的作者,您可以提出任何问题。

Source: https://habr.com/ru/post/zh-CN418579/


All Articles