一种语言要使用到外部库(模块) 时必然会涉及到从哪里以及按何顺序加载依赖,就像 LD_LIBRARY_PATH, CLASSPATH 那样,Python 也有其默认的模块搜索顺序, 依序找到想要的模块即停止。Python 中 sys.path
返回的列表包含了模块搜索的顺序,我们可以程序中修改该列表,或用 PYTHONPATH
环境变量前插路径,甚至是用 .pth
文件来附加路径。
简单的,可以执行命令 python3 -c "import sys; print(str(sys.path).replace(',', '\n'))"
来查看 python3 交互 shell 下的模块搜索路径,类似结果如下:
[''
'/usr/lib/python36.zip'
'/usr/lib/python3.6'
'/usr/lib/python3.6/lib-dynload'
'/home/yanbin/.local/lib/python3.6/site-packages'
'/usr/local/lib/python3.6/dist-packages'
'/usr/lib/python3/dist-packages'
'/usr/lib/python3.6/dist-packages']
注意,第一个元素是个空字符串,代表进入 python3 shell 时的当前目录。
如果在通过一个 py 脚本文件来打印 sys.path
的话显示稍微有所差异。比如在目录 /home/yanbin/Developers/
下创建 test.py
文件,内容为
1 2 3 4 |
import sys for line in sys.path: print(f"'{line}'") |
而我们退到 /home/yanbin
为当前目录来执行 python3 Developers/test.py
, 显示出来的搜索路径如下:
'/home/yanbin/Developers'
'/usr/lib/python36.zip'
'/usr/lib/python3.6'
'/usr/lib/python3.6/lib-dynload'
'/home/yanbin/.local/lib/python3.6/site-packages'
'/usr/local/lib/python3.6/dist-packages'
'/usr/lib/python3/dist-packages'
'/usr/lib/python3.6/dist-packages'
第一行的路径有所不同了,变成了一个绝对路径,也就是说 test.py
可以从它所在目录上加载模块。比如在 /home/yanbin/Developers
下有 mymath.py
, 在 test.py
中能够直接 import mymath
成功。但是把 mymath.py
放到当前目录 /home/yanbin
下就加载不到了。除非用 PYTHONPATH
指定当前目录 .
, 后面会讲到。
再次重复一下 Python shell 与执行脚本文件时,模块搜索路径第一个路径显示方式不一样,Python shell 下用空字符串表示进行 shell 时的当前目录,执行脚本时第一个路径是脚本文件所在的目录。Shell 启动时目录或脚本所以在目录总是最高优先级的模块搜索路径。
接下来介绍如何动态修改 sys.path
以及用环境变量 PYTHONPATH
如何影响 sys.path
来增加新的搜索路径。至于用 .pth
文件附加路径的方式后面会另立新篇。
动态修改 sys.path
sys.path
是一个可变的列表,所以运行时可以随便修改它的内容,比如说想要加载 /home/yanbin/test
下的 mymath 模块,但是它没出现在 sys.path
列表中,我们可以这样做
1 2 3 4 5 |
import sys sys.path.append('/home/yanbin/test') import mymath print(mymath.pi) |
这只是一条路子,程序中不应该经常性的这么做,因为 sys.path
是一个全局变量,如果运行时想改就改,最终不知道模块是从哪儿加载过来的。相比,下面的 PYTHONPATH
比较实用些。
理解 Python 的模块路径有助于理解 Python 的依赖管理工具的实现原理,像 easy_install
, pip
, pipenv
, virtualenv
等,它们通过各种途径最终修改最终影响到 sys.path
列表。它们在还会使用 .pth
文件来进一步增强 sys.path
列表。
PYTHONPATH
环境变量向前添加模块搜索路径
如果用环境变量 PYTHONPATH
设置了模块搜索路径,它的内容将被向前添加到 sys.path
列表中去,准确来讲是插入到 sys.path
的第一个元素后面。PYTHONPATH
中可以使用绝对路径和相对路径,相对路径是相对于 Shell 启动时的目录或脚本文件所在的目录。
举例来看
python shell
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
$ export PYTHONPATH=/home/yanbin/test:extra_modules $ cd / $ python3 >>> import sys >>> for line in sys.path: ... print(f"'{line}'") ... '' '/home/yanbin/test' '/extra_modules' '/usr/lib/python36.zip' '/usr/lib/python3.6' '/usr/lib/python3.6/lib-dynload' '/home/yanbin/.local/lib/python3.6/site-packages' '/usr/local/lib/python3.6/dist-packages' '/usr/lib/python3/dist-packages' '/usr/lib/python3.6/dist-packages' |
看到相对路径 extra_modules
相对于进入 shell 前的 /
目录,效果上相当于
1 |
sys.path[1:1] = ['/home/yanbin/test', '/extra_modules'] |
脚本文件的方式
这一次我们在 PYTHONPATH
中把当前路径给加上
1 2 3 |
$ export PYTHONPATH=.:/home/yanbin/test:extra_modules $ cd /home/yanbin $ python3 Developers/test.py |
输出如下
1 2 3 4 5 6 7 8 9 10 11 |
'/home/yanbin/Developers' '/home/yanbin' '/home/yanbin/test' '/home/yanbin/extra_modules' '/usr/lib/python36.zip' '/usr/lib/python3.6' '/usr/lib/python3.6/lib-dynload' '/home/yanbin/.local/lib/python3.6/site-packages' '/usr/local/lib/python3.6/dist-packages' '/usr/lib/python3/dist-packages' '/usr/lib/python3.6/dist-packages' |
第一行为脚本文件所在的目录,/home/yanbin
为 PYTHONPATH
中的 .
。这样我们既能够从脚本所在的目录,也可以从当前工作目录中加载模块了,惊喜就是 cd 切换一下目录后代码就可能不工作了。
小结:
- sys.path 能列出模块搜索顺序,Python REPL 中第一个路径为空字符串,代表 Shell 启动时的目录,执行脚本文件时第一个路径是脚本文件所在的目录
PYTHONPATH
环境变量可以使用绝对和相对路径。相对路径相对于 Shell 启动时目录或程序脚本文件所在目录PYTHONPATH
中的所有路径会以sys.path[1:1] = <paths in PYTHONPATH>
的方式添加到sys.path
搜索路径列表中- 动态的改动
sys.path
自己应该尽量少用吧,依赖管理工具除外 - 可由此理解依赖管理工具,如
easy_install
,pip
,pipenv
, 和virtualenv
是如何工作的,最终的搜索路径是由sys.path
决定的。
本文链接 https://yanbin.blog/python-module-search-path/, 来自 隔叶黄莺 Yanbin Blog
[版权声明] 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。
[…] 上一篇 Python 的模块搜索路径,介绍了 Python 的模块搜索路径,最终起作用的是 sys.path 路径列表。如果要自定义自己的搜索路径,就是要怎么定制 sys.path 的内容。可以简单的用 PYTHONPATH 环境变量前向添加,这儿将要说的是用 .pth 文件的方式。也可由此进一步理解 Python 依赖管理工具,像 virtualenv 等的工作原理。 […]
十分有用
[…] 我们可以做个测试,在 Lambda 中查看一下 Python 的模块搜索路径,用如下代码 […]
[…] 安装的模块可以是全局的或用户独立的,回忆一下 Python 的模块搜索路径,安装后模块总是要能被 Python 程序搜索到才有用,Python […]
[…] 上一篇 Python 的模块搜索路径,介绍了 Python 的模块搜索路径,最终起作用的是 sys.path 路径列表。如果要自定义自己的搜索路径,就是要怎么定制 sys.path 的内容。可以简单的用 PYTHONPATH 环境变量前向添加,这儿将要说的是用 .pth 文件的方式。也可由此进一步理解 Python 依赖管理工具,像 virtualenv 等的工作原理。 […]