Python如何找到sys.prefix(resp.sys.base_prefix)的值



锡上写着什么。

我已经解开了在使用虚拟环境时如何设置sys.prefix的谜团(Python查找pyvenv.cfg文件[1])。

然而,我并不完全理解Python是如何找到sys.base_prefix
默认值为/usr/local(在Unix上)[2]
然而,这些行为似乎对我的Mac没有影响。

如下面的代码所示,后面是符号链接,在这种情况下,sys.base_prefix只是实际Python二进制文件上方的目录:

# Following symlinks to determinw where the actual hard link is
➜  tmp ls -l /usr/local/bin/python3
lrwxr-xr-x  1 jeffhemmen  admin  38 23 Jul 12:23 /usr/local/bin/python3 -> ../Cellar/python@3.8/3.8.5/bin/python3
➜  tmp ls -l /usr/local/Cellar/python@3.8/3.8.5/bin/python3
lrwxr-xr-x  1 jeffhemmen  staff  55 20 Jul 14:26 /usr/local/Cellar/python@3.8/3.8.5/bin/python3 -> ../Frameworks/Python.framework/Versions/3.8/bin/python3
➜  tmp ls -l /usr/local/Cellar/python@3.8/3.8.5/Frameworks/Python.framework/Versions/3.8/bin/python3
lrwxr-xr-x  1 jeffhemmen  staff  9 20 Jul 14:26 /usr/local/Cellar/python@3.8/3.8.5/Frameworks/Python.framework/Versions/3.8/bin/python3 -> python3.8
➜  tmp ls -l /usr/local/Cellar/python@3.8/3.8.5/Frameworks/Python.framework/Versions/3.8/bin/python3.8
-rwxr-xr-x  1 jeffhemmen  staff  17704 23 Jul 12:23 /usr/local/Cellar/python@3.8/3.8.5/Frameworks/Python.framework/Versions/3.8/bin/python3.8
# Printing sys.base_prefix for all these links
➜  tmp /usr/local/bin/python3 -c "import sys; print(sys.base_prefix)" 
/usr/local/Cellar/python@3.8/3.8.5/Frameworks/Python.framework/Versions/3.8
➜  tmp /usr/local/Cellar/python@3.8/3.8.5/bin/python3 -c "import sys; print(sys.base_prefix)"  
/usr/local/Cellar/python@3.8/3.8.5/Frameworks/Python.framework/Versions/3.8
➜  tmp /usr/local/Cellar/python@3.8/3.8.5/Frameworks/Python.framework/Versions/3.8/bin/python3 -c "import sys; print(sys.base_prefix)"
/usr/local/Cellar/python@3.8/3.8.5/Frameworks/Python.framework/Versions/3.8
➜  tmp /usr/local/Cellar/python@3.8/3.8.5/Frameworks/Python.framework/Versions/3.8/bin/python3.8 -c "import sys; print(sys.base_prefix)"
/usr/local/Cellar/python@3.8/3.8.5/Frameworks/Python.framework/Versions/3.8

然而,在下面的例子中(同一台电脑,我的Mac),sys.base_prefix完全不同:

➜  tmp ls -l /usr/bin/python3 
-rwxr-xr-x  1 root  wheel  31488 10 Aug 21:56 /usr/bin/python3
➜  tmp /usr/bin/python3 -c "import sys; print(sys.base_prefix)" 
/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.7

我在某个地方读到,这取决于Python的安装方式。这是否意味着sys.base_prefix实际上被编译成二进制?如果没有,Python在启动时是否会在某个地方读取文本文件(config、.py、…)?

奖励问题:sys.exec_prefix何时/如何/为什么与sys.prefix(和/或来自sys.base_prefix`的sys.base_exec_prefix)不同?

文档解释了如何设置值以及虚拟环境如何覆盖它。

这是sys模块的源代码,它似乎是在初始化时从解释器状态设置的。我没有沿着这条路走下去。

Python安装目录 是包含任何Python安装所需的所有目录bin/include/lib/share/等的目录。如果您使用的是Ubuntu,它的默认Python安装目录为/usr。当我们创建一个虚拟环境时,会创建一个具有环境名称的Python安装目录。任何Python解释器,不管是不是环境,都需要这个目录结构才能正常工作。因此,使用venv(或任何其他工具,如pyenvpoetryconda)创建的Python环境将创建具有这些确切名称的目录。

例如,如果我们创建一个名为my_venv的Python虚拟环境,我们将拥有Python安装目录结构:

~/my_python_project$ python3 -m venv my_venv
~/my_python_project$ tree my_venv/ -L 1
. ## This is the "Python Installation directory" structure. Notice directories bin/, lib/ etc.
├── bin
├── etc
├── include
├── lib
├── lib64 -> lib
├── pyvenv.cfg
└── share

当我们在shell中键入python时,操作系统会遍历PATH环境变量,查找名为"python"的第一个可执行文件。一旦找到了一个(它可能是一个符号链接,在这种情况下不会被跟踪),Python脚本就会使用该解释器开始执行。Python脚本将自动执行的第一件事是询问操作系统它正在使用的解释器的位置,该解释器位于PATH环境变量中。有了这个位置,Python(更准确地说是site.py模块)将在可执行文件(或符号链接)所在的/bin目录的上一级查找一个名为pyenv.py的文件。如果找到,解释器将理解它是在虚拟环境中;如果找不到,解释器将理解它不在虚拟环境中。

在虚拟环境中,如果pyvenv.cfg文件包含home密钥,解释器将使用它将sys.base_prefixsys_prefix设置为:

  1. sys.base_prefix将保存用于创建此虚拟环境的Python安装目录的路径。为此,解释器将查看pyvenv.cfg中的home键。该键将保存用于创建该虚拟环境的Python可执行文件所在的目录的路径。如果该可执行文件恰好是一个符号链接,那么它将一直跟随到真正的可执行文件。实际可执行文件所在的bin/目录将是用于构建此环境的Python可执行文件的Python安装目录。例如:
~/my_python_projects$ cat ./my_venv/pyvenv.cfg
home = /usr/local/bin   ## this is because this Python virtual environment was created from the executable
## /usr/local/bin/python3, which is a symlink to /usr/bin/python3.8. 
## Thus sys.base_prefix will be /usr
include-system-site-packages = false
version = 3.8.10
~/my_python_projects$ ls -l /usr/local/bin/python3
lrwxrwxrwx 1 root root 18 mar  2  2022 /usr/local/bin/python3 -> /usr/bin/python3.8
~/my_python_projects$ source ./my_venv/bin/activate  ## activate the virtual environment
(my_venv) ~/my_python_projects$ python3 -c "import sys;print(sys.base_prefix)"
/usr
  1. sys.prefix将指向包含pyvenv.cfg文件的目录(如果存在),即虚拟环境的Python安装目录(如果使用)。如果不使用虚拟环境,将与sys.base_prefix相同
(my_venv) ~/my_python_projects$ python3 -c 'import sys;print(sys.base_prefix);print(sys.prefix)'
/usr  ## sys.base_prefix
/home/carl/my_python_projexts/env_no_ga  ## sys.prefix

注意,这两个";基本目录";具有Python安装的目录结构,即目录binincludelib等。请注意,如果我们不使用虚拟环境,sys.base_prefixsys.base都将设置为操作系统的Python安装目录。在我的例子中,我使用Ubuntu 20.04,所以当不使用虚拟环境时,这些变量是/usr

同样从Python文档中,我推断其他两个变量sys.prefixsys.base_prefix设置为:

  1. sys.base_exec_prefix:如果在虚拟环境中,它将与sys.base_prefix相同,即用于创建虚拟环境的Python解释器的Python安装目录。如果不是在虚拟环境中,它将是OS Python安装目录。

  2. sys.exec_prefix:如果在虚拟环境中,它将与sys.prefix相同,即虚拟环境的Python安装目录。如果不是在虚拟环境中,它将是OS Python安装目录。

换句话说,默认情况下,它们将始终分别与sys.base_prefixsys.prefix相同。

阅读了引用的关于sys.base_prefixsys.prefixsys.base_exec_prefixsys.exec_prefix的Python文档,我得出结论,所有这些变量最初都设置在操作系统的Python安装目录中。之后,如果模块site.py确定我们处于虚拟环境中,则如上所述继续改变它们。

我还没有发现的是,用不同的名字定义所有这些变量,但它们的值相同,这背后的理由是什么。

参考书目

  1. Python和模块搜索路径(Mark Jamison著)
  2. 此处为Python文档

最新更新